import { createFileRoute } from "@tanstack/react-router" import { useCallback, useState } from "react" import { useTranslation } from "react-i18next" import { BookOpen, ChevronLeft, ChevronRight } from "lucide-react" import { format, startOfMonth, subMonths } from "date-fns" import { MovieCard } from "@/components/movie-card" import { EmptyState } from "@/components/empty-state" import { SwipeToDelete } from "@/components/swipe-to-delete" import { VirtualList } from "@/components/virtual-list" import { Button } from "@/components/ui/button" import { Skeleton } from "@/components/ui/skeleton" import { useInfiniteDiary, useDeleteReview } from "@/hooks/use-diary" import type { DiaryEntryDto } from "@/lib/api/common" export const Route = createFileRoute("/_app/diary")({ component: DiaryPage, }) function groupByDate(items: DiaryEntryDto[]) { const groups: Record = {} for (const entry of items) { const date = entry.review.watched_at.slice(0, 10) ;(groups[date] ??= []).push(entry) } return Object.entries(groups).sort(([a], [b]) => b.localeCompare(a)) } function DiaryPage() { const { t } = useTranslation() const [month, setMonth] = useState(() => startOfMonth(new Date())) const { data, isPending, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteDiary({ sort_by: "desc" }) const deleteReview = useDeleteReview() const monthLabel = format(month, "MMMM yyyy") const monthStr = format(month, "yyyy-MM") const allItems = data?.pages.flatMap((p) => p.items) ?? [] const filtered = allItems.filter((e) => e.review.watched_at.startsWith(monthStr)) const grouped = groupByDate(filtered) const loadMore = useCallback(() => fetchNextPage(), [fetchNextPage]) type FlatItem = | { type: "header"; date: string } | { type: "entry"; entry: DiaryEntryDto } const flatItems: FlatItem[] = grouped.flatMap(([date, entries]) => [ { type: "header" as const, date }, ...entries.map((entry) => ({ type: "entry" as const, entry })), ]) const activeMonths = [...new Set(allItems.map((e) => e.review.watched_at.slice(0, 7)))].sort() const prevMonth = activeMonths.filter((m) => m < monthStr).at(-1) const nextMonth = activeMonths.filter((m) => m > monthStr).find(() => true) const canGoBack = hasNextPage || !!prevMonth const canGoForward = !!nextMonth && startOfMonth(new Date(nextMonth + "-01")) <= startOfMonth(new Date()) function goBack() { if (prevMonth) { setMonth(startOfMonth(new Date(prevMonth + "-01"))) } else { setMonth((m) => subMonths(m, 1)) } } function goForward() { if (nextMonth) { setMonth(startOfMonth(new Date(nextMonth + "-01"))) } } return (

{t("diary.title")}

{monthLabel}
{isPending && } {!isPending && grouped.length === 0 && ( )} {flatItems.length > 0 && ( item.type === "header" ? (

{item.date}

) : ( deleteReview.mutate(item.entry.review.id)} confirmTitle={t("diary.deleteReview")} confirmDescription={`${item.entry.movie.title} — ${item.entry.review.watched_at.slice(0, 10)}`} > ) } /> )}
) } function DiarySkeleton() { return (
{[1, 2, 3].map((i) => (
))}
) }