Integrate the `ViewCounter` component into meme standard and modal detail pages to track and display content views.
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import { ChevronLeft } from "lucide-react";
|
|
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { ContentCard } from "@/components/content-card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ViewCounter } from "@/components/view-counter";
|
|
import { ContentService } from "@/services/content.service";
|
|
|
|
export const revalidate = 3600; // ISR: Revalider toutes les heures
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}): Promise<Metadata> {
|
|
const { slug } = await params;
|
|
try {
|
|
const content = await ContentService.getOne(slug);
|
|
return {
|
|
title: `${content.title} | MemeGoat`,
|
|
description: content.description || `Regardez ce mème : ${content.title}`,
|
|
openGraph: {
|
|
images: [content.thumbnailUrl || content.url],
|
|
},
|
|
};
|
|
} catch (_error) {
|
|
return { title: "Mème non trouvé | MemeGoat" };
|
|
}
|
|
}
|
|
|
|
export default async function MemePage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await params;
|
|
|
|
try {
|
|
const content = await ContentService.getOne(slug);
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto py-8 px-4">
|
|
<ViewCounter contentId={content.id} />
|
|
<Link
|
|
href="/"
|
|
className="inline-flex items-center text-sm mb-6 hover:text-primary transition-colors"
|
|
>
|
|
<ChevronLeft className="h-4 w-4 mr-1" />
|
|
Retour au flux
|
|
</Link>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
<div className="lg:col-span-2">
|
|
<ContentCard content={content} />
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<div className="bg-white dark:bg-zinc-900 p-6 rounded-xl shadow-sm border">
|
|
<h2 className="font-bold text-lg mb-4">À propos de ce mème</h2>
|
|
<div className="space-y-4 text-sm">
|
|
<div>
|
|
<p className="text-muted-foreground">Publié par</p>
|
|
<p className="font-medium">
|
|
{content.author.displayName || content.author.username}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-muted-foreground">Date</p>
|
|
<p className="font-medium">
|
|
{new Date(content.createdAt).toLocaleDateString("fr-FR", {
|
|
day: "numeric",
|
|
month: "long",
|
|
year: "numeric",
|
|
})}
|
|
</p>
|
|
</div>
|
|
{content.description && (
|
|
<div>
|
|
<p className="text-muted-foreground">Description</p>
|
|
<p>{content.description}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white dark:bg-zinc-900 p-6 rounded-xl shadow-sm border text-center">
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
Envie de créer votre propre mème ?
|
|
</p>
|
|
<Button className="w-full">Utiliser ce template</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} catch (_error) {
|
|
notFound();
|
|
}
|
|
}
|