TorbatYar/frontend/components/accounting/CompletionScoreboard.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

71 lines
2.7 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 { ACCOUNTING_MODULE_SCORES, overallCompletionPercent, type ScoreState } from "@/lib/accounting-scoreboard";
import { Badge, Card, CardContent, CardHeader, CardTitle } from "@/components/ds";
import { cn } from "@/lib/utils";
const tone: Record<ScoreState, "success" | "warning" | "danger" | "default"> = {
complete: "success",
partial: "warning",
missing: "danger",
blocked: "default",
};
const labelFa: Record<ScoreState, string> = {
complete: "کامل",
partial: "جزئی",
missing: "ناقص",
blocked: "مسدود",
};
export function CompletionScoreboard({ compact = false }: { compact?: boolean }) {
const overall = overallCompletionPercent();
const rows = compact ? ACCOUNTING_MODULE_SCORES.filter((s) => s.state !== "blocked").slice(0, 8) : ACCOUNTING_MODULE_SCORES;
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between gap-3">
<div>
<CardTitle>میزان تکمیل ماژول حسابداری</CardTitle>
<p className="mt-1 text-xs text-[var(--muted)]">Scoreboard دائمی بدون mock</p>
</div>
<Badge tone={overall >= 90 ? "success" : "primary"}>{overall}٪</Badge>
</CardHeader>
<CardContent className="space-y-2">
{rows.map((m) => (
<Link
key={m.id}
href={m.href}
className="flex items-center gap-3 rounded-xl px-2 py-2 hover:bg-[var(--surface-muted)]"
>
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-2">
<span className="truncate text-sm font-medium text-secondary">{m.label}</span>
<Badge tone={tone[m.state]}>{labelFa[m.state]}</Badge>
</div>
<div className="mt-1.5 h-1.5 overflow-hidden rounded-full bg-[var(--surface-muted)]">
<div
className={cn(
"h-full rounded-full",
m.state === "blocked" ? "bg-[var(--muted)]" : "bg-primary"
)}
style={{ width: `${m.percent}%` }}
/>
</div>
</div>
<span className="w-10 text-left text-xs tabular-nums text-[var(--muted)]" dir="ltr">
{m.percent}%
</span>
</Link>
))}
{compact ? (
<Link href="/accounting/setup" className="block pt-2 text-xs font-medium text-primary hover:underline">
راهاندازی و مشاهده همه ماژولها
</Link>
) : null}
</CardContent>
</Card>
);
}