TorbatYar/frontend/app/accounting/audit/page.tsx
Mortezakoohjani 12c8615615 Ship enterprise Accounting FE/API with CRUD parity and production wiring.
Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-24 15:26:43 +03:30

137 lines
4.3 KiB
TypeScript

"use client";
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { accountingApi } from "@/lib/accounting-api";
import { useTenantId } from "@/hooks/useTenantId";
import {
PageHeader,
DataTable,
EmptyState,
LoadingState,
ErrorState,
JalaliDateText,
Tabs,
Badge,
} from "@/components/ds";
type TabId = "compliance" | "posting";
export default function AuditPage() {
const { tenantId } = useTenantId();
const [tab, setTab] = useState<TabId>("compliance");
const complianceQ = useQuery({
queryKey: ["accounting", tenantId, "compliance-audit"],
queryFn: () => accountingApi.compliance.listAudit(tenantId!),
enabled: !!tenantId && tab === "compliance",
});
const postingQ = useQuery({
queryKey: ["accounting", tenantId, "posting-audit-logs"],
queryFn: () => accountingApi.vouchers.auditLogs(tenantId!),
enabled: !!tenantId && tab === "posting",
});
const activeQ = tab === "compliance" ? complianceQ : postingQ;
if (!tenantId) return <LoadingState />;
if (activeQ.isLoading) return <LoadingState />;
if (activeQ.error) {
return <ErrorState message={activeQ.error.message} onRetry={() => activeQ.refetch()} />;
}
return (
<div>
<PageHeader
title="حسابرسی و انطباق"
description="رکوردهای Compliance و لاگ‌های ثبت اسناد."
/>
<Tabs
className="mb-6"
value={tab}
onChange={(id) => setTab(id as TabId)}
items={[
{ id: "compliance", label: "رکوردهای انطباق" },
{ id: "posting", label: "لاگ ثبت اسناد" },
]}
/>
{tab === "compliance" && (
<DataTable
columns={[
{
key: "created_at",
header: "زمان",
render: (r) => <JalaliDateText value={String(r.created_at ?? "")} withTime />,
},
{ key: "action", header: "عملیات" },
{ key: "resource_type", header: "منبع" },
{
key: "actor_user_id",
header: "عامل",
render: (r) => (
<span className="font-mono text-xs" dir="ltr">
{String(r.actor_user_id ?? "—")}
</span>
),
},
]}
rows={(complianceQ.data ?? []) as unknown as Record<string, unknown>[]}
empty={
<EmptyState
title="رکورد حسابرسی نیست"
description="پس از عملیات مالی، رکوردها اینجا ظاهر می‌شوند."
/>
}
/>
)}
{tab === "posting" && (
<DataTable
columns={[
{
key: "created_at",
header: "زمان",
render: (r) => <JalaliDateText value={String(r.created_at ?? "")} withTime />,
},
{
key: "operation",
header: "عملیات",
render: (r) => <Badge>{String(r.operation)}</Badge>,
},
{ key: "voucher_number", header: "شماره سند", render: (r) => String(r.voucher_number ?? "—") },
{ key: "resource_type", header: "نوع منبع" },
{
key: "resource_id",
header: "شناسه",
render: (r) => (
<span className="font-mono text-xs" dir="ltr">
{String(r.resource_id ?? "—")}
</span>
),
},
{
key: "actor_user_id",
header: "عامل",
render: (r) => (
<span className="font-mono text-xs" dir="ltr">
{String(r.actor_user_id ?? "—")}
</span>
),
},
]}
rows={(postingQ.data ?? []) as unknown as Record<string, unknown>[]}
empty={
<EmptyState
title="لاگی ثبت نشده"
description="پس از ایجاد، ویرایش یا ثبت اسناد، لاگ‌ها اینجا نمایش داده می‌شوند."
/>
}
/>
)}
</div>
);
}