import { createFileRoute } from "@tanstack/react-router" import { useState } from "react" import { useTranslation } from "react-i18next" import { Calendar, ChevronDown, ExternalLink, Film, Globe, MapPin, User } from "lucide-react" import { Link } from "@tanstack/react-router" import { BackButton } from "@/components/back-button" import { EmptyState } from "@/components/empty-state" import { HorizontalStrip } from "@/components/horizontal-strip" import { SwipeTabs } from "@/components/swipe-tabs" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Separator } from "@/components/ui/separator" import { Skeleton } from "@/components/ui/skeleton" import { posterUrl, tmdbProfileUrl } from "@/lib/api/client" import { usePersonCredits } from "@/hooks/use-search" import { useDocumentTitle } from "@/hooks/use-document-title" import { shortDate } from "@/lib/date" import { differenceInYears, parseISO } from "date-fns" export const Route = createFileRoute("/_app/people/$id")({ component: PersonDetailPage, }) function PersonDetailPage() { const { t } = useTranslation() const { id } = Route.useParams() const { data, isPending } = usePersonCredits(id) useDocumentTitle(data?.person.name) if (isPending) return if (!data) return null const { person, cast, crew } = data const age = person.birthday ? differenceInYears( person.deathday ? parseISO(person.deathday) : new Date(), parseISO(person.birthday), ) : null const creditTabs = [ ...(cast.length > 0 ? [{ value: "cast", label: t("movie.cast") + ` (${cast.length})` }] : []), ...(crew.length > 0 ? [{ value: "crew", label: t("movie.crew") + ` (${crew.length})` }] : []), ] as const return (
{/* Header */}
{person.profile_path ? ( ) : (
)}

{person.name}

{person.known_for_department && ( {person.known_for_department} )} {(person.homepage || person.imdb_url) && (
{person.imdb_url && ( IMDb )} {person.homepage && ( {t("person.homepage")} )}
)}
{/* Details card */} {(person.birthday || person.place_of_birth) && ( {person.birthday && (
{shortDate(person.birthday)} {age != null && ( ({age}) )}
)} {person.deathday && (
{shortDate(person.deathday)} ({t("person.deathday").toLowerCase()})
)} {person.place_of_birth && (
{person.place_of_birth}
)}
)} {/* Biography */} {person.biography && } {/* Also known as */} {person.also_known_as?.length > 0 && ( {t("person.alsoKnownAs")}
{person.also_known_as.map((name) => ( {name} ))}
)} {/* Filmography */} {creditTabs.length > 0 ? ( {(tab) => ( <> {tab === "cast" && ({ movieId: c.movie_id, title: c.title, year: c.release_year, posterPath: c.poster_path, subtitle: c.character }))} />} {tab === "crew" && ({ movieId: c.movie_id, title: c.title, year: c.release_year, posterPath: c.poster_path, subtitle: `${c.job}` }))} />} )} ) : ( )}
) } type FilmStripItem = { movieId: string title: string year?: number posterPath?: string subtitle: string } function FilmStrip({ items }: { items: FilmStripItem[] }) { return ( {items.map((item, i) => (
{item.posterPath ? ( ) : (
)}

{item.title}

{item.year &&

{item.year}

}

{item.subtitle}

))}
) } const BIO_COLLAPSE_THRESHOLD = 300 function BiographySection({ text }: { text: string }) { const { t } = useTranslation() const isLong = text.length > BIO_COLLAPSE_THRESHOLD const [expanded, setExpanded] = useState(!isLong) return ( {t("person.biography")}

{text}

{isLong && ( )}
) } function PersonSkeleton() { return (
{[1, 2, 3, 4].map((i) => ( ))}
) }