"use client"; import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { Plus, Pencil, Archive } from "lucide-react"; import { accountingApi, type Project } from "@/lib/accounting-api"; import { useTenantId } from "@/hooks/useTenantId"; import { PageHeader, Button, Dialog, ConfirmDialog, Input, Label, FieldError, DataTable, EmptyState, LoadingState, ErrorState, Badge, Drawer, DatePicker, JalaliDateText, } from "@/components/ds"; const createSchema = z.object({ code: z.string().min(1, "کد الزامی است"), name: z.string().min(1, "نام الزامی است"), start_date: z.string().optional(), end_date: z.string().optional(), }); const editSchema = z.object({ name: z.string().min(1, "نام الزامی است"), start_date: z.string().optional(), end_date: z.string().optional(), }); export default function ProjectsPage() { const { tenantId } = useTenantId(); const qc = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const [selected, setSelected] = useState(null); const [archiveId, setArchiveId] = useState(null); const createForm = useForm>({ resolver: zodResolver(createSchema), defaultValues: { code: "", name: "", start_date: "", end_date: "" }, }); const editForm = useForm>({ resolver: zodResolver(editSchema), }); const listQ = useQuery({ queryKey: ["accounting", tenantId, "projects"], queryFn: () => accountingApi.projects.list(tenantId!), enabled: !!tenantId, }); const invalidate = async () => { await qc.invalidateQueries({ queryKey: ["accounting", tenantId, "projects"] }); }; const createM = useMutation({ mutationFn: (d: z.infer) => accountingApi.projects.create(tenantId!, { code: d.code, name: d.name, start_date: d.start_date || undefined, end_date: d.end_date || undefined, }), onSuccess: async () => { toast.success("پروژه ایجاد شد"); setCreateOpen(false); createForm.reset(); await invalidate(); }, onError: (e: Error) => toast.error(e.message), }); const updateM = useMutation({ mutationFn: (d: z.infer) => accountingApi.projects.update(tenantId!, selected!.id, { name: d.name, start_date: d.start_date || null, end_date: d.end_date || null, }), onSuccess: async () => { toast.success("پروژه به‌روز شد"); setSelected(null); await invalidate(); }, onError: (e: Error) => toast.error(e.message), }); const archiveM = useMutation({ mutationFn: (id: string) => accountingApi.projects.archive(tenantId!, id), onSuccess: async () => { toast.success("پروژه آرشیو شد"); setArchiveId(null); setSelected(null); await invalidate(); }, onError: (e: Error) => toast.error(e.message), }); if (!tenantId || listQ.isLoading) return ; if (listQ.error) return listQ.refetch()} />; return (
setCreateOpen(true)}> پروژه جدید } /> r.start_date ? : "—", }, { key: "end_date", header: "پایان", render: (r) => (r.end_date ? : "—"), }, { key: "is_active", header: "وضعیت", render: (r) => ( {r.is_active ? "فعال" : "آرشیو"} ), }, { key: "actions", header: "عملیات", render: (r) => { const id = String(r.id); const active = Boolean(r.is_active); return active ? (
) : ( "—" ); }, }, ]} rows={(listQ.data ?? []) as unknown as Record[]} empty={ setCreateOpen(true)}>ایجاد} /> } /> setCreateOpen(false)} title="پروژه جدید">
createM.mutate(d))}>
{createForm.formState.errors.code?.message}
{createForm.formState.errors.name?.message}
( )} />
( )} />
setSelected(null)} title="ویرایش پروژه" description={selected ? `${selected.code}` : undefined} >
updateM.mutate(d))}>
{editForm.formState.errors.name?.message}
( )} />
( )} />
setArchiveId(null)} onConfirm={() => archiveId && archiveM.mutate(archiveId)} title="آرشیو پروژه؟" description="پروژه آرشیو شده در اسناد جدید قابل انتخاب نیست." confirmLabel="آرشیو" danger loading={archiveM.isPending} />
); }