Skip to content

Commit 04a91e6

Browse files
committed
Merge branch 'issue-7-Allow_CRUD_Operations_on_Tasks' of https://github.com/codersforcauses/intermediate_team_3 into use_uv
2 parents 1bc5a5e + 428f0cc commit 04a91e6

9 files changed

Lines changed: 518 additions & 65 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { useEffect, useState } from "react";
2+
3+
import { TimeInput } from "@/components/time_input";
4+
import { TopicInput } from "@/components/topic_input";
5+
6+
interface Time {
7+
id: number;
8+
day: number;
9+
start_time: string;
10+
end_time: string;
11+
repeating: boolean;
12+
}
13+
14+
interface Topic {
15+
id: number;
16+
name: string;
17+
color_hex: number;
18+
}
19+
20+
type TopicSelection =
21+
| { type: "existing"; id: number }
22+
| { type: "new"; name: string; color_hex: number };
23+
24+
interface Item {
25+
id: number;
26+
name: string;
27+
completed: boolean;
28+
description: string;
29+
times: Time[];
30+
topics: Topic[];
31+
}
32+
33+
interface TaskFormProps {
34+
userId: number;
35+
onTaskCreated: (task: Item) => void;
36+
}
37+
38+
export function TaskForm({ userId, onTaskCreated }: TaskFormProps) {
39+
const [taskName, setTaskName] = useState("");
40+
const [description, setDescription] = useState("");
41+
const [times, setTimes] = useState<Time[]>([]);
42+
const [topics, setTopics] = useState<TopicSelection[]>([]);
43+
const [availableTopics, setAvailableTopics] = useState<Topic[]>([]);
44+
45+
useEffect(() => {
46+
fetch("http://localhost:8000/api/planner/topic/")
47+
.then((res) => res.json())
48+
.then((data) => setAvailableTopics(data))
49+
.catch((err) => console.error("Failed to load topics:", err));
50+
}, []);
51+
52+
const handleSubmit = async (e: React.FormEvent) => {
53+
e.preventDefault();
54+
try {
55+
const existing_topic_ids = topics
56+
.filter((t) => t.type === "existing")
57+
.map((t) => t.id);
58+
59+
const new_topics = topics
60+
.filter((t) => t.type === "new")
61+
.map((t) => ({ name: t.name, color_hex: t.color_hex }));
62+
const response = await fetch("http://localhost:8000/api/planner/tasks/", {
63+
method: "POST",
64+
headers: {
65+
"Content-Type": "application/json",
66+
},
67+
body: JSON.stringify({
68+
name: taskName,
69+
description: description,
70+
completed: false,
71+
user_id: userId,
72+
times: times,
73+
existing_topic_ids: existing_topic_ids,
74+
new_topics: new_topics,
75+
}),
76+
});
77+
78+
if (!response.ok) {
79+
throw new Error("Failed to create task");
80+
}
81+
82+
const newTask = await response.json();
83+
onTaskCreated(newTask);
84+
setTaskName("");
85+
setDescription("");
86+
setTimes([]);
87+
setTopics([]);
88+
} catch (error) {
89+
console.error("Error creating task:", error);
90+
}
91+
};
92+
93+
return (
94+
<form
95+
onSubmit={handleSubmit}
96+
className="mx-auto h-[79vh] w-full max-w-md space-y-4 overflow-y-auto rounded-xl bg-zinc-700 p-4"
97+
>
98+
<input
99+
type="text"
100+
value={taskName}
101+
onChange={(e) => setTaskName(e.target.value)}
102+
placeholder="Task Name"
103+
className="w-full rounded border-2 bg-zinc-700 p-2 text-zinc-200"
104+
required
105+
/>
106+
<textarea
107+
value={description}
108+
onChange={(e) => setDescription(e.target.value)}
109+
placeholder="Description"
110+
className="h-32 w-full resize-none rounded border-2 bg-zinc-700 p-2 text-zinc-200"
111+
/>
112+
<TimeInput times={times} setTimes={setTimes} />
113+
<TopicInput
114+
availableTopics={availableTopics}
115+
topics={topics}
116+
setTopics={setTopics}
117+
/>
118+
119+
<button
120+
type="submit"
121+
className="rounded border-2 border-zinc-200 bg-blue-600 px-4 py-2 text-zinc-200 hover:bg-blue-700"
122+
>
123+
Add Task
124+
</button>
125+
</form>
126+
);
127+
}

client/src/components/task_item.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
type Time = {
1+
interface Time {
22
id: number;
33
day: number;
44
start_time: string;
55
end_time: string;
66
repeating: boolean;
7-
};
7+
}
88

9-
type Topic = {
9+
interface Topic {
1010
id: number;
1111
name: string;
1212
color_hex: number;
13-
};
13+
}
1414

15-
type Item = {
15+
interface Item {
1616
id: number;
1717
name: string;
1818
completed: boolean;
1919
description: string;
2020
times: Time[];
2121
topics: Topic[];
22-
};
22+
}
2323

