feat(plan): ilk dönem sihirbazı (dönem, gelir, gider, birikim, bütçe)
- /plan/donem-ac?adim=1..5: her adım hemen kaydedilir, adım adreste - Panel ve Plan'da açık dönem yoksa "Dönemi başlat" kartı - MoneyInput (sayısal klavye, Türkçe biçim), moneyText/toKurus, period-dates - lib/periods.ts: dönem, plan kalemi, özet, bütçe ve öneri kancaları - SummaryTiles, PageLoading/PageError; 13 yeni vitest Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { formatMoney } from '@/lib/format'
|
||||
import { t } from '@/lib/strings'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { PeriodSummary } from '@/types/PeriodSummary'
|
||||
|
||||
const s = t.summary
|
||||
|
||||
/**
|
||||
* Plan özeti: Gelir − Sabit giderler − Birikim − Devreden borç (+ Devreden artı) = Harcanabilir.
|
||||
* Mobilde 2 sütun, geniş ekranda tek satır. Devir kutucukları yalnızca tutar varsa görünür.
|
||||
*/
|
||||
export function SummaryTiles({ summary }: { summary: PeriodSummary }) {
|
||||
const tiles: { label: string; amount: number; sign?: '−' | '+'; tone?: string }[] = [
|
||||
{ label: s.income, amount: summary.income_total, tone: 'text-income' },
|
||||
{ label: s.fixed, amount: summary.fixed_expense_total, sign: '−' },
|
||||
{ label: s.savings, amount: summary.savings_goal, sign: '−' },
|
||||
]
|
||||
if (summary.carryover_debt > 0) {
|
||||
tiles.push({ label: s.debt, amount: summary.carryover_debt, sign: '−', tone: 'text-expense' })
|
||||
}
|
||||
if (summary.carryover_surplus > 0) {
|
||||
tiles.push({ label: s.surplus, amount: summary.carryover_surplus, sign: '+' })
|
||||
}
|
||||
|
||||
return (
|
||||
<dl className="grid grid-cols-2 gap-2 sm:flex sm:flex-wrap">
|
||||
{tiles.map((tile) => (
|
||||
<div key={tile.label} className="rounded-xl border bg-card p-3 sm:min-w-32 sm:flex-1">
|
||||
<dt className="text-xs text-muted-foreground">
|
||||
{tile.sign && <span aria-hidden>{tile.sign} </span>}
|
||||
{tile.label}
|
||||
</dt>
|
||||
<dd className={cn('money mt-1 font-semibold', tile.tone)}>{formatMoney(tile.amount)}</dd>
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={cn(
|
||||
'col-span-2 rounded-xl border-2 p-3 sm:min-w-40 sm:flex-[1.4]',
|
||||
summary.spendable < 0
|
||||
? 'border-expense/50 bg-expense/5'
|
||||
: 'border-primary/40 bg-primary/5',
|
||||
)}
|
||||
>
|
||||
<dt className="text-xs font-medium text-muted-foreground">
|
||||
<span aria-hidden>= </span>
|
||||
{s.spendable}
|
||||
</dt>
|
||||
<dd
|
||||
className={cn(
|
||||
'money mt-1 text-xl font-bold',
|
||||
summary.spendable < 0 ? 'text-expense' : 'text-foreground',
|
||||
)}
|
||||
>
|
||||
{formatMoney(summary.spendable)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user