Skip to content
This repository was archived by the owner on Jun 14, 2025. It is now read-only.

Commit aa2c6bf

Browse files
committed
Project milestones; Admins can be assigned to tasks
1 parent 3bbe69e commit aa2c6bf

File tree

7 files changed

+741
-7
lines changed

7 files changed

+741
-7
lines changed

src/pages/projects/[id].tsx

Lines changed: 336 additions & 1 deletion
Large diffs are not rendered by default.

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

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import Navbar from "~/components/Navbar";
1010
type TaskPriority = "LOW" | "MEDIUM" | "HIGH" | "URGENT";
1111
type TaskStatus = "TODO" | "IN_PROGRESS" | "REVIEW" | "COMPLETED";
1212

13+
interface UserType {
14+
id: string;
15+
name: string;
16+
email: string;
17+
role: string;
18+
}
19+
1320
const EditTaskPage: NextPage = () => {
1421
const router = useRouter();
1522
const { status } = useSession();
@@ -22,12 +29,14 @@ const EditTaskPage: NextPage = () => {
2229
priority: TaskPriority;
2330
status: TaskStatus;
2431
dueDate: string;
32+
assigneeIds: string[];
2533
}>({
2634
title: "",
2735
description: "",
2836
priority: "MEDIUM",
2937
status: "TODO",
3038
dueDate: "",
39+
assigneeIds: [],
3140
});
3241

3342
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -37,6 +46,12 @@ const EditTaskPage: NextPage = () => {
3746
{ enabled: !!taskId && status === "authenticated" }
3847
);
3948

49+
// Get available users - project members + admins
50+
const { data: users, isLoading: usersLoading } = api.user.getAll.useQuery(
51+
undefined,
52+
{ enabled: status === "authenticated" }
53+
);
54+
4055
const updateTaskMutation = api.task.update.useMutation({
4156
onSuccess: () => {
4257
void router.push(`/tasks/${taskId}`);
@@ -47,6 +62,16 @@ const EditTaskPage: NextPage = () => {
4762
},
4863
});
4964

65+
const assignUsersMutation = api.task.assignUsers.useMutation({
66+
onSuccess: () => {
67+
void router.push(`/tasks/${taskId}`);
68+
},
69+
onError: (error: unknown) => {
70+
console.error("Error assigning users:", error);
71+
setIsSubmitting(false);
72+
},
73+
});
74+
5075
// Populate form when task data loads
5176
useEffect(() => {
5277
if (task) {
@@ -56,6 +81,7 @@ const EditTaskPage: NextPage = () => {
5681
priority: task.priority,
5782
status: task.status,
5883
dueDate: task.dueDate ? new Date(task.dueDate).toISOString().split('T')[0]! : "",
84+
assigneeIds: task.assignments?.map(a => a.user.id) ?? [],
5985
});
6086
}
6187
}, [task]);
@@ -87,15 +113,29 @@ const EditTaskPage: NextPage = () => {
87113
const submitData = {
88114
id: taskId,
89115
title: formData.title,
90-
description: formData.description || undefined,
116+
description: formData.description ?? undefined,
91117
priority: formData.priority,
92118
status: formData.status,
93119
dueDate: formData.dueDate ? new Date(formData.dueDate) : undefined,
94120
};
95121

96-
updateTaskMutation.mutate(submitData);
122+
try {
123+
// Update task details first
124+
await updateTaskMutation.mutateAsync(submitData);
125+
126+
// Then update assignments
127+
await assignUsersMutation.mutateAsync({
128+
taskId,
129+
userIds: formData.assigneeIds,
130+
});
131+
} catch (error) {
132+
console.error("Error updating task:", error);
133+
setIsSubmitting(false);
134+
}
97135
};
98136

137+
const usersList = (users as UserType[] | undefined) ?? [];
138+
99139
return (
100140
<div className="min-h-screen bg-background">
101141
<Navbar />
@@ -206,6 +246,37 @@ const EditTaskPage: NextPage = () => {
206246
/>
207247
</div>
208248

249+
{/* Team Assignment */}
250+
{!usersLoading && usersList.length > 0 && (
251+
<div>
252+
<h3 className="text-lg font-semibold text-foreground mb-4 pb-2 border-b border-border">
253+
Team Assignment
254+
</h3>
255+
<div>
256+
<label className="label">
257+
Assign To
258+
</label>
259+
<select
260+
multiple
261+
aria-label="Select assignees"
262+
value={formData.assigneeIds}
263+
onChange={(e) => {
264+
const selectedOptions = Array.from(e.target.selectedOptions, option => option.value);
265+
setFormData({ ...formData, assigneeIds: selectedOptions });
266+
}}
267+
className="select h-32"
268+
>
269+
{usersList.map((user) => (
270+
<option key={user.id} value={user.id}>
271+
{user.name} ({user.email}) {user.role === "ADMIN" ? "[Admin]" : ""}
272+
</option>
273+
))}
274+
</select>
275+
<p className="mt-1 text-xs text-muted-foreground">Hold Ctrl/Cmd to select multiple users</p>
276+
</div>
277+
</div>
278+
)}
279+
209280
{/* Submit Buttons */}
210281
<div className="flex justify-end space-x-4 pt-6 border-t border-border">
211282
<Link
@@ -227,7 +298,7 @@ const EditTaskPage: NextPage = () => {
227298
</div>
228299

229300
{/* Error Display */}
230-
{updateTaskMutation.error && (
301+
{(updateTaskMutation.error ?? assignUsersMutation.error) && (
231302
<div className="mt-6 max-w-2xl mx-auto">
232303
<div className="card bg-destructive/5 border-destructive/20 p-4">
233304
<div className="flex">
@@ -236,7 +307,7 @@ const EditTaskPage: NextPage = () => {
236307
Error updating task
237308
</h3>
238309
<div className="mt-2 text-sm text-destructive/80">
239-
<p>{updateTaskMutation.error.message}</p>
310+
<p>{updateTaskMutation.error?.message ?? assignUsersMutation.error?.message}</p>
240311
</div>
241312
</div>
242313
</div>

src/pages/tasks/new.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface UserType {
1616
id: string;
1717
name: string;
1818
email: string;
19+
role: string;
1920
}
2021

2122
const NewTaskPage: NextPage = () => {
@@ -252,7 +253,7 @@ const NewTaskPage: NextPage = () => {
252253
>
253254
{usersList.map((user) => (
254255
<option key={user.id} value={user.id}>
255-
{user.name} ({user.email})
256+
{user.name} ({user.email}) {user.role === "ADMIN" ? "[Admin]" : ""}
256257
</option>
257258
))}
258259
</select>

src/server/api/root.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { projectRouter } from "~/server/api/routers/project";
33
import { taskRouter } from "~/server/api/routers/task";
44
import { categoriesTagsRouter } from "./routers/categories-tags";
55
import { userRouter } from "~/server/api/routers/user";
6+
import { milestoneRouter } from "~/server/api/routers/milestone";
67

78
/**
89
* This is the primary router for your server.
@@ -14,6 +15,7 @@ export const appRouter = createTRPCRouter({
1415
task: taskRouter,
1516
categoriesTags: categoriesTagsRouter,
1617
user: userRouter,
18+
milestone: milestoneRouter,
1719
});
1820

1921
// export type definition of API

0 commit comments

Comments
 (0)