feat(değerlendirme): ay sonu değerlendirmesi sayfası
- Kakeibo'nun 4 sorusu, nasıl hesaplandığıyla birlikte - Grup bazında planlanan/gerçekleşen, en çok harcanan kategoriler, haftalık dağılım - "Nasıl daha iyi yapabilirim?" (Markdown) ve yalnızca başlıkları listelenen notlar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,66 @@
|
||||
import { ComingSoon, PageHeader } from '@/components/page'
|
||||
import { useState } from 'react'
|
||||
import { Info } from 'lucide-react'
|
||||
import { PageError, PageHeader, PageLoading } from '@/components/page'
|
||||
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} />
|
||||
<ComingSoon when="Faz 6'da" />
|
||||
{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 />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user