import { cache } from "react"; import DoctorPage from "@/components/doctor"; import Layout from "@/components/layout/StLayout"; import { notFound } from "next/navigation"; import { getStateInfo } from "@/lib/getStateInfo"; import { getRequestOrigin } from "@/lib/getCanonicalUrl"; import { safeJsonLd } from "@/lib/sanitize"; import { imageUrl } from "@/helper"; const API_URL = process.env.NEXT_PUBLIC_API_URL; const FALLBACK_IMG = "https://nobat724.com/assets/images/logo.png"; const getDoctor = cache(async (slug) => { if (!slug || slug === "undefined") return null; const res = await fetch(`${API_URL}/api/v1/doctor/${slug}`, { next: { revalidate: 3600, tags: [`doctor-${slug}`] }, }); if (res.status === 404 || res.status === 400) return null; if (!res.ok) throw new Error(`Failed to fetch doctor: ${res.status}`); const json = await res.json(); return json?.data?.data ?? null; }); const getDoctorAddresses = cache(async (doctorId) => { if (!doctorId) return []; try { const res = await fetch(`${API_URL}/api/v1/clinic-pro/doctor-addresses/${doctorId}`, { next: { revalidate: 3600, tags: [`doctor-addresses-${doctorId}`] }, }); if (!res.ok) return []; const json = await res.json(); return json?.data ?? []; } catch { return []; } }); export async function generateMetadata({ params }) { const { slug } = await params; const { matchedCity } = await getStateInfo(); const siteName = matchedCity?.site_name || "نوبت 724"; const doctor = await getDoctor(slug); if (!doctor) notFound(); try { const specialtyNames = doctor.specialties?.map((s) => s.name).join(" و ") || ""; const title = `دکتر ${doctor.name}${specialtyNames ? " | " + specialtyNames : ""} | ${siteName}`; const detailText = (doctor.detail || "") .replace(/<[^>]*>/g, "") .replace(/\s+/g, " ") .trim(); const description = ( detailText || `رزرو نوبت آنلاین دکتر ${doctor.name}${specialtyNames ? " متخصص " + specialtyNames : ""}${doctor.address ? " | " + doctor.address : ""}`.trim() ).slice(0, 160); const image = imageUrl(doctor.img?.[0]?.url) || 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; const origin = await getRequestOrigin(); let comments = null; let rateAggregate = { point: 0, satisfaction: 0, averages: [] }; const doctor = await getDoctor(slug); if (!doctor) notFound(); 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 addresses = doctor ? await getDoctorAddresses(doctor.id) : []; const specialtyNames = doctor?.specialties?.map((s) => s.name).join(" و ") || ""; const jsonLd = doctor ? { "@context": "https://schema.org", "@type": "Physician", "@id": `${origin}/doctor/${doctor.uuid}#physician`, name: `دکتر ${doctor.name}`, url: `${origin}/doctor/${doctor.uuid}`, ...(specialtyNames && { medicalSpecialty: specialtyNames }), ...(doctor.img?.[0]?.url && { image: { "@type": "ImageObject", url: imageUrl(doctor.img[0].url), }, }), ...(Number(doctor.point) > 0 && { aggregateRating: { "@type": "AggregateRating", ratingValue: doctor.point, bestRating: "5", ratingCount: comments?.length || 1, }, }), ...(comments?.length > 0 && { review: comments.slice(0, 5).map((c) => ({ "@type": "Review", author: { "@type": "Person", name: c.author?.real_name || "بیمار" }, reviewBody: c.comment, datePublished: new Date(c.created * 1000).toISOString(), })), }), ...(doctor.social_media && { sameAs: Object.values(doctor.social_media).filter(Boolean), }), ...(addresses.length > 0 && { workLocation: addresses.map((addr) => ({ "@type": "MedicalClinic", name: addr.clinic_name || addr.name || `مطب دکتر ${doctor.name}`, address: { "@type": "PostalAddress", streetAddress: addr.address, }, ...(addr.telephone && { telephone: addr.telephone }), ...(addr.map?.latitude && addr.map?.longitude && { geo: { "@type": "GeoCoordinates", latitude: addr.map.latitude, longitude: addr.map.longitude, }, }), })), }), } : null; const specialtyLabel = doctor?.specialties?.[0]?.name || "پزشکان"; const breadcrumbJsonLd = doctor ? { "@context": "https://schema.org", "@type": "BreadcrumbList", itemListElement: [ { "@type": "ListItem", position: 1, name: "خانه", item: origin }, { "@type": "ListItem", position: 2, name: specialtyLabel, item: `${origin}/doctors?specialty=${encodeURIComponent(specialtyLabel)}`, }, { "@type": "ListItem", position: 3, name: `دکتر ${doctor.name}` }, ], } : null; return ( <> {jsonLd && (