So I’m creating task-specific checklists that can be unique for each task. I managed to create the popup and inside the popup I can add checklists that can be checked but the tasks don’t retain that checklist, and when I manage to make them retain it it retains it for all tasks not for the specific task I have clicked here is my code, I hope someone can help me figure out what is the problem here:
import React, { useState, useEffect } from 'react';
import {
Paper,
Typography,
IconButton,
TextField,
Menu,
MenuItem,
Card,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControlLabel,
List,
ListItem,
ListItemText,
Grid,
} from '@mui/material';
import useMediaQuery from '@mui/material/useMediaQuery'
import { useTheme } from '@mui/material/styles';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import Leaderboard from './Leaderboard';
import TodoPopup from './TodoPopup';
import "./TodoList.css";
import CloseIcon from "@mui/icons-material/Close";
import CheckBoxIcon from "@mui/icons-material/CheckBox";
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
function TodoList() {
const defaultTasks = [
{ name: "Learn Angular", category: "inProgress", color: "white", checklist: []},
{ name: "Learn React", category: "inProgress", color: "white", checklist: []},
{ name: "Learn Vue", category: "complete", color: "white", checklist: []},
{ name: "Learn Bootstrap", category: "todo", color: "white", checklist: []}
];
const [tasks, setTasks] = useState(() => {
const savedTasks = JSON.parse(localStorage.getItem('tasks'));
return savedTasks || defaultTasks;
});
const [anchorEl, setAnchorEl] = useState(null);
const [newTaskName, setNewTaskName] = useState("");
const [selectedCategory, setSelectedCategory] = useState("all");
const [openColorMenu, setOpenColorMenu] = useState(false);
const [selectedTaskColor, setSelectedTaskColor] = useState([]);
const [tempSelectedTask, setTempSelectedTask] = useState(selectedTask);
const [selectedColor, setSelectedColor] = useState("white");
const [name, setName] = useState("");
const [open, setOpen] = useState(false);
const [leaderboardData, setLeaderboardData] = useState([]);
const [showLeaderboard, setShowLeaderboard] = useState(false);
//TodoPopup
const [checklistItem, setChecklistItem] = useState('');
const [checklist, setChecklist] = useState([]);
const [checkedItems, setCheckedItems] = useState([]);
const [selectedTask, setSelectedTask] = useState(null);
const handleColorSelect = (color,) => {
setSelectedColor(color);
const updatedTasks = tasks.map((t) => {
if (t === selectedTask) {
return {...t, color: color};
}
return t
})
setTasks(updatedTasks);
};
const handleMenuOpen = (event, t) => {
setAnchorEl(event.currentTarget);
setSelectedTask(t);
setTempSelectedTask(t);
setSelectedTaskColor(t.color);
}
const handleMenuClose = () => {
setAnchorEl(null);
setSelectedTask(null);
}
const handleMoveTask = (newCategory) => {
const updatedTasks = tasks.map(task => {
if (task.name === selectedTask.name) {
return {...task, category: newCategory, color: selectedColor};
}
return task;
})
setTasks(updatedTasks);
handleMenuClose();
}
const onDragStart = (ev, id, col) => {
ev.dataTransfer.setData("id", id);
}
const onDragOver = ev => {
ev.preventDefault();
}
const onDrop = (ev, cat) => {
let id = ev.dataTransfer.getData("id");
let updateTasks = tasks.map(task => {
if (task.name === id) {
return {...task, category: cat, color: task.color};
}
return task;
})
setTasks(updateTasks);
}
useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
useEffect(() => {
const savedTasks = JSON.parse(localStorage.getItem('tasks'));
if(savedTasks) {
setTasks(savedTasks);
}
}, []);
const deleteTask = () => {
setTasks(tasks.filter((task) => task.name !== selectedTask.name));
handleMenuClose();
};
const handleAddTodo = (event) => {
event.preventDefault();
const newTodo = {
id: `task-${tasks.length + 1}`,
name: newTaskName,
category: "todo"
}
setTasks([...tasks, newTodo]);
setNewTaskName("");
if(selectedCategory === "inProgress") {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === newTodo.id?{...task, category: "inProgress"} : task
)
)
} else if (selectedTask === "complete") {
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === newTodo.id?{...task, category: "complete"} : task
)
)
}
}
useEffect(() => {
const data = JSON.parse(localStorage.getItem("leaderboardData"));
if(data) {
setLeaderboardData(data)
}
}, []);
const handleAddToLeaderboard = (name) => {
const newScore = tasks1.complete.length;
const newLeaderboardData = JSON.parse(localStorage.getItem("leaderboardData")) || [];
const limitedName = name.substring(0, 20).trim() || "Anonymous";
newLeaderboardData.push({ name: limitedName, score: newScore });
localStorage.setItem("leaderboardData", JSON.stringify(newLeaderboardData));
setLeaderboardData(newLeaderboardData);
};
// This is where I get the tasks on click
const handleTaskClick = (task) => {
setSelectedTask(task.key);
setChecklist(task.checklist || []);
}
const handlePopupClose = () => {
setSelectedTask(null);
}
//Popup
const handleAddChecklistItem = () => {
if (checklistItem.trim() !== '') {
const newItem = { id: checklist.length, value: checklistItem, checked: false };
setChecklist((prevChecklist) => [...prevChecklist, newItem]);
setChecklistItem('');
setCheckedItems((prevCheckedItems) => [...prevCheckedItems, false]);
}
}
const handleChecklistItemClick = (itemId) => {
setCheckedItems((prevCheckedItems) =>
prevCheckedItems.map((checked, index) => (index === itemId ? !checked : checked))
);
};
//
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const tasks1 = {
todo: [],
inProgress: [],
complete: []
};
const countTasks = () => {
return (
<div className="count-tasks-container" onClick={() => setOpen(true)}>
Completed Tasks
<Typography variant="div" className="countComplete__tasks">
{tasks1.complete.length}
</Typography>
<Typography className="count-tasks-tooltip">
Join the Leaderboard
</Typography>
</div>
)
}
leaderboardData.sort((a, b) => b.score - a.score);
tasks.forEach((t) => {
tasks1[t.category].push(
<div onDragStart={e => onDragStart(e, t.name,)} draggable key={t.name} style={{ marginBottom: "10px"}} >
<Card key={t.name} style={{ display: "flex", justifyContent: "space-between", backgroundColor: t.color}} >
<Typography sx={{ padding: "5px", color: selectedColor !== 'white' ? 'black' : 'inherit', fontWeight: 600 }} >{t.name}</Typography>
<IconButton aria-controls="simple-menu" aria-haspopup="true"
onClick={(e) => handleMenuOpen(e, t)}
>
<MoreHorizIcon />
</IconButton>
<Menu id="simple-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl && selectedTask === t)}
onClose={handleMenuClose}
>
<MenuItem onClick={() => handleMoveTask("todo")}>Move to Todo</MenuItem>
<MenuItem onClick={() => handleMoveTask("inProgress")}>Move in Progress</MenuItem>
<MenuItem onClick={() => handleMoveTask("complete")}>Move to Complete</MenuItem>
<MenuItem sx={{ display: "flex", justifyContent: "space-between"}} onClick={() => setOpenColorMenu(true)}>
<Typography>
Color
</Typography>
<ExpandMoreIcon />
</MenuItem>
{openColorMenu && (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
{[ "red", "blue", "green", "yellow", "purple", ].map((color) => (
<Typography
variant="div"
key={color}
style={{
backgroundColor: color,
width: 20,
height: 20,
borderRadius: "50%",
margin: "0 10px",
cursor: "pointer",
border: selectedColor === color ? "2px solid white" : "none",
}}
onClick={() => handleColorSelect(color)}
/>
))}
</div>
)}
<MenuItem sx={{ color: "red"}} onClick={deleteTask}>Delete</MenuItem>
</Menu>
</Card>
</div>
)
})
return (
<div style={{ display: "flex", justifyContent: 'center', height: '100%', marginTop: isMobile ? '4vh' : '2vh', flexWrap: isMobile ? 'wrap' : 'no-wrap' }}>
{isMobile && (
<IconButton onClick={() => setShowLeaderboard(!showLeaderboard)} sx={{position: "absolute", top: "4vh", right: "2vh", backgroundColor: "white" }}>
<EmojiEventsIcon sx={{ fontSize: "3rem",}} />
</IconButton>
)}
{showLeaderboard && isMobile && (
<Leaderboard leaderboardData={leaderboardData} showLeaderboard={showLeaderboard} setShowLeaderboard={setShowLeaderboard} closeLeaderboard={() => setShowLeaderboard(false)} />
)}
<Paper
sx={{ width: isMobile ? '100%' : '400px', height: '600px', mr: isMobile ? '0' : '3vh', mb: isMobile ? '3vh' : '0', display: 'flex', flexDirection: 'column' }}
onDragOver={e => onDragOver(e)}
onDrop={e => onDrop(e, "todo")}
>
<div className="todo-header">
<Typography variant="h4" sx={{ textAlign: 'center', fontWeight: '500'}} >
Todo
<Typography variant="div" className="todoCount_tasks">
{tasks1.todo.length}
</Typography>
</Typography>
</div>
<Typography variant="div" sx={{ fontSize: '20px', overflowY: 'auto', height: '600px'}} >
{tasks1.todo.map((task, index) => (
<div key={index} onClick={() => handleTaskClick(task)} >
{task}
</div>
))}
</Typography>
<form onSubmit={handleAddTodo} style={{ position: "sticky", bottom: 0, display: "flex", flexDirection: "column", paddingLeft: "5px", paddingRight: "5px" }} >
<TextField sx={{ width: '100%', marginBottom: '1vh', height: '100%', background: "white"}} label="New Todo" value={newTaskName} onChange={(e) => setNewTaskName(e.target.value)} />
</form>
</Paper>
<Paper sx={{ width: isMobile ? '100%' : '400px', height: '600px', mr: isMobile ? '0' : '3vh', mb: isMobile ? '3vh' : '0', display: 'flex', flexDirection: 'column'}}
onDragOver={e => onDragOver(e)} onDrop={e => onDrop(e, "inProgress")} >
<div className="todo-header">
<Typography variant="h4" sx={{ textAlign: 'center', fontWeight: '500'}} >
In Progress
<Typography variant="div" className="todoCount_tasks">
{tasks1.inProgress.length}
</Typography>
</Typography>
</div>
<Typography variant="div" sx={{ fontSize: '20px', overflowY: 'auto', height: '600px', }}>
{tasks1.inProgress}
</Typography>
</Paper>
<Paper sx={{ width: isMobile ? '100%' : '400px', height: '600px', mr: isMobile ? '0' : '3vh', mb: isMobile ? '3vh' : '0', display: 'flex', flexDirection: 'column' }} onDragOver={e => onDragOver(e)} onDrop={e => onDrop(e, "complete")}>
<Typography variant="h4" sx={{ textAlign: 'center', fontWeight: '500', }} >
{countTasks()}
</Typography>
<Typography variant="div" sx={{ fontSize: '20px', }} >{tasks1.complete}</Typography>
</Paper>
{!isMobile && (
<Paper sx={{ width: '300px', height: '400px', position: 'relative', justifyContent: "flex-end"}}>
<div style={{ display: "flex", alignItems: 'center', position: "absolute", top: '10px', left: '10px',}}>
<EmojiEventsIcon />
<Typography variant="h5" sx={{ display: "inline", textAlign: 'center', ml: "25px"}}>
Leaderboard
</Typography>
</div>
<div style={{ alignItems: 'center', textAlign: 'center', marginTop: '50px'}}>
{leaderboardData.map((data, index) => (
<div key={index} style={{ display: "flex", justifyContent: "space-between" }} >
<Typography variant="h6" sx={{ display: "flex", width: '100%'}} >
{ index < 10 ? `${index + 1}. ${data.name}` : ''}
</Typography>
{ index < 10 && (
<Typography variant="subtitle1">{data.score}</Typography>
)}
</div>
))}
</div>
</Paper>
)}
<Dialog open={!!selectedTask} onClose={handlePopupClose} maxWidth="sm" fullWidth >
<DialogTitle style={{ display: 'flex', alignItems: 'center' }}>
<Typography style={{ fontSize: '30px', flex: '1'}} >{selectedTask}</Typography>
<IconButton onClick={() => setSelectedTask(null)}>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<TextField
value={checklistItem}
onChange={(e) => setChecklistItem(e.target.value)}
label="Add checklist item"
fullWidth
variant="outlined"
margin="normal"
/>
{checklist.map((item, index) => (
<div key={index} onClick={() => handleChecklistItemClick(item.id)} style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }} >
{checkedItems[item.id] ? (
<CheckBoxIcon fontSize="medium" />
) : (
<CheckBoxOutlineBlankIcon fontSize="medium" />
)}
<Typography variant="body1" component="span" style={{ marginLeft: 5, fontSize: '20px' }}>
{item.value}
</Typography>
</div>
))}
</DialogContent>
<DialogActions>
<Button onClick={handleAddChecklistItem}>Add</Button>
</DialogActions>
</Dialog>
</div>
)
}
export default TodoList;
I’m trying to trying to make a popup that can add checklists for each task individually.