import { createFileRoute, Link } from "@tanstack/react-router" import { lazy, Suspense, useState } from "react" import { useTranslation } from "react-i18next" import { Bar, BarChart, XAxis, YAxis } from "recharts" import { BarChart3, DollarSign, Globe, Hash, Share2, Star } from "lucide-react" import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart" import { BackButton } from "@/components/back-button" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" import { RatingHistogram } from "@/components/rating-histogram" import { RevealCard } from "@/components/reveal-card" import { HeroCard } from "@/components/wrapup-hero" import { FunFacts } from "@/components/wrapup-fun-facts" import { RankCard } from "@/components/wrapup-rank-card" import { posterUrl } from "@/lib/api/client" import { fmtUsd } from "@/lib/format" import { WATCH_MEDIUMS } from "@/lib/watch-mediums" import { useWrapUpReport } from "@/features/wrapup" import { useDocumentTitle } from "@/hooks/use-document-title" import type { MovieRef } from "@/features/wrapup" const WrapUpShareCard = lazy(() => import("@/components/wrapup-share-card").then((m) => ({ default: m.WrapUpShareCard }))) const monthlyChartConfig = { count: { label: "Movies", color: "var(--primary)" }, } satisfies ChartConfig const genreChartConfig = { count: { label: "Movies", color: "var(--primary)" }, } satisfies ChartConfig export const Route = createFileRoute("/_app/wrapup/$id")({ component: WrapUpReportPage, }) function WrapUpReportPage() { const { t } = useTranslation() const { id } = Route.useParams() const { data: report, isPending } = useWrapUpReport(id) const [showShare, setShowShare] = useState(false) useDocumentTitle(report ? `${report.date_range.start.slice(0, 4)} Wrap-Up` : undefined) if (isPending) return if (!report) return null const watchHours = Math.round(report.total_watch_time_minutes / 60) return (
{showShare && ( setShowShare(false)} /> )} {/* Ratings */} {t("wrapup.ratings")} {report.avg_rating != null && (

{report.avg_rating.toFixed(1)}★

{t("wrapup.averageRating")}

)}
{report.busiest_month && ( {t("wrapup.busiestMonth", { month: report.busiest_month })} )} {report.busiest_day_of_week && ( {t("wrapup.favoriteDay", { day: report.busiest_day_of_week })} )}
{/* Top Directors */} {report.top_directors.length > 0 && ( )} {/* Top Actors */} {report.top_actors.length > 0 && ( )} {/* Genres */} {report.top_genres.length > 0 && ( {t("wrapup.genres")} {t("wrapup.genresExplored", { count: report.genre_diversity })} } />
{report.highest_rated_genre && ( {t("wrapup.highestRated", { genre: report.highest_rated_genre })} )} {report.lowest_rated_genre && ( {t("wrapup.lowestRated", { genre: report.lowest_rated_genre })} )}
)} {/* Watch Medium Distribution */} {report.watch_medium_distribution && report.watch_medium_distribution.length > 0 && ( {t("wrapup.howYouWatched", { defaultValue: "How You Watched" })} {report.watch_medium_distribution.map((wm) => { const def = WATCH_MEDIUMS.find((d) => d.value === wm.medium) const Icon = def?.icon return (
{Icon && } {def ? t(def.labelKey) : wm.medium} {t("common.films", { count: wm.count })}
) })}
)} {/* Monthly Activity */} {report.movies_per_month.length > 0 && ( {t("wrapup.monthlyActivity")} v.slice(5)} tick={{ fontSize: 10, fill: "rgba(255,255,255,0.85)" }} tickLine={false} axisLine={false} /> report.movies_per_month.find((m) => m.year_month === String(v))?.label ?? String(v)} />} /> )} {/* Keywords */} {report.top_keywords.length > 0 && ( {t("wrapup.keywords")}
{report.top_keywords .filter((k) => !k.keyword.includes("creditsstinger")) .slice(0, 15) .map((k) => ( {k.keyword} {k.count} ))}
)} {/* Budget & Language */} {(report.total_budget_watched != null || report.language_distribution.length > 1) && (() => { const hasBudget = report.total_budget_watched != null const hasLang = report.language_distribution.length > 1 const bothVisible = hasBudget && hasLang return (
{hasBudget && (

{fmtUsd(report.total_budget_watched!)}

{t("wrapup.totalBudget")}

{report.avg_budget != null && (

{t("wrapup.avgBudget", { amount: fmtUsd(report.avg_budget) })}

)}
)} {hasLang && (

{report.language_distribution.length}

{t("wrapup.languages")}

{report.language_distribution.slice(0, 3).map((l) => l.language.toUpperCase()).join(", ")}

)}
)})()} {/* Highlights */} {t("wrapup.highlights")}
{/* Rewatches */} {report.total_rewatches > 0 && ( {t("wrapup.rewatches")}

{report.total_rewatches}

{t("wrapup.moviesRewatched")}

{report.most_rewatched_movie && (

{t("wrapup.mostRewatched")} {report.most_rewatched_movie.title} ({report.most_rewatched_movie.year})

)}
)} {/* All Movies */} {report.poster_paths.length > 0 && ( {t("wrapup.allMovies", { count: report.poster_paths.length })}
{report.poster_paths.map((path, i) => (
))}
)}
) } function MovieHighlight({ label, movie, showRuntime }: { label: string; movie?: MovieRef; showRuntime?: boolean }) { if (!movie) return null const content = (
{movie.poster_path && (
{movie.title}
)}

{label}

{movie.title}

{showRuntime && movie.runtime_minutes ? `${movie.runtime_minutes} min` : movie.year}

) if (movie.movie_id) { return {content} } return content } function ReportSkeleton() { return (
) }