From 1d59eba8b8055e7744f5b76f5e9a9f86cf3df328 Mon Sep 17 00:00:00 2001 From: Precipicee <23813728@student.uwa.edu.au> Date: Sat, 10 Jan 2026 00:21:42 +0800 Subject: [PATCH 1/2] fixed empty time input --- client/src/components/task_form.tsx | 5 ++- client/src/components/task_item.tsx | 4 +- client/src/components/time_input.tsx | 55 ++++++++++++++-------------- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/client/src/components/task_form.tsx b/client/src/components/task_form.tsx index 96b8171..e06834a 100644 --- a/client/src/components/task_form.tsx +++ b/client/src/components/task_form.tsx @@ -66,6 +66,9 @@ export function TaskForm({ userId, onTaskCreated }: TaskFormProps) { const new_topics = topics .filter((t) => t.type === "new") .map((t) => ({ name: t.name, color_hex: t.color_hex })); + + const validTimes = times.filter(t => t.start_time && t.end_time); + const response = await fetch("http://localhost:8000/api/planner/tasks/", { method: "POST", headers: { @@ -77,7 +80,7 @@ export function TaskForm({ userId, onTaskCreated }: TaskFormProps) { description: description, completed: false, user_id: userId, - times: times, + times: validTimes, existing_topic_ids: existing_topic_ids, new_topics: new_topics, }), diff --git a/client/src/components/task_item.tsx b/client/src/components/task_item.tsx index b2d0208..e4e645c 100644 --- a/client/src/components/task_item.tsx +++ b/client/src/components/task_item.tsx @@ -93,6 +93,8 @@ export function TaskItem({ color_hex: t.color_hex, })); + const validTimes = draftTimes.filter(t => t.start_time && t.end_time); + const response = await fetch( `http://localhost:8000/api/planner/tasks/${item.id}/`, { @@ -107,7 +109,7 @@ export function TaskItem({ completed: item.completed, existing_topic_ids: existing_topic_ids, new_topics: new_topics, - times: draftTimes, + times: validTimes, }), }, ); diff --git a/client/src/components/time_input.tsx b/client/src/components/time_input.tsx index 30eb937..7cb5020 100644 --- a/client/src/components/time_input.tsx +++ b/client/src/components/time_input.tsx @@ -22,17 +22,35 @@ const daysOfWeek = [ ]; export function TimeInput({ times, setTimes }: TimeInputProps) { + + const addTime = () => { + // Add a new empty draft time only if there isn't already an empty one + const hasEmpty = times.some(t => !t.start_time || !t.end_time); + if (!hasEmpty) { + setTimes([ + ...times, + { id: 0, day: 1, start_time: "", end_time: "", repeating: false }, + ]); + } + }; + + const updateTime = (index: number, field: keyof Time, value: any) => { + const newTimes = [...times]; + newTimes[index] = { ...newTimes[index], [field]: value }; + setTimes(newTimes); + }; + + const removeTime = (index: number) => { + setTimes(times.filter((_, i) => i !== index)); + }; + return (
{times.map((time, index) => (
{ - const newTimes = [...times]; - newTimes[index].start_time = e.target.value; - setTimes(newTimes); - }} + onChange={(e) => updateTime(index, "start_time", e.target.value)} /> { - const newTimes = [...times]; - newTimes[index].end_time = e.target.value; - setTimes(newTimes); - }} + onChange={(e) => updateTime(index, "end_time", e.target.value)} /> { - const newTimes = [...times]; - newTimes[index].repeating = e.target.checked; - setTimes(newTimes); - }} + onChange={(e) => updateTime(index, "repeating", e.target.checked)} /> @@ -77,12 +83,7 @@ export function TimeInput({ times, setTimes }: TimeInputProps) { ))} From ebeb1a6268ef8f0c115186c2290cc1889d89122b Mon Sep 17 00:00:00 2001 From: Precipicee <23813728@student.uwa.edu.au> Date: Sat, 10 Jan 2026 08:46:21 +0800 Subject: [PATCH 2/2] made topics user-specific rather than general --- client/src/components/task_form.tsx | 28 +++++++++++++++++++++++----- client/src/pages/tasks.tsx | 27 +++++++++++++++++++++++---- server/task_planner/views.py | 5 ++++- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/client/src/components/task_form.tsx b/client/src/components/task_form.tsx index 4658572..1c7d8ad 100644 --- a/client/src/components/task_form.tsx +++ b/client/src/components/task_form.tsx @@ -43,10 +43,28 @@ export function TaskForm({ userId, onTaskCreated }: TaskFormProps) { const [availableTopics, setAvailableTopics] = useState([]); useEffect(() => { - fetch("http://localhost:8000/api/planner/topic/") - .then((res) => res.json()) - .then((data) => setAvailableTopics(data)) - .catch((err) => console.error("Failed to load topics:", err)); + async function fetchTopics() { + try { + const token = localStorage.getItem("access"); + if (!token) return; + + const res = await fetch("http://localhost:8000/api/planner/topic/", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!res.ok) { + throw new Error("Failed to load topics"); + } + + const data = await res.json(); + setAvailableTopics(data); + } catch (err) { + console.error("Failed to load topics", err); + } + } + fetchTopics(); }, []); const handleSubmit = async (e: React.FormEvent) => { @@ -67,7 +85,7 @@ export function TaskForm({ userId, onTaskCreated }: TaskFormProps) { .filter((t) => t.type === "new") .map((t) => ({ name: t.name, color_hex: t.color_hex })); - const validTimes = times.filter(t => t.start_time && t.end_time); + const validTimes = times.filter((t) => t.start_time && t.end_time); const response = await fetch("http://localhost:8000/api/planner/tasks/", { method: "POST", diff --git a/client/src/pages/tasks.tsx b/client/src/pages/tasks.tsx index f89c9f8..b0984bd 100644 --- a/client/src/pages/tasks.tsx +++ b/client/src/pages/tasks.tsx @@ -85,10 +85,29 @@ export default function TasksPage() { }, [router]); useEffect(() => { - fetch("http://localhost:8000/api/planner/topic/") - .then((res) => res.json()) - .then((data) => setAvailableTopics(data)) - .catch((err) => console.error("Failed to load topics", err)); + async function fetchTopics() { + try { + const token = localStorage.getItem("access"); + if (!token) return; + + const res = await fetch("http://localhost:8000/api/planner/topic/", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!res.ok) { + throw new Error("Failed to load topics"); + } + + const data = await res.json(); + setAvailableTopics(data); + } catch (err) { + console.error("Failed to load topics", err); + } + } + + fetchTopics(); }, []); if (loading) { diff --git a/server/task_planner/views.py b/server/task_planner/views.py index 3b32ef7..89de296 100644 --- a/server/task_planner/views.py +++ b/server/task_planner/views.py @@ -55,8 +55,11 @@ def get(self, request): }) class TopicList(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + def get(self, request): - topics = Topic.objects.all() + topics = Topic.objects.filter(user=request.user) serializer = TopicReadSerializer(topics, many=True) return Response(serializer.data)