Files
2026-06-18 17:13:24 +02:00

122 lines
4.2 KiB
TypeScript

"use client";
import { useState } from "react";
import { SearchBar } from "@/components/SearchBar";
import { MovieHeader } from "@/components/MovieHeader";
import { MovieInsightPanel } from "@/components/MovieInsightPanel";
import type { TmdbMovieDetails, TmdbSearchResult } from "@/lib/tmdb";
export default function HomePage() {
const [selectedMovie, setSelectedMovie] = useState<TmdbSearchResult | null>(
null,
);
const [movieDetails, setMovieDetails] = useState<TmdbMovieDetails | null>(
null,
);
const [isLoadingMovie, setIsLoadingMovie] = useState(false);
const [movieError, setMovieError] = useState<string | null>(null);
const handleSelect = async (movie: TmdbSearchResult) => {
setSelectedMovie(movie);
setMovieDetails(null);
setMovieError(null);
setIsLoadingMovie(true);
try {
const res = await fetch(`/api/movie/${movie.id}`);
if (!res.ok) {
throw new Error("Failed to load movie details");
}
const details: TmdbMovieDetails = await res.json();
setMovieDetails(details);
} catch (err) {
setMovieError(
err instanceof Error ? err.message : "Failed to load movie details",
);
} finally {
setIsLoadingMovie(false);
}
};
const hasFullMovie = movieDetails != null;
return (
<>
{/* Compact navbar: title + slogan on left, search on right */}
<nav className="sticky top-0 z-50 border-b border-stone-200 bg-white">
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-6">
<div className="flex min-w-0 items-center gap-3">
<span className="font-semibold tracking-tight text-stone-950">
Film Intel
</span>
<span className="hidden truncate text-xs text-stone-500 sm:inline">
Reliable film ratings, explained
</span>
</div>
<div className="w-full max-w-[320px]">
<SearchBar
autoFocus={!selectedMovie}
onSelect={handleSelect}
compact
/>
</div>
</div>
</nav>
<main className="mx-auto min-h-[calc(100vh-3.5rem)] w-full max-w-6xl px-6">
{!selectedMovie && (
<div className="flex flex-col items-center justify-center py-16 text-center">
<p className="max-w-md text-sm text-stone-600">
Search for any film using the bar above. We&apos;ll explain its
age rating and how it differs across countries.
</p>
</div>
)}
{selectedMovie && (
<div className="space-y-8 py-8">
{hasFullMovie ? (
<MovieHeader movie={movieDetails} showBack={false} />
) : (
<div className="rounded-2xl border border-stone-200 bg-white p-6 shadow-sm">
<div className="space-y-2">
<h2 className="text-2xl font-semibold text-stone-950">
{selectedMovie.title}
{selectedMovie.release_date ? (
<span className="ml-2 font-normal text-stone-500">
({selectedMovie.release_date.slice(0, 4)})
</span>
) : null}
</h2>
{selectedMovie.overview && (
<p className="text-sm leading-relaxed text-stone-600">
{selectedMovie.overview}
</p>
)}
{isLoadingMovie && (
<p className="text-sm text-amber-600">
Loading extended details...
</p>
)}
{movieError && (
<p className="text-sm text-red-600">
{movieError}. Card content will still load.
</p>
)}
</div>
</div>
)}
<MovieInsightPanel movieId={selectedMovie.id} />
</div>
)}
</main>
<footer className="border-t border-stone-200 py-6 text-center text-xs text-stone-500">
AI-generated content for informational purposes. Verify timestamps on
your copy.
</footer>
</>
);
}