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
+24 -9
View File
@@ -1,9 +1,11 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Suspense } from "react";
import { PageHeader } from "@/components/page-header";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { SummaryCardsSkeleton } from "@/features/dashboard/components/summary-cards-skeleton";
import { ContactsDueBanner } from "@/features/contacts/components/due-banner";
import { getNetWorth } from "@/features/net-worth/service";
import { OverdueBanner } from "@/features/recurring/components/overdue-banner";
import { tr } from "@/i18n/tr";
import { requireActiveHousehold } from "@/lib/dal";
@@ -16,22 +18,27 @@ type SummaryCard = {
key: keyof typeof tr.dashboard.cards;
tone?: string;
amountMinor: number | null;
href?: string;
};
/** FAZ 14: gerçek özet verisi (gelir, gider, bakiye, kart borcu, net varlık). */
async function getSummary(): Promise<SummaryCard[]> {
/**
* Özet kartları. Bakiye, kart borcu ve net varlık gerçek (Faz 9); bu ayın gelir/gider/kalan
* kartları Faz 14'te dolar.
*/
async function getSummary(userId: string, householdId: string): Promise<SummaryCard[]> {
const nw = await getNetWorth(userId, householdId);
return [
{ key: "monthIncome", tone: "text-income", amountMinor: null },
{ key: "monthExpense", tone: "text-expense", amountMinor: null },
{ key: "monthRemaining", tone: "text-budget", amountMinor: null },
{ key: "totalBalance", amountMinor: null },
{ key: "cardDebt", tone: "text-expense", amountMinor: null },
{ key: "netWorth", amountMinor: null },
{ key: "totalBalance", amountMinor: nw?.accountsMinor ?? null, href: "/hesaplar" },
{ key: "cardDebt", tone: "text-expense", amountMinor: nw?.cardDebtMinor ?? null },
{ key: "netWorth", amountMinor: nw?.totalMinor ?? null, href: "/birikim" },
];
}
async function SummaryCards() {
const cards = await getSummary();
async function SummaryCards({ userId, householdId }: { userId: string; householdId: string }) {
const cards = await getSummary(userId, householdId);
return (
<div className="grid grid-cols-2 gap-3 lg:grid-cols-3 xl:grid-cols-6">
{cards.map((c) => (
@@ -39,7 +46,15 @@ async function SummaryCards() {
<CardHeader>
<CardDescription>{tr.dashboard.cards[c.key]}</CardDescription>
<CardTitle className={cn("text-lg tabular-nums", c.tone)}>
{c.amountMinor === null ? "—" : formatMoney(c.amountMinor)}
{c.amountMinor === null ? (
"—"
) : c.href ? (
<Link href={c.href} className="hover:underline">
{formatMoney(c.amountMinor)}
</Link>
) : (
formatMoney(c.amountMinor)
)}
</CardTitle>
</CardHeader>
{c.amountMinor === null ? (
@@ -68,7 +83,7 @@ export default async function DashboardPage() {
<ContactsDueBanner userId={user.id} householdId={household.id} />
</Suspense>
<Suspense fallback={<SummaryCardsSkeleton />}>
<SummaryCards />
<SummaryCards userId={user.id} householdId={household.id} />
</Suspense>
</>
);