Skip to content

Commit 08e6df4

Browse files
committed
created basic add form and altered backend to be more extensible
1 parent 499da5d commit 08e6df4

5 files changed

Lines changed: 137 additions & 23 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useState } from "react";
2+
3+
interface Time {
4+
id: number;
5+
day: number;
6+
start_time: string;
7+
end_time: string;
8+
repeating: boolean;
9+
}
10+
11+
interface Topic {
12+
id: number;
13+
name: string;
14+
color_hex: number;
15+
}
16+
17+
interface Item {
18+
id: number;
19+
name: string;
20+
completed: boolean;
21+
description: string;
22+
times: Time[];
23+
topics: Topic[];
24+
}
25+
26+
interface TaskFormProps {
27+
userId: number;
28+
onTaskCreated: (task: Item) => void;
29+
}
30+
31+
export function TaskForm({ userId, onTaskCreated }: TaskFormProps) {
32+
const [taskName, setTaskName] = useState("");
33+
const [description, setDescription] = useState("");
34+
35+
const handleSubmit = async (e: React.FormEvent) => {
36+
e.preventDefault();
37+
try {
38+
const response = await fetch("http://localhost:8000/api/planner/tasks/", {
39+
method: "POST",
40+
headers: {
41+
"Content-Type": "application/json",
42+
},
43+
body: JSON.stringify({
44+
name: taskName,
45+
description: description,
46+
completed: false,
47+
user_id: userId,
48+
}),
49+
});
50+
51+
if (!response.ok) {
52+
throw new Error("Failed to create task");
53+
}
54+
55+
const newTask = await response.json();
56+
onTaskCreated(newTask);
57+
setTaskName("");
58+
setDescription("");
59+
} catch (error) {
60+
console.error("Error creating task:", error);
61+
}
62+
};
63+
64+
return (
65+
<form
66+
onSubmit={handleSubmit}
67+
className="space-y-4 rounded-xl border bg-zinc-700 p-4"
68+
>
69+
<input
70+
type="text"
71+
value={taskName}
72+
onChange={(e) => setTaskName(e.target.value)}
73+
placeholder="Task Name"
74+
className="w-full rounded border bg-zinc-800 p-2 text-zinc-200"
75+
required
76+
/>
77+
<textarea
78+
value={description}
79+
onChange={(e) => setDescription(e.target.value)}
80+
placeholder="Description"
81+
className="w-full rounded border bg-zinc-800 p-2 text-zinc-200"
82+
/>
83+
<button
84+
type="submit"
85+
className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
86+
>
87+
Add Task
88+
</button>
89+
</form>
90+
);
91+
}

client/src/components/task_item.tsx

Lines changed: 6 additions & 6 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;

client/src/components/task_list.tsx

Lines changed: 6 additions & 6 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[];

client/src/pages/[id]/tasks.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
import { useRouter } from "next/router";
22
import { useEffect, useState } from "react";
33

4+
import { TaskForm } from "@/components/task_form";
45
import { TaskList } from "@/components/task_list";
56

6-
type Time = {
7+
interface Time {
78
id: number;
89
day: number;
910
start_time: string;
1011
end_time: string;
1112
repeating: boolean;
12-
};
13+
}
1314

14-
type Topic = {
15+
interface Topic {
1516
id: number;
1617
name: string;
1718
color_hex: number;
18-
};
19+
}
1920

20-
type Item = {
21+
interface Item {
2122
id: number;
2223
name: string;
2324
completed: boolean;
2425
description: string;
2526
times: Time[];
2627
topics: Topic[];
27-
};
28+
}
2829

2930
export default function TasksPage() {
3031
const router = useRouter();
@@ -43,7 +44,6 @@ export default function TasksPage() {
4344
);
4445
const data = await response.json();
4546
setItems(data);
46-
console.log(data);
4747
} catch (err) {
4848
console.error("Failed to load tasks:", err);
4949
} finally {
@@ -57,6 +57,10 @@ export default function TasksPage() {
5757
return <p>Loading tasks...</p>;
5858
}
5959

60+
const handleTaskCreated = (task: Item) => {
61+
setItems((prev) => [...prev, task]);
62+
};
63+
6064
function handleToggleTask(id: number) {
6165
setItems((prevItems) =>
6266
prevItems.map((item) =>
@@ -66,9 +70,15 @@ export default function TasksPage() {
6670
}
6771

6872
return (
69-
<div className="m-4 h-fit w-fit rounded-xl bg-zinc-700 p-4 text-center">
70-
<h1 className="mb-4 text-3xl font-bold text-zinc-300">Task List</h1>
71-
<TaskList items={items} onToggleTask={handleToggleTask} />
73+
<div>
74+
<div className="m-4 h-fit w-fit rounded-xl bg-zinc-700 p-4 text-center">
75+
<h1 className="mb-4 text-3xl font-bold text-zinc-300">Task List</h1>
76+
<TaskList items={items} onToggleTask={handleToggleTask} />
77+
</div>
78+
<div className="m-4 h-fit w-fit rounded-xl bg-zinc-700 p-4 text-center">
79+
<h1 className="mb-4 text-3xl font-bold text-zinc-300">Add Task</h1>
80+
<TaskForm userId={Number(id)} onTaskCreated={handleTaskCreated} />
81+
</div>
7282
</div>
7383
);
7484
}

server/task_planner/views.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from django.shortcuts import render
22

33
# from rest_framework import generics, permissions
4+
from rest_framework.exceptions import ValidationError
45
from rest_framework.response import Response
56
from rest_framework.views import APIView
67
from rest_framework.viewsets import ModelViewSet
8+
#from rest_framework.permissions import IsAuthenticated
79

810
from .models import Task, Topic, Time
911
from .serializers import TaskReadSerializer, TaskWriteSerializer, TopicSerializer, TimeSerializer
@@ -25,6 +27,9 @@ def get(self, request):
2527
return Response(serializer.data)
2628

2729
class TaskViewSet(ModelViewSet):
30+
31+
#permission_classes = [IsAuthenticated] # Uncomment when authentication is added.
32+
2833
def get_serializer_class(self):
2934
if self.action in ['list', 'retrieve']:
3035
return TaskReadSerializer
@@ -41,4 +46,12 @@ def get_queryset(self):
4146
return Task.objects.all()
4247

4348
def perform_create(self, serializer):
44-
serializer.save(user=self.request.user)
49+
# Use explicit user_id when provided (no auth yet); otherwise require auth
50+
user_id = self.request.data.get("user_id")
51+
if user_id:
52+
serializer.save(user_id=user_id)
53+
return
54+
if self.request.user and self.request.user.is_authenticated:
55+
serializer.save(user=self.request.user)
56+
return
57+
raise ValidationError({"user_id": "Provide user_id or authenticate."})

0 commit comments

Comments
 (0)