feat(değerlendirme): geçmiş dönemler ve düzeltme için yeniden açma

- Defter, Plan ve Değerlendirme'de dönem seçici (?donem=)
- Kapalı dönem salt okunur; düzeltmedeki dönem düzenlenebilir ve yeniden kapatılır
- Hane sahibi için onaylı "Düzeltme için yeniden aç"
- Geçmiş dönemler listesi, dönem durum rozeti

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 23:11:04 +03:00
parent 2c16cf4163
commit 861bfba85a
16 changed files with 500 additions and 127 deletions
+29 -20
View File
@@ -9,10 +9,12 @@ import { MoneyInput } from '@/components/money-input'
import { PageError, PageHeader, PageLoading } from '@/components/page'
import { Button } from '@/components/ui/button'
import { NoPeriodCard } from '@/features/period-wizard/no-period-card'
import { SelectedPeriod } from '@/features/periods/selected-period'
import { StepActions, StepIntro, Stepper } from '@/features/period-wizard/wizard-ui'
import { PeriodHeading } from '@/features/planning/plan-read'
import { formatDate, formatMoney, formatNumber, parseMoney, toIsoDate } from '@/lib/format'
import { useClosePeriod, useCurrentPeriod, useReview } from '@/lib/periods'
import { periodPath } from '@/lib/period-select'
import { useClosePeriod, useReview } from '@/lib/periods'
import { settlePreview } from '@/lib/review'
import { t } from '@/lib/strings'
import { cn } from '@/lib/utils'
@@ -27,32 +29,36 @@ const s = t.closing
/**
* Dönem kapatma sihirbazı: Özet → Birikim → Onay → Sonuç.
*
* Kapanınca açık dönem kalmaz (`current` → `null`); sonuç bu yüzden sayfanın kendi
* durumunda tutulur. Adımlar adreste değil: yarıda yenilenirse baştan başlar (hiçbir şey
* Kapanınca dönem artık seçilemez (açık dönem kalmaz); sonuç bu yüzden sayfanın kendi
* durumunda tutulur. `?donem=` ile düzeltmedeki dönem yeniden kapatılır. Adımlar adreste değil: yarıda yenilenirse baştan başlar (hiçbir şey
* kaydedilmemiştir, kapatma yalnızca son adımda tek istekle yapılır).
*/
export function ClosePeriodPage() {
const current = useCurrentPeriod()
const [result, setResult] = useState<PeriodCloseResult | null>(null)
let body: ReactNode
if (result) body = <Result result={result} />
else if (current.isPending) body = <PageLoading />
else if (current.isError)
body = <PageError message={current.error.message} onRetry={current.refetch} />
else if (!current.data)
body = (
<div className="space-y-4">
<p className="text-muted-foreground">{s.noOpen}</p>
<NoPeriodCard />
</div>
)
else body = <Wizard period={current.data} onClosed={setResult} />
const empty = (
<div className="space-y-4">
<p className="text-muted-foreground">{s.noOpen}</p>
<NoPeriodCard />
</div>
)
return (
<div className="mx-auto max-w-3xl">
<PageHeader title={s.title} description={s.description} />
{body}
{result ? (
<Result result={result} />
) : (
// Açık dönem ya da `?donem=` ile düzeltmedeki dönem (yeniden kapatma)
<SelectedPeriod empty={empty}>
{(p) =>
p.status === 'closed' ? (
<p className="text-muted-foreground">{s.alreadyClosed}</p>
) : (
<Wizard key={p.id} period={p} onClosed={setResult} />
)
}
</SelectedPeriod>
)}
</div>
)
}
@@ -128,7 +134,10 @@ function StepSummary({
{!period.reflection_md && (
<Notice>
{s.noReflection}{' '}
<Link to={paths.review} className="font-medium text-foreground underline">
<Link
to={periodPath(paths.review, period)}
className="font-medium text-foreground underline"
>
{s.writeReflection}
</Link>
</Notice>