TorbatYar/frontend/app/accounting/settings/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

198 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import Link from "next/link";
import { useQuery } from "@tanstack/react-query";
import { ChevronLeft, ExternalLink, Palette, Shield, Users } from "lucide-react";
import { accountingApi } from "@/lib/accounting-api";
import { useTenantId } from "@/hooks/useTenantId";
import { useColorMode } from "@/components/providers/ColorModeProvider";
import {
PageHeader,
LoadingState,
ErrorState,
Card,
CardContent,
CardHeader,
CardTitle,
Badge,
Button,
} from "@/components/ds";
const SETUP_STEPS: { key: keyof Awaited<ReturnType<typeof accountingApi.setup.status>>; label: string }[] = [
{ key: "has_chart", label: "دفتر حساب (Chart)" },
{ key: "has_accounts", label: "حساب‌ها" },
{ key: "has_currency", label: "ارز" },
{ key: "has_base_currency", label: "ارز پایه" },
{ key: "has_fiscal_year", label: "سال مالی" },
{ key: "has_fiscal_period", label: "دوره مالی" },
{ key: "has_cash_box", label: "صندوق نقد" },
{ key: "has_customer", label: "مشتری" },
{ key: "has_voucher", label: "سند حسابداری" },
];
const SUPERAPP_LINKS = [
{
href: "/dashboard/settings",
title: "کاربران و نقش‌ها",
description: "مدیریت دسترسی اعضای workspace از SuperApp",
icon: Users,
},
{
href: "/dashboard/settings",
title: "نقش‌ها و مجوزها",
description: "تخصیص نقش IAM — از تنظیمات workspace",
icon: Shield,
},
{
href: "/dashboard/settings",
title: "برندسازی (White-label)",
description: "لوگو، رنگ و هویت بصری tenant",
icon: Palette,
},
];
export default function AccountingSettingsPage() {
const { tenantId } = useTenantId();
const { mode, toggle } = useColorMode();
const healthQ = useQuery({
queryKey: ["accounting", "health"],
queryFn: () => accountingApi.health(),
});
const setupQ = useQuery({
queryKey: ["accounting", tenantId, "setup-status"],
queryFn: () => accountingApi.setup.status(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || setupQ.isLoading) return <LoadingState />;
if (setupQ.error) {
return <ErrorState message={setupQ.error.message} onRetry={() => setupQ.refetch()} />;
}
const setup = setupQ.data!;
return (
<div>
<PageHeader
title="تنظیمات حسابداری"
description="وضعیت راه‌اندازی، سرویس و پیوندهای مدیریت workspace."
/>
<div className="mb-6 rounded-2xl border border-[var(--border)] bg-[var(--surface)] p-5">
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
<div>
<h2 className="font-semibold text-secondary">پیشرفت راهاندازی</h2>
<p className="mt-1 text-sm text-[var(--muted)]">
{setup.completed_steps} از {setup.total_steps} مرحله تکمیل شده
</p>
</div>
<Badge tone={setup.percent >= 100 ? "success" : setup.percent >= 50 ? "primary" : "warning"}>
{setup.percent}%
</Badge>
</div>
<div className="mb-4 h-2 overflow-hidden rounded-full bg-[var(--surface-muted)]">
<div
className="h-full rounded-full bg-primary transition-all"
style={{ width: `${setup.percent}%` }}
/>
</div>
<ul className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
{SETUP_STEPS.map(({ key, label }) => (
<li key={key} className="flex items-center justify-between rounded-xl border border-[var(--border)] px-3 py-2 text-sm">
<span className="text-secondary">{label}</span>
<Badge tone={setup[key] ? "success" : "default"}>{setup[key] ? "✓" : "—"}</Badge>
</li>
))}
</ul>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>سرویس حسابداری</CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-sm">
<div className="flex items-center justify-between">
<span className="text-[var(--muted)]">وضعیت</span>
<Badge tone={healthQ.data?.status === "ok" ? "success" : "warning"}>
{healthQ.data?.status ?? "…"}
</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-[var(--muted)]">نسخه</span>
<span className="font-medium">{healthQ.data?.version ?? "—"}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[var(--muted)]">Tenant</span>
<span className="font-mono text-xs" dir="ltr">
{tenantId}
</span>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>ظاهر ماژول</CardTitle>
</CardHeader>
<CardContent className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">تم رابط</p>
<p className="text-xs text-[var(--muted)]">روشن / تاریک RTL حفظ میشود</p>
</div>
<Button type="button" variant="outline" onClick={toggle}>
{mode === "dark" ? "تم روشن" : "تم تاریک"}
</Button>
</CardContent>
</Card>
</div>
<h2 className="mb-3 mt-8 text-sm font-semibold text-secondary">مدیریت workspace (SuperApp)</h2>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{SUPERAPP_LINKS.map((link) => {
const Icon = link.icon;
return (
<Link
key={link.title}
href={link.href}
className="group flex flex-col rounded-2xl border border-[var(--border)] bg-[var(--surface)] p-4 transition-colors hover:border-primary/40 hover:bg-[var(--surface-muted)]"
>
<div className="mb-3 flex items-center justify-between">
<div className="rounded-xl bg-[var(--surface-muted)] p-2">
<Icon className="h-4 w-4 text-primary" />
</div>
<ExternalLink className="h-3.5 w-3.5 text-[var(--muted)] opacity-0 transition-opacity group-hover:opacity-100" />
</div>
<p className="font-medium text-secondary">{link.title}</p>
<p className="mt-1 text-xs text-[var(--muted)]">{link.description}</p>
<span className="mt-3 inline-flex items-center gap-1 text-xs text-primary">
<ChevronLeft className="h-3 w-3" />
رفتن به تنظیمات
</span>
</Link>
);
})}
</div>
<div className="mt-6 grid gap-2 sm:grid-cols-2">
{[
["/accounting/chart-of-accounts", "دفتر حساب‌ها"],
["/accounting/fiscal", "سال و دوره مالی"],
["/accounting/currencies", "ارزها"],
["/accounting/treasury", "خزانه"],
].map(([href, label]) => (
<Link
key={href}
href={href}
className="rounded-xl border border-[var(--border)] px-4 py-3 text-sm hover:bg-[var(--surface-muted)]"
>
{label}
</Link>
))}
</div>
</div>
);
}