2c16cf4163
- /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>
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
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>
|
||
)
|
||
}
|