Vitron-Front/app/routes/admin.ledger._index.tsx
Arda Samadi cedeef4746 feat(admin): ledger UI — overview/reconciliation, payouts, transactions, per-seller (slice 2a)
Ledger section with sub-tabs: overview (money held/owed, payouts by status, and
a reconciliation warning card), payouts + transactions lists, per-seller balances
list, and a per-seller detail showing recorded vs derived balance + discrepancy,
bank accounts and payout history. Enabled the Ledger nav item.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:28:11 +03:30

113 lines
4.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.

import {
Loader2,
Wallet,
Landmark,
ArrowDownCircle,
ArrowUpCircle,
AlertTriangle,
Clock,
CheckCircle2,
} from "lucide-react";
import { useLedgerOverview } from "~/requestHandler/use-admin-hooks";
import { KpiCard } from "~/components/admin/KpiCard";
import { faToman, faNum } from "~/components/admin/DataTable";
export default function LedgerOverview() {
const { data, isLoading, isError } = useLedgerOverview();
if (isLoading)
return (
<div className="flex min-h-[40vh] items-center justify-center">
<Loader2 className="h-7 w-7 animate-spin text-GRAY" />
</div>
);
if (isError || !data)
return <p className="text-RED text-[14px]">خطا در بارگذاری دفتر مالی</p>;
const { payouts, reconciliation } = data;
const hasDiscrepancy = reconciliation.completedPayoutsNotDebited > 0;
return (
<div>
{/* Money held / owed */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<KpiCard label="موجودی کیف پول کاربران" value={faToman(data.walletHeld)} icon={Wallet} />
<KpiCard label="مانده قابل پرداخت فروشندگان" value={faToman(data.sellerPayable)} icon={Landmark} />
<KpiCard label="کل واریزها (شارژ کیف پول)" value={faToman(data.depositsTotal)} icon={ArrowDownCircle} accent="green" />
<KpiCard label="کل خریدها" value={faToman(data.purchasesTotal)} icon={ArrowUpCircle} />
</div>
{/* Payouts by status */}
<h2 className="text-[15px] font-extrabold mt-7 mb-3">برداشتها</h2>
<div className="grid grid-cols-3 gap-3">
<KpiCard
label="در انتظار"
value={faToman(payouts.pending.amount)}
sub={`${faNum(payouts.pending.count)} درخواست`}
icon={Clock}
accent={payouts.pending.count > 0 ? "red" : "default"}
/>
<KpiCard
label="پرداخت‌شده"
value={faToman(payouts.completed.amount)}
sub={`${faNum(payouts.completed.count)} مورد`}
icon={CheckCircle2}
accent="green"
/>
<KpiCard
label="ردشده"
value={faToman(payouts.rejected.amount)}
sub={`${faNum(payouts.rejected.count)} مورد`}
/>
</div>
{/* Reconciliation warning */}
{hasDiscrepancy && (
<div className="mt-7 rounded-2xl border border-RED/30 bg-RED/5 p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="h-5 w-5 text-RED shrink-0 mt-0.5" />
<div className="flex-1">
<h3 className="font-extrabold text-[15px] text-RED">
مغایرت حسابداری شناسایی شد
</h3>
<p className="text-[13px] text-BLACK2 mt-1 leading-6">
مبلغ{" "}
<b className="tabular-nums">
{faToman(reconciliation.completedPayoutsNotDebited)}
</b>{" "}
بهعنوان برداشت «پرداختشده» ثبت شده اما از مانده فروشندگان کسر
نشده است. به همین دلیل «مانده قابل پرداخت» فعلی احتمالاً بیش از
مقدار واقعی است.
</p>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-3 mt-3">
<div className="rounded-xl bg-WHITE border border-WHITE3 p-3">
<p className="text-[12px] text-GRAY">مانده ثبتشده</p>
<p className="font-bold tabular-nums mt-1">
{faToman(reconciliation.sellerPayableRecorded)}
</p>
</div>
<div className="rounded-xl bg-WHITE border border-WHITE3 p-3">
<p className="text-[12px] text-GRAY">برآورد مانده واقعی</p>
<p className="font-bold tabular-nums mt-1 text-GREEN">
{faToman(reconciliation.sellerPayableEstimatedTrue)}
</p>
</div>
<div className="rounded-xl bg-WHITE border border-WHITE3 p-3">
<p className="text-[12px] text-GRAY">اختلاف</p>
<p className="font-bold tabular-nums mt-1 text-RED">
{faToman(reconciliation.completedPayoutsNotDebited)}
</p>
</div>
</div>
<p className="text-[12px] text-GRAY mt-3">
این مورد در فاز بعدی (دفتر روزنامهای و اصلاح جریان پول) رفع خواهد
شد.
</p>
</div>
</div>
</div>
)}
</div>
);
}