makearmy-app/components/buying-guide/BuyingGuideProduct.tsx
2025-10-15 16:53:14 -04:00

188 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/buying-guide/product/[id]/page.tsx
import Link from "next/link";
import ReactMarkdown from "react-markdown";
const API_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
const ASSET_URL = process.env.NEXT_PUBLIC_ASSET_URL;
async function getEntry(id: string) {
const res = await fetch(
`${API_URL}/items/bg_entries/${id}?fields=*,links.id,links.text,links.url,links.target,scores.id,scores.cat,scores.value,scores.body,header.id,date_updated`,
{
cache: "no-store",
}
);
if (!res.ok) {
const error = await res.text();
console.error(`Failed to fetch entry: ${error}`);
throw new Error(`Error fetching entry ${id}`);
}
const { data } = await res.json();
return data;
}
export default async function ProductDetail({
params,
}: {
params: Promise<{ id: string }>;
}) {
const id = (await params).id;
const entry = await getEntry(id);
const avgScore =
entry?.scores?.length > 0
? (
entry.scores.reduce((sum: number, s: any) => sum + Number(s.value), 0) /
entry.scores.length
).toFixed(1)
: "N/A";
const headerUrl = entry.header?.id
? `${ASSET_URL}/assets/${entry.header.id}?cache-buster=${entry.date_updated}&key=system-large-contain`
: null;
return (
<div className="max-w-4xl mx-auto px-4 py-8 space-y-6">
{/* Header Banner */}
{headerUrl && (
<div className="w-full h-64 relative overflow-hidden rounded-xl shadow">
<img
src={headerUrl}
alt="Header Image"
className="object-cover w-full h-full rounded-xl"
/>
</div>
)}
{/* Title */}
<div className="flex justify-between items-start">
<div>
<h2 className="text-xl font-semibold text-white">{entry.product_make}</h2>
<h1 className="text-4xl font-bold text-yellow-500 mt-2">{entry.product_model}</h1>
</div>
<div className="text-right">
{entry.product_price && (
<p className="text-lg text-white font-medium mt-1">
{entry.product_price.startsWith("Starting at") ? entry.product_price : `Starting at ${entry.product_price}`}
</p>
)}
<Link
href="/buying-guide"
className="text-sm text-blue-500 underline mt-2 inline-block"
>
Back to Buying Guide
</Link>
</div>
</div>
{/* Links & Score Summary */}
{(Array.isArray(entry.links) || Array.isArray(entry.scores)) && (
<div className="flex flex-col md:flex-row gap-8">
{Array.isArray(entry.links) && entry.links.length > 0 && (
<div className="md:w-1/2">
<h3 className="text-xl font-semibold mb-2">Links</h3>
<ul className="list-disc ml-6 space-y-1">
{entry.links.map((link: any, idx: number) => (
<li key={idx}>
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-700 underline"
>
{link.text || link.url}
</a>
{link.target && (
<span className="text-sm text-gray-500"> ({link.target})</span>
)}
</li>
))}
</ul>
</div>
)}
{(Array.isArray(entry.scores) && entry.scores.length > 0) && (
<div className="md:w-1/2">
<h3 className="text-xl font-semibold mb-2">Score Summary</h3>
<ul className="space-y-1">
{entry.scores.map((s: any, idx: number) => (
<li key={idx} className="flex justify-between">
<span>{s.cat}</span>
<span className="font-semibold">{s.value}/10</span>
</li>
))}
</ul>
<p className="mt-2 font-bold text-right">Total: {avgScore}</p>
</div>
)}
</div>
)}
{/* Overview */}
{entry.review_overview_text && (
<div className="prose max-w-none">
<h3 className="text-xl font-semibold mb-2">Overview</h3>
<ReactMarkdown>{entry.review_overview_text}</ReactMarkdown>
</div>
)}
{/* Intro */}
{entry.review_intro_text && (
<div className="prose max-w-none">
<h3 className="text-xl font-semibold mb-2">{`${entry.product_make}, ${entry.product_model} Review by ${entry.author}`}</h3>
<ReactMarkdown>{entry.review_intro_text}</ReactMarkdown>
</div>
)}
{/* Detailed Scores */}
{(Array.isArray(entry.scores) && entry.scores.length > 0) && (
<div className="space-y-4">
{entry.scores.map((s: any, idx: number) => (
<div key={idx} className="p-4 rounded border">
<p className="text-xl font-semibold">
{s.cat} <span className="text-blue-600">{s.value}/10</span>
</p>
<div className="text-sm text-gray-400">
<ReactMarkdown>{s.body}</ReactMarkdown>
</div>
</div>
))}
</div>
)}
{/* Recommendation */}
{entry.rec_text && (
<div className="prose max-w-none">
<h3 className="text-xl font-semibold mb-2">Recommendation</h3>
<ReactMarkdown>{entry.rec_text}</ReactMarkdown>
</div>
)}
{/* Updates */}
{entry.updates && (
<div className="prose max-w-none">
<h3 className="text-xl font-semibold mb-2">Updates</h3>
<ReactMarkdown>{entry.updates}</ReactMarkdown>
</div>
)}
{/* Video */}
{entry.video_review_url && (
<div>
<h3 className="text-xl font-semibold mb-2">Video Review</h3>
<div className="aspect-w-16 aspect-h-9">
<iframe
src={entry.video_review_url.replace("watch?v=", "embed/")}
className="w-full h-96 rounded"
frameBorder="0"
allowFullScreen
/>
</div>
</div>
)}
</div>
);
}