feat(ui): değer grafiği, net varlık kartı, dashboard kartları ve ödeme diyaloğunda varlık bağı

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-06 00:19:05 +03:00
parent 9a60085d14
commit b022c1c3b0
10 changed files with 825 additions and 36 deletions
@@ -0,0 +1,62 @@
import { Card, CardContent } from "@/components/ui/card";
import { tr } from "@/i18n/tr";
import { cn } from "@/lib/utils";
import { formatMoney } from "@/shared/format";
import type { NetWorth } from "../service";
const t = tr.assets.netWorth;
/** Net varlık dökümü: artılar ve eksiler, altta toplam. */
export function NetWorthCard({ data }: { data: NetWorth }) {
const rows: { label: string; minor: number; sign: 1 | -1 }[] = [
{ label: t.accounts, minor: data.accountsMinor, sign: 1 },
{ label: t.assets, minor: data.assetsMinor, sign: 1 },
{ label: t.contactReceivable, minor: data.contactReceivableMinor, sign: 1 },
{ label: t.inKindReceivable, minor: data.inKindReceivableMinor, sign: 1 },
{ label: t.cardDebt, minor: data.cardDebtMinor, sign: -1 },
{ label: t.contactPayable, minor: data.contactPayableMinor, sign: -1 },
{ label: t.inKindPayable, minor: data.inKindPayableMinor, sign: -1 },
];
const plus = rows.filter((r) => r.sign === 1);
const minus = rows.filter((r) => r.sign === -1);
const list = (items: typeof rows) => (
<dl className="flex flex-col text-sm">
{items.map((r) => (
<div key={r.label} className="flex items-center justify-between gap-2 py-1">
<dt className="text-muted-foreground">{r.label}</dt>
<dd className="tabular-nums">
{r.sign < 0 && r.minor ? "−" : ""}
{formatMoney(r.minor)}
</dd>
</div>
))}
</dl>
);
return (
<Card size="sm">
<CardContent className="flex flex-col gap-3">
<div>
<p
className={cn(
"text-3xl font-semibold tracking-tight tabular-nums",
data.totalMinor < 0 && "text-expense",
)}
>
{formatMoney(data.totalMinor)}
</p>
<p className="text-sm text-muted-foreground">{t.description}</p>
</div>
<div className="grid gap-x-8 gap-y-1 border-t pt-2 sm:grid-cols-2">
{list(plus)}
{list(minus)}
</div>
{data.missingPrice ? (
<p className="text-xs text-muted-foreground">{t.missingPrice(data.missingPrice)}</p>
) : null}
{data.includesHidden ? (
<p className="text-xs text-muted-foreground">{t.includesHidden}</p>
) : null}
</CardContent>
</Card>
);
}