Files
kakeibo/frontend/src/features/review/page.tsx
T
suphicakir 2c16cf4163 feat(değerlendirme): dönem kapatma sihirbazı
- /degerlendirme/kapat: Özet → Birikim → Onay → Sonuç
- Birikime aktarılacak tutar (varsayılan tamamı), kalan artı/açık devri önizlemesi
- Değerlendirme sayfasına "Dönemi Kapat" bölümü
- Yeni dönem önerisi son dönemin bitişinden başlar (erken kapatılan ay önerilmez)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 22:59:38 +03:00

81 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react'
import { Info } from 'lucide-react'
import { Link } from 'react-router'
import { paths } from '@/components/layout/nav-items'
import { PageError, PageHeader, PageLoading } from '@/components/page'
import { Button } from '@/components/ui/button'
import { NotesSection } from '@/features/notes/notes-section'
import { NoPeriodCard } from '@/features/period-wizard/no-period-card'
import { PeriodHeading } from '@/features/planning/plan-read'
import { useCategories } from '@/lib/catalog'
import { formatDate, toIsoDate } from '@/lib/format'
import { useCurrentPeriod, useReview } from '@/lib/periods'
import { t } from '@/lib/strings'
import type { Period } from '@/types/Period'
import { GroupComparison, TopCategories, WeeklyDistribution } from './breakdowns'
import { Questions } from './questions'
import { Reflection } from './reflection'
export function ReviewPage() {
const current = useCurrentPeriod()
return (
<>
<PageHeader title={t.pages.review.title} description={t.pages.review.description} />
{current.isPending ? (
<PageLoading />
) : current.isError ? (
<PageError message={current.error.message} onRetry={current.refetch} />
) : current.data ? (
<ReviewView period={current.data} />
) : (
<NoPeriodCard />
)}
</>
)
}
/**
* Hesaplar sorgu anında backend'de yapılır (`GET /periods/{id}/review`); defterde
* bir kayıt değişince bu sayfa da yenilenir. Kapalı dönem: salt okunur (Adım 5'te seçilebilir).
*/
function ReviewView({ period }: { period: Period }) {
const review = useReview(period.id)
const categories = useCategories(true)
const [today] = useState(() => toIsoDate(new Date()))
const readOnly = period.status === 'closed'
if (review.isError) return <PageError message={review.error.message} onRetry={review.refetch} />
if (!review.data) return <PageLoading />
return (
<div className="space-y-6">
<PeriodHeading period={period} />
{!readOnly && today <= period.last_date && (
<p className="flex items-start gap-2 rounded-xl bg-muted px-3 py-2 text-sm text-muted-foreground">
<Info className="mt-0.5 size-4 shrink-0" aria-hidden />
{t.review.ongoing(formatDate(period.last_date))}
</p>
)}
<Questions review={review.data} />
<div className="grid items-start gap-4 lg:grid-cols-2">
<GroupComparison review={review.data} />
<TopCategories review={review.data} categories={categories.data ?? []} />
</div>
<WeeklyDistribution review={review.data} />
<Reflection period={period} readOnly={readOnly} />
<NotesSection periodId={period.id} context="review" readOnly={readOnly} collapsed />
{!readOnly && (
<section className="flex flex-col gap-4 rounded-2xl border bg-card p-5 sm:flex-row sm:items-center sm:justify-between">
<div className="space-y-1">
<h2 className="font-semibold">{t.review.closeTitle}</h2>
<p className="text-sm text-muted-foreground">{t.review.closeHint}</p>
</div>
<Button asChild size="lg" className="shrink-0">
<Link to={paths.closePeriod}>{t.review.closeAction}</Link>
</Button>
</section>
)}
</div>
)
}