From bfda37d4e3cbccad1df61b104d88b3420ecbff1f Mon Sep 17 00:00:00 2001 From: MOHAN Date: Fri, 17 Jul 2026 23:30:41 +0530 Subject: [PATCH] Add accountant workflow UI --- app/accountant/page.tsx | 160 ++++++++++++++++++ .../[id]/accountant-tasks/[taskId]/route.ts | 6 + .../households/[id]/accountant-tasks/route.ts | 10 ++ components/app-shell.tsx | 1 + 4 files changed, 177 insertions(+) create mode 100644 app/accountant/page.tsx create mode 100644 app/api/households/[id]/accountant-tasks/[taskId]/route.ts create mode 100644 app/api/households/[id]/accountant-tasks/route.ts diff --git a/app/accountant/page.tsx b/app/accountant/page.tsx new file mode 100644 index 0000000..ee4723d --- /dev/null +++ b/app/accountant/page.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { FormEvent, useEffect, useMemo, useState } from "react"; +import { AppShell } from "@/components/app-shell"; +import { apiFetch } from "@/lib/api"; + +type Household = { id: string; name: string }; +type Person = { id: string; email: string; fullName?: string | null }; +type AccountantTask = { + id: string; + title: string; + description?: string | null; + taskType: string; + status: string; + priority: string; + dueDate?: string | null; + completedAt?: string | null; + assignedTo?: Person | null; + createdBy?: Person | null; +}; + +export default function AccountantPage() { + const [households, setHouseholds] = useState([]); + const [householdId, setHouseholdId] = useState(""); + const [tasks, setTasks] = useState([]); + const [draft, setDraft] = useState({ title: "", description: "", taskType: "review", priority: "medium", dueDate: "" }); + const [status, setStatus] = useState(""); + const selectedHousehold = useMemo(() => households.find((item) => item.id === householdId), [households, householdId]); + + useEffect(() => { + void loadHouseholds(); + }, []); + + useEffect(() => { + if (householdId) void loadTasks(householdId); + }, [householdId]); + + const loadHouseholds = async () => { + const res = await apiFetch("/api/households"); + if (res.error) { + setStatus(res.error.message); + return; + } + const list = res.data ?? []; + setHouseholds(list); + setHouseholdId((current) => current || list[0]?.id || ""); + }; + + const loadTasks = async (id = householdId) => { + const res = await apiFetch(`/api/households/${id}/accountant-tasks`); + if (res.error) { + setStatus(res.error.message); + setTasks([]); + return; + } + setStatus(""); + setTasks(res.data ?? []); + }; + + const createTask = async (event: FormEvent) => { + event.preventDefault(); + if (!householdId || !draft.title.trim()) return; + const res = await apiFetch(`/api/households/${householdId}/accountant-tasks`, { + method: "POST", + body: JSON.stringify({ + title: draft.title, + description: draft.description || undefined, + taskType: draft.taskType, + priority: draft.priority, + dueDate: draft.dueDate || undefined, + }), + }); + if (res.error) { + setStatus(res.error.message); + return; + } + setDraft({ title: "", description: "", taskType: "review", priority: "medium", dueDate: "" }); + await loadTasks(); + }; + + const updateTask = async (task: AccountantTask, nextStatus: string) => { + if (!householdId) return; + const res = await apiFetch(`/api/households/${householdId}/accountant-tasks/${task.id}`, { + method: "PATCH", + body: JSON.stringify({ status: nextStatus }), + }); + if (res.error) { + setStatus(res.error.message); + return; + } + await loadTasks(); + }; + + return ( + +
+ {status ?
{status}
: null} + +
+
+
+

Household

+

{selectedHousehold?.name ?? "Choose a household to manage advisor work."}

+
+ +
+
+ +
+

New Task

+
+ setDraft({ ...draft, title: event.target.value })} placeholder="Task title" className="rounded-md border border-slate-300 px-3 py-2 text-sm" /> + setDraft({ ...draft, dueDate: event.target.value })} className="rounded-md border border-slate-300 px-3 py-2 text-sm" /> + + +
+