feat(defter): hafta seçici ve hızlı harcama giriş formu

- /defter: önceki/sonraki/bu hafta, haftanın harcanan/bütçe toplamı
- Form: tutar, 4 grup düğmesi, kategori ve ödeme türü seçimi, tarih, açıklama
- Son seçimler bu cihazda hatırlanır; sunucu hataları ilgili alana düşer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 11:20:18 +03:00
parent 7143613ab2
commit f930f2fbcc
14 changed files with 696 additions and 5 deletions
+69 -2
View File
@@ -1,11 +1,78 @@
import { ComingSoon, PageHeader } from '@/components/page'
import { useState } from 'react'
import { useSearchParams } from 'react-router'
import { PageError, PageHeader, PageLoading } from '@/components/page'
import { NoPeriodCard } from '@/features/period-wizard/no-period-card'
import { Section } from '@/features/planning/plan-read'
import { useCategories, usePaymentMethods } from '@/lib/catalog'
import { toIsoDate } from '@/lib/format'
import { parseWeekParam, weekIndexFor } from '@/lib/ledger'
import { useBudget, useCurrentPeriod } from '@/lib/periods'
import { t } from '@/lib/strings'
import type { Period } from '@/types/Period'
import { EntryForm } from './entry-form'
import { WeekPicker } from './week-picker'
export function LedgerPage() {
const current = useCurrentPeriod()
return (
<>
<PageHeader title={t.pages.ledger.title} description={t.pages.ledger.description} />
<ComingSoon when="Faz 5'te" />
{current.isPending ? (
<PageLoading />
) : current.isError ? (
<PageError message={current.error.message} onRetry={current.refetch} />
) : current.data ? (
<LedgerView period={current.data} />
) : (
<NoPeriodCard />
)}
</>
)
}
/** Hafta adreste tutulur (`?hafta=2`): yenileyince / geri gelince aynı hafta açılır. */
function LedgerView({ period }: { period: Period }) {
const [params, setParams] = useSearchParams()
const [today] = useState(() => toIsoDate(new Date()))
const inPeriod = today >= period.start_date && today <= period.last_date
const todayIndex = inPeriod ? weekIndexFor(period.weeks, today) : null
const index =
parseWeekParam(params.get('hafta'), period.weeks.length) ?? weekIndexFor(period.weeks, today)
const week = period.weeks[index - 1]
const budget = useBudget(period.id)
const categories = useCategories()
const methods = usePaymentMethods()
const goTo = (n: number) =>
setParams(n === todayIndex ? {} : { hafta: String(n) }, { replace: true })
const failed = [categories, methods].find((q) => q.isError)
return (
<div className="mx-auto max-w-3xl space-y-4">
<WeekPicker
weeks={period.weeks}
index={index}
todayIndex={todayIndex}
budget={budget.data}
onChange={goTo}
/>
<Section title={t.ledger.formTitle}>
{failed?.error ? (
<PageError message={failed.error.message} onRetry={failed.refetch} />
) : !categories.data || !methods.data ? (
<PageLoading />
) : (
<EntryForm
period={period}
week={week}
today={today}
categories={categories.data}
methods={methods.data}
onSaved={(date) => goTo(weekIndexFor(period.weeks, date))}
/>
)}
</Section>
</div>
)
}