2424
type ItemProps = {
2525
item: Item;
@@ -46,7 +46,7 @@ export function TaskItem({ item, onToggle }: ItemProps) {
4646
</span>
4747
</div>
4848
<div className="flex flex-col items-end text-sm text-zinc-300">
49-
{item.times.length > 0
49+
{item.times?.length > 0
5050
? item.times.map((t) => (
5151
<span key={t.id}>
5252
{formatDay(t.day)} {t.start_time.slice(0, 5)} -{" "}
@@ -57,12 +57,12 @@ export function TaskItem({ item, onToggle }: ItemProps) {
5757
</div>
5858
</div>
5959

60-
<div className="flex space-x-1 text-sm text-zinc-300">
61-
{item.topics.length > 0
60+
<div className="flex flex-wrap gap-1 text-sm text-zinc-300">
61+
{item.topics?.length > 0
6262
? item.topics.map((topic) => (
6363
<span
6464
key={topic.id}
65-
className="flex items-center gap-1 rounded-full border-2 border-zinc-800 px-2 py-0.5 text-zinc-100"
65+
className="flex items-center gap-1 rounded-lg border-2 border-zinc-500 px-2 py-0.5 text-zinc-100"
6666
>
6767
<span
6868
className="h-2 w-2 rounded-full"

client/src/components/task_list.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
import { TaskItem } from "./task_item";
22

3-
type Time = {
3+
interface Time {
44
id: number;
55
day: number;
66
start_time: string;
77
end_time: string;
88
repeating: boolean;
9-
};
9+
}
1010

11-
type Topic = {
11+
interface Topic {
1212
id: number;
1313
name: string;
1414
color_hex: number;
15-
};
15+
}
1616

17-
type Item = {
17+
interface Item {
1818
id: number;
1919
name: string;
2020
completed: boolean;
2121
description: string;
2222
times: Time[];
2323
topics: Topic[];
24-
};
24+
}
2525

2626
type ListProps = {
2727
items: Item[];
@@ -37,7 +37,7 @@ export function TaskList({ items, onToggleTask }: ListProps) {
3737
);
3838
}
3939
return (
40-
<div className="mx-auto h-[85vh] w-full max-w-md overflow-y-auto p-2">
40+
<div className="mx-auto h-[79vh] w-full max-w-md overflow-y-auto p-2">
4141
<ul className="space-y-2">
4242
{items.map((item) => (
4343
<li key={item.id}>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
interface Time {
2+
id: number;
3+
day: number;
4+
start_time: string;
5+
end_time: string;
6+
repeating: boolean;
7+
}
8+
9+
interface TimeInputProps {
10+
times: Time[];
11+
setTimes: (times: Time[]) => void;
12+
}
13+
14+
const daysOfWeek = [
15+
{ label: "Mon", value: 1 },
16+
{ label: "Tue", value: 2 },
17+
{ label: "Wed", value: 3 },
18+
{ label: "Thu", value: 4 },
19+
{ label: "Fri", value: 5 },
20+
{ label: "Sat", value: 6 },
21+
{ label: "Sun", value: 7 },
22+
];
23+
24+
export function TimeInput({ times, setTimes }: TimeInputProps) {
25+
return (
26+
<div>
27+
{times.map((time, index) => (
28+
<div key={index}>
29+
<select
30+
value={time.day}
31+
onChange={(e) => {
32+
const newTimes = [...times];
33+
newTimes[index].day = parseInt(e.target.value);
34+
setTimes(newTimes);
35+
}}
36+
>
37+
{daysOfWeek.map((day) => (
38+
<option key={day.value} value={day.value}>
39+
{day.label}
40+
</option>
41+
))}
42+
</select>
43+
<input
44+
type="time"
45+
value={time.start_time}
46+
onChange={(e) => {
47+
const newTimes = [...times];
48+
newTimes[index].start_time = e.target.value;
49+
setTimes(newTimes);
50+
}}
51+
/>
52+
<input
53+
type="time"
54+
value={time.end_time}
55+
onChange={(e) => {
56+
const newTimes = [...times];
57+
newTimes[index].end_time = e.target.value;
58+
setTimes(newTimes);
59+
}}
60+
/>
61+
<input
62+
type="checkbox"
63+
checked={time.repeating}
64+
onChange={(e) => {
65+
const newTimes = [...times];
66+
newTimes[index].repeating = e.target.checked;
67+
setTimes(newTimes);
68+
}}
69+
/>
70+
<button
71+
type="button"
72+
onClick={() => setTimes(times.filter((_, i) => i !== index))}
73+
>
74+
Remove
75+
</button>
76+
</div>
77+
))}
78+
<button
79+
type="button"
80+
onClick={() =>
81+
setTimes([
82+
...times,
83+
{ id: 0, day: 1, start_time: "", end_time: "", repeating: false },
84+
])
85+
}
86+
>
87+
Add Time
88+
</button>
89+
</div>
90+
);
91+
}

0 commit comments

Comments
 (0)