diff --git a/src/app/(app)/hesaplar/[id]/page.tsx b/src/app/(app)/hesaplar/[id]/page.tsx
index 8bd5cfa..83a25e7 100644
--- a/src/app/(app)/hesaplar/[id]/page.tsx
+++ b/src/app/(app)/hesaplar/[id]/page.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { hasHouseholdRole } from "@/features/households/roles";
import { StatementView } from "@/features/transactions/components/statement-view";
+import { getCardSummary, getOverdraftSummary } from "@/features/transactions/card";
import { currentMonth, getAccountStatement } from "@/features/transactions/statement";
import { tr } from "@/i18n/tr";
import { requireActiveHousehold } from "@/lib/dal";
@@ -21,10 +22,20 @@ export default async function AccountStatementPage({
const month = requested > now ? now : requested;
const statement = await getAccountStatement(user.id, household.id, id, month);
if (!statement) notFound();
+ const [card, overdraft] = await Promise.all([
+ statement.account.type === "credit_card"
+ ? getCardSummary(user.id, household.id, id)
+ : Promise.resolve(null),
+ statement.account.type === "bank"
+ ? getOverdraftSummary(user.id, household.id, id)
+ : Promise.resolve(null),
+ ]);
return (
);
diff --git a/src/components/layout/nav-config.ts b/src/components/layout/nav-config.ts
index c333d29..239e67f 100644
--- a/src/components/layout/nav-config.ts
+++ b/src/components/layout/nav-config.ts
@@ -27,7 +27,7 @@ export type NavItem = {
/** Şu an tamamlanmış en yüksek faz — sayfalar eklendikçe artar. */
/** Alt fazlar ondalıkla: 6.1 = Faz 6a. */
-export const CURRENT_PHASE = 7.1;
+export const CURRENT_PHASE = 7.2;
export const isAvailable = (item: NavItem) => item.phase <= CURRENT_PHASE;
diff --git a/src/features/transactions/components/card-summary.tsx b/src/features/transactions/components/card-summary.tsx
new file mode 100644
index 0000000..b615a93
--- /dev/null
+++ b/src/features/transactions/components/card-summary.tsx
@@ -0,0 +1,314 @@
+"use client";
+
+import { ChevronDown, CreditCard, Landmark, Percent, Wallet } from "lucide-react";
+import { useState } from "react";
+import { useQuickAdd } from "@/components/layout/quick-add";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
+import { tr } from "@/i18n/tr";
+import { cn } from "@/lib/utils";
+import { formatDate, formatMoney } from "@/shared/format";
+import { formatMoneyInput } from "@/shared/money";
+import type { CardSummary, OverdraftSummary } from "../card";
+import { Amount, rowTitle } from "./transaction-parts";
+
+const c = tr.card;
+const o = tr.overdraft;
+const d = (ymd: string) => formatDate(`${ymd}T12:00:00+03:00`);
+const pct = (bp: number) => formatMoneyInput(bp).replace(/,00$/, "");
+
+function Stat({
+ label,
+ value,
+ tone,
+ sub,
+}: {
+ label: string;
+ value: string;
+ tone?: string;
+ sub?: React.ReactNode;
+}) {
+ return (
+
+
{label}
+
{value}
+ {sub ?
{sub}
: null}
+
+ );
+}
+
+/** Limit kullanım çubuğu: borç + gelecek taksitler / limit. */
+function UsageBar({ used, pending, limit }: { used: number; pending: number; limit: number }) {
+ if (limit <= 0) return null;
+ const u = Math.min(100, (used / limit) * 100);
+ const p = Math.min(100 - u, (pending / limit) * 100);
+ return (
+
+ );
+}
+
+export function CardSummaryPanel({
+ summary: s,
+ accountId,
+ canWrite,
+}: {
+ summary: CardSummary;
+ accountId: string;
+ canWrite: boolean;
+}) {
+ const { openTransactionForm } = useQuickAdd();
+ const [historyOpen, setHistoryOpen] = useState(false);
+ const st = s.statement;
+
+ function pay() {
+ openTransactionForm({
+ kind: "create",
+ type: "transfer",
+ prefill: {
+ accountId: s.paymentAccount?.id,
+ toAccountId: accountId,
+ amountMinor: st.remainingMinor || undefined,
+ description: c.payDescription,
+ },
+ });
+ }
+
+ return (
+
+
+
+
+ {c.title}
+
+
+
+
+
+
+
+
+
+ {s.pendingInstallmentsMinor ? (
+
+ {c.pendingInstallments(formatMoney(s.pendingInstallmentsMinor))}
+
+ ) : null}
+
+
+
+
+
{c.statementPeriod}
+
+ {c.periodRange(d(st.period.start), d(st.period.end))}
+
+
+
+
+
+
+
+
+
+
+ {c.dueDate}: {d(st.dueDate)}{" "}
+
+ {st.remainingMinor ? `· ${c.daysLeft(s.daysToDue)}` : `· ${c.paidInFull}`}
+
+
+ {canWrite ? (
+
+ ) : null}
+
+
+
+
+
+
{c.currentPeriod}
+
+ {c.periodRange(d(s.current.start), d(s.current.end))}
+
+
+
+
+
+
+ {c.upcomingInstallments}
+ {s.upcomingInstallments.length ? (
+
+ {s.upcomingInstallments.map((x) => (
+ -
+
+ {c.installmentPeriod(d(x.periodEnd), d(x.dueDate), x.count)}
+
+ {formatMoney(x.totalMinor)}
+
+ ))}
+
+ ) : (
+ {c.noInstallments}
+ )}
+
+
+ {s.planned.length ? (
+
+ {c.planned}
+
+ {s.planned.map((p) => (
+ -
+
+ {d(p.date)} · {rowTitle(p)}
+
+
+
+ ))}
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+ | {c.historyHeaders.period} |
+ {c.historyHeaders.debt} |
+ {c.historyHeaders.minimum} |
+ {c.historyHeaders.paid} |
+ {c.historyHeaders.due} |
+
+
+
+ {s.history.map((h) => (
+
+ |
+ {c.periodRange(d(h.period.start), d(h.period.end))}
+ |
+ {formatMoney(h.debtMinor)} |
+ {formatMoney(h.minimumMinor)} |
+ {formatMoney(h.paidMinor)} |
+ {d(h.dueDate)} |
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+export function OverdraftPanel({
+ summary: s,
+ accountId,
+ canWrite,
+}: {
+ summary: OverdraftSummary;
+ accountId: string;
+ canWrite: boolean;
+}) {
+ const { openTransactionForm } = useQuickAdd();
+ const range = c.periodRange(d(s.statement.start), d(s.statement.end));
+
+ function bookInterest() {
+ openTransactionForm({
+ kind: "create",
+ type: "expense",
+ prefill: {
+ accountId,
+ amountMinor: s.statement.interestMinor || undefined,
+ categoryId: s.interestCategoryId ?? undefined,
+ date: s.statement.end,
+ description: o.interestDescription(range),
+ },
+ });
+ }
+
+ return (
+
+
+
+
+ {o.title}
+
+
+
+
+
+
+
+
+
+
+
+
{o.lastPeriod}
+ {range}
+
+
+
+ {s.interestBooked ? (
+
{o.booked}
+ ) : canWrite && s.statement.interestMinor > 0 ? (
+
+ ) : null}
+
+ {o.taxNote}
+
+
+ {o.currentSoFar}:{" "}
+
+ {formatMoney(s.current.interestSoFarMinor)}
+
+
+
+
+ );
+}
diff --git a/src/features/transactions/components/statement-view.tsx b/src/features/transactions/components/statement-view.tsx
index 9627412..44f3ea1 100644
--- a/src/features/transactions/components/statement-view.tsx
+++ b/src/features/transactions/components/statement-view.tsx
@@ -27,6 +27,8 @@ import { todayIstanbul } from "@/shared/dates";
import { formatDate, formatMoney } from "@/shared/format";
import { previewReconciliationAction, reconcileAccountAction } from "../actions";
import type { AccountStatement, ReconcilePreview, StatementRow } from "../statement";
+import type { CardSummary, OverdraftSummary } from "../card";
+import { CardSummaryPanel, OverdraftPanel } from "./card-summary";
import { RowIcon, rowSubtitle, rowTitle, useTransactionDetail } from "./transaction-parts";
const t = tr.statement;
@@ -57,9 +59,13 @@ function SignedMoney({ minor, className }: { minor: number; className?: string }
export function StatementView({
statement: s,
+ card,
+ overdraft,
canWrite,
}: {
statement: AccountStatement;
+ card?: CardSummary | null;
+ overdraft?: OverdraftSummary | null;
canWrite: boolean;
}) {
const { openDetail, element: detailElement } = useTransactionDetail(canWrite);
@@ -95,6 +101,13 @@ export function StatementView({
}
/>
+ {card ? (
+
+ ) : null}
+ {overdraft ? (
+
+ ) : null}
+