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:
@@ -326,9 +326,9 @@ const appointmentPayload = {
|
|||||||
هر محل یک `MedicalClinic` با `openingHoursSpecification` بساز و به `workLocation` وصل کن.
|
هر محل یک `MedicalClinic` با `openingHoursSpecification` بساز و به `workLocation` وصل کن.
|
||||||
در حالت سرویسی، `availableService` هم از `services` قابل ساخت است.
|
در حالت سرویسی، `availableService` هم از `services` قابل ساخت است.
|
||||||
|
|
||||||
اگر endpoint ساعات هفتگی عمومی در دسترس نبود، این بند را محدود کن به افزودن
|
**انجام شد:** `booking_locations` حالا فیلد `opening_hours` دارد (شیفتهای فعال هفته با نام
|
||||||
`availableService` و در PR ذکر کن که `openingHoursSpecification` نیاز به یک endpoint عمومی
|
انگلیسی روز)، پس `openingHoursSpecification` مستقیماً از همان ساخته میشود و به endpoint
|
||||||
جدید در `clinicpro` دارد.
|
جدیدی نیاز نیست.
|
||||||
- هر صفحه باید `generateMetadata` داشته باشد و `params` همیشه `await` شود.
|
- هر صفحه باید `generateMetadata` داشته باشد و `params` همیشه `await` شود.
|
||||||
|
|
||||||
### ۷. سازگاری و حالتهای مرزی
|
### ۷. سازگاری و حالتهای مرزی
|
||||||
|
|||||||
@@ -129,6 +129,14 @@ async function Doctor({ params }) {
|
|||||||
? await Promise.all([getDoctorAddresses(doctor.id), getBookingLocations(doctor.uuid)])
|
? 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.
|
// سرویسهای قابل رزرو، تجمیعشده از همهٔ محلها و یکتاشده بر اساس uuid.
|
||||||
const bookableServices = Object.values(
|
const bookableServices = Object.values(
|
||||||
bookingLocations
|
bookingLocations
|
||||||
@@ -192,6 +200,14 @@ async function Doctor({ params }) {
|
|||||||
streetAddress: addr.address,
|
streetAddress: addr.address,
|
||||||
},
|
},
|
||||||
...(addr.telephone && { telephone: addr.telephone }),
|
...(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 && {
|
...(addr.map?.latitude && addr.map?.longitude && {
|
||||||
geo: {
|
geo: {
|
||||||
"@type": "GeoCoordinates",
|
"@type": "GeoCoordinates",
|
||||||
|
|||||||
Reference in New Issue
Block a user