"use client"; import { useState } from "react"; import { useParams } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Select } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { useListProjectsProjectsGet } from "@/api/generated/projects/projects"; import { useListEmployeesEmployeesGet } from "@/api/generated/org/org"; import { useCreateTaskTasksPost, useListTasksTasksGet, useUpdateTaskTasksTaskIdPatch, useDeleteTaskTasksTaskIdDelete, useCreateTaskCommentTaskCommentsPost, useListTaskCommentsTaskCommentsGet, } from "@/api/generated/work/work"; import { useListProjectMembersProjectsProjectIdMembersGet, useAddProjectMemberProjectsProjectIdMembersPost, useRemoveProjectMemberProjectsProjectIdMembersMemberIdDelete, useUpdateProjectMemberProjectsProjectIdMembersMemberIdPatch, } from "@/api/generated/projects/projects"; function getActorEmployeeId(): number | null { if (typeof window === "undefined") return null; try { const v = window.localStorage.getItem("actor_employee_id"); if (!v) return null; const n = Number(v); return Number.isFinite(n) ? n : null; } catch { return null; } } const STATUSES = ["backlog", "ready", "in_progress", "review", "done", "blocked"] as const; export default function ProjectDetailPage() { const params = useParams(); const projectId = Number(params?.id); const projects = useListProjectsProjectsGet(); const projectList = projects.data ?? []; const project = projectList.find((p) => p.id === projectId); const employees = useListEmployeesEmployeesGet(); const employeeList = employees.data ?? []; const members = useListProjectMembersProjectsProjectIdMembersGet(projectId); const memberList = members.data ?? []; const addMember = useAddProjectMemberProjectsProjectIdMembersPost({ mutation: { onSuccess: () => members.refetch() }, }); const removeMember = useRemoveProjectMemberProjectsProjectIdMembersMemberIdDelete({ mutation: { onSuccess: () => members.refetch() }, }); const updateMember = useUpdateProjectMemberProjectsProjectIdMembersMemberIdPatch({ mutation: { onSuccess: () => members.refetch() }, }); const tasks = useListTasksTasksGet({ project_id: projectId }); const taskList = tasks.data ?? []; const createTask = useCreateTaskTasksPost({ mutation: { onSuccess: () => tasks.refetch() }, }); const updateTask = useUpdateTaskTasksTaskIdPatch({ mutation: { onSuccess: () => tasks.refetch() }, }); const deleteTask = useDeleteTaskTasksTaskIdDelete({ mutation: { onSuccess: () => tasks.refetch() }, }); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [assigneeId, setAssigneeId] = useState(""); const [reviewerId, setReviewerId] = useState(""); const [commentTaskId, setCommentTaskId] = useState(null); const [replyToCommentId, setReplyToCommentId] = useState(null); const [commentBody, setCommentBody] = useState(""); const comments = useListTaskCommentsTaskCommentsGet( { task_id: commentTaskId ?? 0 }, { query: { enabled: Boolean(commentTaskId) } }, ); const commentList = comments.data ?? []; const addComment = useCreateTaskCommentTaskCommentsPost({ mutation: { onSuccess: () => { comments.refetch(); setCommentBody(""); setReplyToCommentId(null); }, }, }); const tasksByStatus = (() => { const map = new Map(); for (const s of STATUSES) map.set(s, []); for (const t of taskList) { const status = t.status ?? "backlog"; map.get(status)?.push(t); } return map; })(); const employeeName = (id: number | null | undefined) => employeeList.find((e) => e.id === id)?.name ?? "—"; const projectMembers = memberList; const commentById = new Map(); for (const c of commentList) { if (c.id != null) commentById.set(Number(c.id), c); } return (
{!Number.isFinite(projectId) ? (
Invalid project id in URL.
) : null} {projects.isLoading || employees.isLoading || members.isLoading || tasks.isLoading ? (
Loading…
) : null} {projects.error ?
{(projects.error as Error).message}
: null} {employees.error ?
{(employees.error as Error).message}
: null} {members.error ?
{(members.error as Error).message}
: null} {tasks.error ?
{(tasks.error as Error).message}
: null}

{project?.name ?? `Project #${projectId}`}

Project detail: staffing + tasks.

Create task Project-scoped tasks {createTask.error ?
{(createTask.error as Error).message}
: null} setTitle(e.target.value)} />