feat(seo): emit openingHoursSpecification per work location

The doctor page listed where a doctor works but never when, so search engines
had no working hours for any location. booking_locations now carries
opening_hours per context, so each MedicalClinic in the Physician JSON-LD gets
its own openingHoursSpecification, matched to the address by uuid.

Verified against the rendered page: the clinic location emits five shifts with
schema.org weekday URLs alongside the availableService entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-18 13:53:28 +03:30
co-authored by Claude Opus 4.8
parent 8d0902f35c
commit 0eb172517f
2 changed files with 19 additions and 3 deletions
@@ -326,9 +326,9 @@ const appointmentPayload = {
هر محل یک `MedicalClinic` با `openingHoursSpecification` بساز و به `workLocation` وصل کن.
در حالت سرویسی، `availableService` هم از `services` قابل ساخت است.
اگر endpoint ساعات هفتگی عمومی در دسترس نبود، این بند را محدود کن به افزودن
`availableService` و در PR ذکر کن که `openingHoursSpecification` نیاز به یک endpoint عمومی
جدید در `clinicpro` دارد.
**انجام شد:** `booking_locations` حالا فیلد `opening_hours` دارد (شیفت‌های فعال هفته با نام
انگلیسی روز)، پس `openingHoursSpecification` مستقیماً از همان ساخته می‌شود و به endpoint
جدیدی نیاز نیست.
- هر صفحه باید `generateMetadata` داشته باشد و `params` همیشه `await` شود.
### ۷. سازگاری و حالت‌های مرزی
+16
View File
@@ -129,6 +129,14 @@ async function Doctor({ params }) {
? await Promise.all([getDoctorAddresses(doctor.id), getBookingLocations(doctor.uuid)])
: [[], []];
// ساعات کاری per-location است؛ با uuid آدرس به هر محل وصل می‌شود.
const openingHoursByLocation = bookingLocations.reduce((acc, location) => {
if (location.location_uuid && location.opening_hours?.length) {
acc[location.location_uuid] = location.opening_hours;
}
return acc;
}, {});
// سرویس‌های قابل رزرو، تجمیع‌شده از همهٔ محل‌ها و یکتاشده بر اساس uuid.
const bookableServices = Object.values(
bookingLocations
@@ -192,6 +200,14 @@ async function Doctor({ params }) {
streetAddress: addr.address,
},
...(addr.telephone && { telephone: addr.telephone }),
...(openingHoursByLocation[addr.uuid]?.length && {
openingHoursSpecification: openingHoursByLocation[addr.uuid].map((shift) => ({
"@type": "OpeningHoursSpecification",
dayOfWeek: `https://schema.org/${shift.day}`,
opens: shift.opens,
closes: shift.closes,
})),
}),
...(addr.map?.latitude && addr.map?.longitude && {
geo: {
"@type": "GeoCoordinates",