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

104 lines
2.8 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import type { CardType } from "@/lib/cards";
import { InternationalRatingsView } from "./InternationalRatings";
import { RatingExplainedView } from "./RatingExplained";
interface CardContentProps {
movieId: number;
cardType: CardType;
}
export function CardContent({ movieId, cardType }: CardContentProps) {
const [data, setData] = useState<unknown>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [retryToken, setRetryToken] = useState(0);
useEffect(() => {
const controller = new AbortController();
let cancelled = false;
async function fetchCard() {
try {
const response = await fetch(
`/api/cards/${cardType}?movieId=${movieId}`,
{ signal: controller.signal },
);
const payload = await response.json();
if (!response.ok) {
throw new Error(payload.error ?? "Something went wrong. Try again.");
}
if (!cancelled) {
setData(payload.data);
setError(null);
}
} catch (err) {
if ((err as Error).name === "AbortError" || cancelled) {
return;
}
setError(
err instanceof Error ? err.message : "Something went wrong. Try again.",
);
setData(null);
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
void fetchCard();
return () => {
cancelled = true;
controller.abort();
};
}, [cardType, movieId, retryToken]);
if (loading) {
return (
<div className="space-y-3 rounded-2xl border border-stone-200 bg-white p-6">
<div className="h-4 w-40 animate-pulse rounded bg-stone-200" />
<div className="h-4 w-full animate-pulse rounded bg-stone-200" />
<div className="h-4 w-5/6 animate-pulse rounded bg-stone-200" />
<p className="text-sm text-stone-600">Generating insight...</p>
</div>
);
}
if (error) {
return (
<div className="rounded-2xl border border-red-200 bg-red-50 p-6">
<p className="text-red-700">{error}</p>
<button
type="button"
onClick={() => {
setLoading(true);
setRetryToken((token) => token + 1);
}}
className="mt-4 rounded-lg border border-stone-300 px-4 py-2 text-sm text-stone-800 transition hover:bg-stone-100"
>
Try again
</button>
</div>
);
}
if (!data) {
return null;
}
switch (cardType) {
case "rating_explained":
return <RatingExplainedView data={data} />;
case "international_ratings":
return <InternationalRatingsView data={data} />;
default:
return null;
}
}