112 lines
5.5 KiB
TypeScript
112 lines
5.5 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||
import Link from "next/link";
|
||
import { toast } from "sonner";
|
||
import { Button, Card, CardContent, PageHeader, Textarea } from "@/components/ds";
|
||
import { useCurrentHub } from "@/modules/workspace/hooks/useCurrentHub";
|
||
import { workspaceApi } from "@/modules/workspace/services/workspace-api";
|
||
import { APPROVAL_TYPE, displayPerson, formatWhen, labelOf } from "@/modules/workspace/labels";
|
||
import { StatusChip } from "@/modules/workspace/components/StatusChip";
|
||
import { WorkspaceEmptyState, WorkspacePageError, WorkspacePageLoader } from "@/modules/workspace/pages/shared";
|
||
|
||
export const WorkspaceApprovals = function WorkspaceApprovalsPage() {
|
||
const queryClient = useQueryClient();
|
||
const { tenantId, hubId, loading } = useCurrentHub();
|
||
const [comments, setComments] = useState<Record<string, string>>({});
|
||
const approvals = useQuery({
|
||
queryKey: ["workspace", tenantId, "approvals", hubId],
|
||
queryFn: () => workspaceApi.approvals.list(tenantId!, { work_hub_id: hubId || undefined }),
|
||
enabled: !!tenantId && !!hubId,
|
||
});
|
||
const respond = useMutation({
|
||
mutationFn: ({ id, status, comment }: { id: string; status: string; comment?: string }) =>
|
||
workspaceApi.approvals.respond(tenantId!, id, { status, comment }),
|
||
onSuccess: () => {
|
||
toast.success("پاسخ تأیید ذخیره شد");
|
||
void queryClient.invalidateQueries({ queryKey: ["workspace", tenantId, "approvals"] });
|
||
},
|
||
onError: (err) => toast.error(err instanceof Error ? err.message : "ثبت پاسخ ناموفق بود"),
|
||
});
|
||
|
||
if (!tenantId || loading || approvals.isLoading) return <WorkspacePageLoader />;
|
||
if (!hubId) return <WorkspaceEmptyState title="مرکز کار لازم است" description="ابتدا مرکز کار را انتخاب کنید." />;
|
||
if (approvals.error) return <WorkspacePageError error={approvals.error} onRetry={() => approvals.refetch()} />;
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<PageHeader title="تأییدیهها" description="تأیید داخلی، مشتری و نهایی با تاریخچه واقعی." />
|
||
{(approvals.data ?? []).length === 0 ? (
|
||
<WorkspaceEmptyState title="تأییدیهای نیست" description="از صفحه محتوا درخواست تأیید ثبت کنید." />
|
||
) : (
|
||
<div className="space-y-3">
|
||
{(approvals.data ?? []).map((row) => (
|
||
<Card key={String(row.id)}>
|
||
<CardContent className="flex flex-wrap items-center justify-between gap-3 p-5">
|
||
<div>
|
||
<p className="font-medium text-secondary">{labelOf(APPROVAL_TYPE, String(row.approval_type || ""))}</p>
|
||
<p className="text-sm text-[var(--muted)]">
|
||
تأییدکننده: {displayPerson(String(row.approver_user_id || ""))} · {formatWhen(String(row.requested_at || row.created_at || ""))}
|
||
</p>
|
||
{row.comment ? <p className="mt-1 text-sm">{String(row.comment)}</p> : null}
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<StatusChip value={String(row.status || "")} kind="approval" />
|
||
<Link href={`/workspace/content/${row.content_id}`} className="text-sm text-primary">
|
||
مشاهده محتوا
|
||
</Link>
|
||
{row.status === "pending" ? (
|
||
<div className="flex w-full flex-col gap-2 sm:w-auto">
|
||
<Textarea
|
||
value={comments[String(row.id)] || ""}
|
||
onChange={(event) => setComments((prev) => ({ ...prev, [String(row.id)]: event.target.value }))}
|
||
placeholder="یادداشت بررسی"
|
||
rows={2}
|
||
/>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Button
|
||
size="sm"
|
||
onClick={() =>
|
||
respond.mutate({ id: String(row.id), status: "approved", comment: comments[String(row.id)] })
|
||
}
|
||
>
|
||
تأیید
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
onClick={() =>
|
||
respond.mutate({
|
||
id: String(row.id),
|
||
status: "changes_requested",
|
||
comment: comments[String(row.id)] || "نیاز به اصلاح",
|
||
})
|
||
}
|
||
>
|
||
اصلاح
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
variant="danger"
|
||
onClick={() =>
|
||
respond.mutate({ id: String(row.id), status: "rejected", comment: comments[String(row.id)] })
|
||
}
|
||
>
|
||
رد
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default WorkspaceApprovals;
|