Files
nobat724_front/app/doctor/[slug]/page.js
T
hamed 06a877a725 feat(metadata): enhance SEO by adding Open Graph and Twitter metadata across various pages
feat(blog): implement loading and error handling components for blog pages

feat(clinic): add loading and error handling components for clinic pages

feat(doctor): create loading and error handling components for doctor pages

feat(clinics): add loading component for clinics page

feat(specialties): improve metadata for specialties page with Open Graph and Twitter images

feat(layout): add structured data for Organization and WebSite in layout

fix(middleware): restrict middleware execution to specific routes to improve performance

chore(audit): add comprehensive SEO and performance audit documentation
2026-06-21 11:38:39 +03:30

138 lines
4.0 KiB
JavaScript

import { cache } from "react";
import DoctorPage from "@/components/doctor";
import Layout from "@/components/layout/StLayout";
import { getStateInfo } from "@/lib/getStateInfo";
import { imageUrl } from "@/helper";
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const FALLBACK_IMG = "https://www.nobat724.com/assets/images/logo.png";
const getDoctor = cache(async (slug) => {
try {
const res = await fetch(`${API_URL}/api/v1/doctor/${slug}`, {
next: { revalidate: 3600, tags: [`doctor-${slug}`] },
});
if (!res.ok) return null;
const json = await res.json();
return json?.data?.data ?? null;
} catch {
return null;
}
});
export async function generateMetadata({ params }) {
const { slug } = await params;
const { matchedCity } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
try {
const doctor = await getDoctor(slug);
if (!doctor) return {};
const specialtyNames = doctor.specialties?.map((s) => s.name).join(" و ") || "";
const title = `دکتر ${doctor.name}${specialtyNames ? " | " + specialtyNames : ""} | ${siteName}`;
const description =
doctor.detail ||
`رزرو نوبت آنلاین دکتر ${doctor.name}${specialtyNames ? " متخصص " + specialtyNames : ""}${doctor.address ? " | " + doctor.address : ""}`.trim();
const image = imageUrl(doctor.img) || FALLBACK_IMG;
return {
title,
description,
openGraph: {
title,
description,
type: "profile",
images: [image],
},
twitter: {
card: "summary",
title,
description,
images: [image],
},
};
} catch {
return {};
}
}
async function Doctor({ params }) {
const { slug } = await params;
let comments = null;
let rateAggregate = { point: 0, satisfaction: 0, averages: [] };
const doctor = await getDoctor(slug);
if (doctor) {
try {
const [resComments, resRate] = await Promise.all([
fetch(`${API_URL}/api/v1/comments/${doctor.uuid}`, { cache: "no-store" }),
fetch(`${API_URL}/api/v1/rate/${doctor.uuid}`, { cache: "no-store" }),
]);
const [jsonComments, jsonRate] = await Promise.all([
resComments.ok ? resComments.json() : null,
resRate.ok ? resRate.json() : null,
]);
comments = jsonComments?.data?.data;
rateAggregate = jsonRate?.data?.data ?? rateAggregate;
} catch (error) {}
}
const specialtyNames = doctor?.specialties?.map((s) => s.name).join(" و ") || "";
const jsonLd = doctor
? {
"@context": "https://schema.org",
"@type": "Physician",
name: `دکتر ${doctor.name}`,
...(specialtyNames && { medicalSpecialty: specialtyNames }),
...(doctor.img && { image: doctor.img }),
...(doctor.address && {
address: {
"@type": "PostalAddress",
streetAddress: doctor.address,
},
}),
}
: null;
const breadcrumbJsonLd = doctor
? {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{ "@type": "ListItem", position: 1, name: "خانه", item: "https://www.nobat724.com" },
{ "@type": "ListItem", position: 2, name: "پزشکان", item: "https://www.nobat724.com/doctors" },
{ "@type": "ListItem", position: 3, name: `دکتر ${doctor.name}` },
],
}
: null;
return (
<>
{jsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)}
{breadcrumbJsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
/>
)}
<Layout>
<DoctorPage
doctor={doctor}
comments={comments}
rateAggregate={rateAggregate}
slug={slug}
/>
</Layout>
</>
);
}
export default Doctor;