Skip to content

Commit 3a7437a

Browse files
committed
Add subtask CRUD and axios for it
1 parent 97e4cc5 commit 3a7437a

2 files changed

Lines changed: 103 additions & 61 deletions

File tree

frontend/src/api/SubTaskApi.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { axiosInstance } from "./AxiosConfig";
22

3-
export const getSubtasks = async (parentTaskId) => {
3+
export const getSubtask = async (parentTaskId) => {
44
try {
55
const response = await axiosInstance.get(`subtasks?parent_task=${parentTaskId}`);
66
return response.data;
@@ -10,10 +10,11 @@ export const getSubtasks = async (parentTaskId) => {
1010
}
1111
};
1212

13-
export const addSubtask = async (parentTaskId, text) => {
13+
export const addSubtasks = async (parentTaskId, text) => {
1414
try {
1515
const response = await axiosInstance.post("subtasks/", {
16-
text,
16+
description: text,
17+
completed: false,
1718
parent_task: parentTaskId,
1819
});
1920
return response.data;
@@ -23,11 +24,21 @@ export const addSubtask = async (parentTaskId, text) => {
2324
}
2425
};
2526

26-
export const deleteSubtask = async (subtaskId) => {
27+
export const deleteSubtasks = async (subtaskId) => {
2728
try {
2829
await axiosInstance.delete(`subtasks/${subtaskId}/`);
2930
} catch (error) {
3031
console.error("Error deleting subtask:", error);
3132
throw error;
3233
}
3334
};
35+
36+
export const updateSubtask = async (subtaskId, data) => {
37+
try {
38+
const response = await axiosInstance.patch(`subtasks/${subtaskId}/`, data);
39+
return response.data;
40+
} catch (error) {
41+
console.error("Error updating subtask:", error);
42+
throw error;
43+
}
44+
};

frontend/src/components/kanbanBoard/taskDetailModal.jsx

Lines changed: 88 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1-
import { useState } from "react";
1+
import { useState, useEffect } from "react";
22
import { FaTasks, FaRegListAlt } from "react-icons/fa";
3-
import { FaPlus } from "react-icons/fa6";
3+
import { FaPlus, FaRegTrashCan } from "react-icons/fa6";
44
import { TbChecklist } from "react-icons/tb";
55
import DatePicker from "react-datepicker";
66
import { TimePicker } from "react-ios-time-picker";
77
import "react-datepicker/dist/react-datepicker.css";
8-
import { borderColor } from "@mui/system";
8+
import { addSubtasks, deleteSubtasks, getSubtask, updateSubtask } from "src/api/SubTaskApi";
99

10-
export function TaskDetailModal({
11-
title,
12-
description,
13-
tags,
14-
difficulty,
15-
challenge,
16-
importance,
17-
taskId,
18-
updateTask,
19-
}) {
20-
let date = new Date();
10+
export function TaskDetailModal({ title, description, tags, difficulty, challenge, importance, taskId, updateTask }) {
2111
const [isChallengeChecked, setChallengeChecked] = useState(challenge);
2212
const [isImportantChecked, setImportantChecked] = useState(importance);
2313
const [currentDifficulty, setCurrentDifficulty] = useState(difficulty);
@@ -27,7 +17,9 @@ export function TaskDetailModal({
2717
const [startDateEnabled, setStartDateEnabled] = useState(false);
2818
const [endDateEnabled, setEndDateEnabled] = useState(false);
2919
const [isTaskComplete, setTaskComplete] = useState(false);
30-
const [value, setValue] = useState('10:00');
20+
const [value, setValue] = useState("10:00");
21+
const [subtaskText, setSubtaskText] = useState("");
22+
const [subtasks, setSubtasks] = useState([]);
3123

3224
const onChange = (timeValue) => {
3325
setValue(timeValue);
@@ -46,11 +38,7 @@ export function TaskDetailModal({
4638

4739
const handleTagChange = (tag) => {
4840
const isSelected = selectedTags.includes(tag);
49-
setSelectedTags(
50-
isSelected
51-
? selectedTags.filter((selectedTag) => selectedTag !== tag)
52-
: [...selectedTags, tag]
53-
);
41+
setSelectedTags(isSelected ? selectedTags.filter((selectedTag) => selectedTag !== tag) : [...selectedTags, tag]);
5442
};
5543

5644
const handleStartDateChange = () => {
@@ -75,12 +63,73 @@ export function TaskDetailModal({
7563
}
7664
};
7765

66+
const addSubtask = async () => {
67+
try {
68+
if (subtaskText.trim() !== "") {
69+
const newSubtask = await addSubtasks(taskId, subtaskText.trim());
70+
setSubtasks([...subtasks, newSubtask]);
71+
setSubtaskText("");
72+
}
73+
} catch (error) {
74+
console.error("Error adding subtask:", error);
75+
}
76+
};
77+
78+
const toggleSubtaskCompletion = async (index) => {
79+
try {
80+
const updatedSubtasks = [...subtasks];
81+
updatedSubtasks[index].completed = !updatedSubtasks[index].completed;
82+
await updateSubtask(updatedSubtasks[index].id, { completed: updatedSubtasks[index].completed });
83+
setSubtasks(updatedSubtasks);
84+
} catch (error) {
85+
console.error("Error updating subtask:", error);
86+
}
87+
};
88+
89+
const deleteSubtask = async (index) => {
90+
try {
91+
await deleteSubtasks(subtasks[index].id);
92+
const updatedSubtasks = [...subtasks];
93+
updatedSubtasks.splice(index, 1);
94+
setSubtasks(updatedSubtasks);
95+
} catch (error) {
96+
console.error("Error deleting subtask:", error);
97+
}
98+
};
99+
100+
const subtaskElements = subtasks.map((subtask, index) => (
101+
<div key={index} className="flex items-center space-x-2">
102+
<input
103+
type="checkbox"
104+
checked={subtask.completed}
105+
className="checkbox checkbox-xs bg-gray-400"
106+
onChange={() => toggleSubtaskCompletion(index)}
107+
/>
108+
<div className={`flex items-center rounded p-2 shadow border-2 ${subtask.completed && "line-through"}`}>
109+
{subtask.description}
110+
<FaRegTrashCan className="cursor-pointer ml-2 text-red-500" onClick={() => deleteSubtask(index)} />
111+
</div>
112+
</div>
113+
));
114+
115+
useEffect(() => {
116+
const fetchSubtasks = async () => {
117+
try {
118+
const fetchedSubtasks = await getSubtask(taskId);
119+
setSubtasks(fetchedSubtasks);
120+
} catch (error) {
121+
console.error("Error fetching subtasks:", error);
122+
}
123+
};
124+
125+
fetchSubtasks();
126+
}, [taskId]);
127+
78128
// Existing tags
79129
const existingTags = tags.map((tag, index) => (
80130
<div
81131
key={index}
82-
className={`text-xs inline-flex items-center font-bold leading-sm uppercase px-2 py-1 bg-${tag.color}-200 text-${tag.color}-700 rounded-full`}
83-
>
132+
className={`text-xs inline-flex items-center font-bold leading-sm uppercase px-2 py-1 bg-${tag.color}-200 text-${tag.color}-700 rounded-full`}>
84133
{tag.label}
85134
</div>
86135
));
@@ -89,8 +138,7 @@ export function TaskDetailModal({
89138
const selectedTagElements = selectedTags.map((tag, index) => (
90139
<div
91140
key={index}
92-
className={`text-xs inline-flex items-center font-bold leading-sm uppercase px-2 py-1 bg-${tag.color}-200 text-${tag.color}-700 rounded-full`}
93-
>
141+
className={`text-xs inline-flex items-center font-bold leading-sm uppercase px-2 py-1 bg-${tag.color}-200 text-${tag.color}-700 rounded-full`}>
94142
{tag.label}
95143
</div>
96144
));
@@ -114,16 +162,10 @@ export function TaskDetailModal({
114162
<div className="flex flex-col py-2 pb-4">
115163
<div className="flex flex-row space-x-5">
116164
<div className="dropdown">
117-
<label
118-
tabIndex={0}
119-
className="btn-md border-2 rounded-xl m-1 py-1"
120-
>
165+
<label tabIndex={0} className="btn-md border-2 rounded-xl m-1 py-1">
121166
+ Add Tags
122167
</label>
123-
<ul
124-
tabIndex={0}
125-
className="dropdown-content z-[10] menu p-2 shadow bg-base-100 rounded-box w-52"
126-
>
168+
<ul tabIndex={0} className="dropdown-content z-[10] menu p-2 shadow bg-base-100 rounded-box w-52">
127169
{tags.map((tag, index) => (
128170
<li key={index}>
129171
<label className="cursor-pointer space-x-2">
@@ -155,26 +197,21 @@ export function TaskDetailModal({
155197
<input
156198
type="checkbox"
157199
checked={startDateEnabled}
158-
className="checkbox checkbox-xs bg-black"
200+
className="checkbox checkbox-xs bg-gray-400"
159201
onChange={handleStartDateChange}
160202
/>
161-
<div
162-
className={`rounded p-2 shadow border-2 ${
163-
!startDateEnabled && "opacity-50"
164-
}`}
165-
>
203+
<div className={`rounded p-2 shadow border-2 ${!startDateEnabled && "opacity-50"}`}>
166204
<DatePicker
167205
selected={dateStart}
168206
onChange={(date) => setDateStart(date)}
169207
disabled={!startDateEnabled}
170208
/>
171209
</div>
172210
</div>
173-
</div>
211+
</div>
174212

175213
<div className="rounded p-2 shadow border-2">
176-
177-
<TimePicker
214+
<TimePicker
178215
value={value}
179216
onChange={onChange}
180217
className="rounded p-2 shadow border-2 z-[10000] relative"
@@ -188,7 +225,7 @@ export function TaskDetailModal({
188225
<input
189226
type="checkbox"
190227
checked={isTaskComplete}
191-
className="checkbox checkbox-xl bg-black"
228+
className="checkbox checkbox-xl bg-gray-400"
192229
onChange={handleTaskCompleteChange}
193230
/>
194231
</div>
@@ -202,19 +239,11 @@ export function TaskDetailModal({
202239
<input
203240
type="checkbox"
204241
checked={endDateEnabled}
205-
className="checkbox checkbox-xs bg-black"
242+
className="checkbox checkbox-xs bg-gray-400"
206243
onChange={handleEndDateChange}
207244
/>
208-
<div
209-
className={`rounded p-2 shadow border-2 ${
210-
!endDateEnabled && "opacity-50"
211-
}`}
212-
>
213-
<DatePicker
214-
selected={dateEnd}
215-
onChange={(date) => setDateEnd(date)}
216-
disabled={!endDateEnabled}
217-
/>
245+
<div className={`rounded p-2 shadow border-2 ${!endDateEnabled && "opacity-50"}`}>
246+
<DatePicker selected={dateEnd} onChange={(date) => setDateEnd(date)} disabled={!endDateEnabled} />
218247
</div>
219248
</div>
220249
</div>
@@ -295,17 +324,19 @@ export function TaskDetailModal({
295324
type="text"
296325
placeholder="subtask topic"
297326
className="input input-bordered flex-1 w-full"
327+
value={subtaskText}
328+
onChange={(e) => setSubtaskText(e.target.value)}
298329
/>
299-
<button className="btn">
330+
<button className="btn" onClick={addSubtask}>
300331
<FaPlus />
301332
Add Subtask
302333
</button>
303334
</div>
335+
{/* Display Subtasks */}
336+
<div className="flex flex-col space-y-2 pt-2">{subtaskElements}</div>
304337
</div>
305338
<form method="dialog">
306-
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">
307-
X
308-
</button>
339+
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">X</button>
309340
</form>
310341
</div>
311342
</dialog>

0 commit comments

Comments
 (0)