refactor(all): migrate from React to Next.js

This commit is contained in:
2026-05-20 09:46:17 +03:30
parent dacbd3a328
commit f23108cda3
86 changed files with 2831 additions and 2679 deletions

View File

@@ -0,0 +1,115 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import EventDetail from "@/views/EventDetail";
import { PublicApiError, getPublicEventBySlug } from "@/lib/public-api";
import { siteUrl } from "@/lib/site";
import { getThumbUrl } from "@/lib/utils";
type Params = Promise<{ slug: string }>;
function cleanText(value?: string | null) {
if (!value) return "";
return value.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
}
async function loadEvent(slug: string) {
try {
return await getPublicEventBySlug(slug);
} catch (error) {
if (error instanceof PublicApiError && error.status === 404) {
notFound();
}
throw error;
}
}
export async function generateMetadata({
params,
}: {
params: Params;
}): Promise<Metadata> {
const { slug } = await params;
const event = await loadEvent(slug);
const description = cleanText(event.description).slice(0, 160);
const image = event.absolute_featured_image_url || getThumbUrl(event) || `${siteUrl}/favicon.ico`;
return {
title: event.title,
description,
alternates: { canonical: `/events/${event.slug}` },
robots: event.status === "draft" ? { index: false, follow: false } : undefined,
openGraph: {
title: event.title,
description,
url: `${siteUrl}/events/${event.slug}`,
siteName: "انجمن علمی کامپیوتر شرق گیلان",
type: "website",
images: [image],
locale: "fa_IR",
},
twitter: {
card: "summary_large_image",
title: event.title,
description,
images: [image],
},
};
}
export default async function EventDetailPage({
params,
}: {
params: Params;
}) {
const { slug } = await params;
const event = await loadEvent(slug);
const description = cleanText(event.description).slice(0, 160);
const structuredData = {
"@context": "https://schema.org",
"@type": "Event",
name: event.title,
description,
startDate: event.start_time,
endDate: event.end_time,
eventStatus:
event.status === "completed"
? "https://schema.org/EventCompleted"
: event.status === "cancelled"
? "https://schema.org/EventCancelled"
: "https://schema.org/EventScheduled",
eventAttendanceMode:
event.event_type === "online"
? "https://schema.org/OnlineEventAttendanceMode"
: event.event_type === "on_site"
? "https://schema.org/OfflineEventAttendanceMode"
: "https://schema.org/MixedEventAttendanceMode",
image: [event.absolute_featured_image_url || getThumbUrl(event) || `${siteUrl}/favicon.ico`],
url: `${siteUrl}/events/${event.slug}`,
organizer: {
"@type": "Organization",
name: "انجمن علمی کامپیوتر شرق گیلان",
url: siteUrl,
},
offers: {
"@type": "Offer",
url: `${siteUrl}/events/${event.slug}`,
priceCurrency: "IRR",
price: String(event.price ?? 0),
availability:
(event.capacity ?? 0) > (event.registration_count ?? 0)
? "https://schema.org/InStock"
: "https://schema.org/SoldOut",
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<EventDetail initialEvent={event} />
</>
);
}

View File

@@ -0,0 +1,11 @@
import type { Metadata } from "next";
import EventFreeSuccessPage from "@/views/EventFreeSuccessPage";
export const metadata: Metadata = {
title: "نتیجه ثبت‌نام رویداد",
robots: { index: false, follow: false },
};
export default function EventSuccessPage() {
return <EventFreeSuccessPage />;
}