import { useState } from 'react' import { Link } from 'react-router' import { paths } from '@/components/layout/nav-items' import { PageError } from '@/components/page' import { Skeleton } from '@/components/skeleton' import { Button } from '@/components/ui/button' import { WeekSummary } from '@/features/ledger/week-summary' import { PeriodHeading } from '@/features/planning/plan-read' import { dailyAllowance } from '@/lib/dashboard' import { formatDayMonth, formatMoney, toIsoDate } from '@/lib/format' import { weekIndexFor } from '@/lib/ledger' import { periodPath } from '@/lib/period-select' import { useBudget, useStats, useSummary } from '@/lib/periods' import { t } from '@/lib/strings' import { cn } from '@/lib/utils' import type { GroupBudget } from '@/types/GroupBudget' import type { Period } from '@/types/Period' import type { PeriodSummary } from '@/types/PeriodSummary' const s = t.dashboard /** Özet kartları: gelir, sabit gider, toplam birikim, devreden borç, anlık durum, gruplar */ export function Overview({ period, writable }: { period: Period; writable: boolean }) { const summary = useSummary(period.id) const budget = useBudget(period.id) const stats = useStats() const [today] = useState(() => toIsoDate(new Date())) const failed = [summary, budget].find((q) => q.isError) if (failed?.error) return if (!summary.data || !budget.data) return const sum = summary.data return (
{/* Borç kartı yalnızca varsa: 3 kartta mobilde sonuncusu tam genişlik, masaüstünde 3 sütun */}
0 ? 'lg:grid-cols-4' : 'lg:grid-cols-3 [&>*:last-child]:col-span-2 lg:[&>*:last-child]:col-span-1', )} > {sum.carryover_debt > 0 && ( )}
{/* Anlık durum DOM'da önce (ekran okuyucu ve mobilde ilk), masaüstünde sağ sütunda */}
) } function Tile({ emoji, label, amount, hint, tone, }: { emoji: string label: string /** `undefined` → yükleniyor */ amount: number | undefined hint?: string tone?: string }) { return (
{emoji} {label}
{amount === undefined ? : formatMoney(amount)}
{hint &&
{hint}
}
) } /** En belirgin kart: kalan harcanabilir + günlük öneri */ function StatusCard({ period, summary, today, writable, }: { period: Period summary: PeriodSummary today: string writable: boolean }) { const over = summary.remaining < 0 const { daysLeft, perDay } = dailyAllowance(summary.remaining, period, today) return (

📍 {s.statusTitle}

{over ? s.overspent : s.remaining}

{formatMoney(over ? -summary.remaining : summary.remaining)}

{s.statusDetail(formatMoney(summary.spendable), formatMoney(summary.spent_total))}

{writable && (daysLeft === 0 ? (

{s.ended}

) : (

{perDay !== null ? s.perDay(daysLeft, formatMoney(perDay)) : s.daysLeft(daysLeft)}

))}
) } /** 4 grup: planlanan / gerçekleşen; Haftalık (bugünün haftası) ↔ Aylık (tüm dönem) */ function GroupsCard({ period, budget, summary, today, writable, }: { period: Period budget: GroupBudget[] summary: PeriodSummary today: string writable: boolean }) { const [mode, setMode] = useState<'week' | 'month'>('week') const index = weekIndexFor(period.weeks, today) const week = period.weeks[index - 1] const range = (a: string, b: string) => `${formatDayMonth(a)}–${formatDayMonth(b)}` return (

🎯 {s.groupsTitle}

{(['week', 'month'] as const).map((m) => ( ))}

{mode === 'week' && week ? s.weekLabel(index, range(week.start_date, week.last_date)) : s.monthLabel(range(period.start_date, period.last_date))}

{summary.budget_total === 0 ? ( ) : ( )} {writable && summary.spent_total === 0 && summary.budget_total > 0 && (

{s.noEntries}

)}
) } function Empty({ text, to, action }: { text: string; to: string; action: string }) { return (

{text}

) } /** Yükleniyor: kartların şeklinde yer tutucular */ export function DashboardSkeleton() { return (
{[0, 1, 2, 3].map((i) => ( ))}
) }