- Updated DoctorPage component to accept bookingResources prop for appointment list.
- Added serviceQuery function to serialize service item UUIDs for API requests.
- Introduced new API endpoints for fetching booking resources and resource slots.
- Enhanced tests for new resource-based booking functionality, including resource selection and service availability.
- Created ResourceSelect component for selecting appointment types, including doctor and resource options.
- Updated appointment submission logic to include resource_uuid in payload when applicable.
- Ensured UI reflects changes in booking flow without disrupting existing doctor-centric experience.
- Removed the newly added city-focused section from the homepage.
- Fixed 404 error for Persian slugs in specialties and blog pages by implementing a decode helper.
- Removed the "مخصوص شهر" label from article headers.
- Linked article tags to their respective filter pages.
- Adjusted related content images to prevent distortion.
- Added a section in the "About Us" page for physician registration guidance.
- Updated the logo in the "About Us" header to the correct version.
- Added tests for the new functionality and ensured existing tests are updated accordingly.
- ایجاد پرامپت برای رفع خطای 404 ناشی از لینک `/doctor/undefined` در سایتهای بهبهان و یاسوج.
- افزودن کامپوننت `CityHighlights` برای نمایش محتوای یکتا و آمار پزشکان و تخصصهای پرمراجعه در صفحهٔ اصلی هر شهر.
- بهروزرسانی تستهای مربوط به کامپوننتهای `ItemDoctor` و `CityHighlights` برای اطمینان از عدم وجود لینکهای نامعتبر و نمایش صحیح اطلاعات.
- ایجاد سند جدید برای مستندسازی خطاهای Search Console که باگ نیستند و نیاز به رفع ندارند.
- افزودن تستهای واحد برای توابع `buildCityIntro` و دیگر توابع مرتبط با تولید متن یکتا برای صفحات لیست.
- Introduced SpecialtyChips component to manage the display of doctor's specialties with a primary specialty and a count of additional specialties.
- Updated ItemDoctor component to utilize SpecialtyChips for better UI presentation.
- Enhanced PosterLight and Poster components to display primary specialties and a text line for sub-specialties.
- Implemented nextSpecialtyFilter function to improve specialty selection logic in the Content component.
- Updated search functionality to allow searching by both doctor name and specialty.
- Added new specialties to specialties.json for better coverage.
- Created sync-specialties script to synchronize specialties data with the backend during build.
- Added tests for new components and helper functions to ensure functionality and reliability.
- Added `getBlogTagFacets` API call to fetch blog tags based on city scope.
- Updated `BlogsPage` to read selected tag from URL and handle tag changes with URL updates.
- Modified `Title` component to display tags from the new API and reflect active tag state.
- Enhanced breadcrumb navigation to link categories to their respective pages.
- Adjusted related content section to display articles from the same category and fixed layout issues.
- Corrected heading hierarchy across various components for better SEO compliance.
- Ensured consistent styling and spacing in related content items.
- Introduced a new script `make-blog-cover.mjs` to create a default cover image for blog posts.
- The image is generated in PNG format with dimensions 1200x630, suitable for Open Graph.
- Utilizes the site's branding colors and logo from `public/nobat724.svg`.
- Includes custom font styling using the Vazirmatn font.
- The generated image is saved to `public/assets/images/blog-default-cover.png`.
Deactivated doctors are now absent from the public list, but their
/doctor/{uuid} page still loaded. getDoctor() now treats the backend's
raw `is_active === false` as not-found, so the profile 404s like the
list — matching the site's "hide inactive doctors" behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The backend now answers 503 with code MAINTENANCE_MODE while maintenance is
on. Without this change a visitor got a red error toast over a broken page
client-side, and a silently empty page server-side, because fetchReq discards
the status and returns null on any failure.
- lib/maintenance.js detects the state by BOTH status 503 and the error code;
a bare 503 can come from a reverse proxy and is not maintenance
- The axios interceptor checks it before the 401 branch, so a maintenance
response never triggers the refresh-token path or logs the user out
- fetchReq redirects to /maintenance, with a silentMaintenance opt-out used by
getStateInfo: that one runs inside generateMetadata and while rendering the
maintenance page itself, where a redirect is either ineffective or loops
- redirect() works by throwing, so the try/catch blocks in the doctors,
clinics and specialties pages now rethrow NEXT_REDIRECT instead of
swallowing it
- clinicApi.js handles 503 too; it previously rendered maintenance as a clinic
with zero doctors
- The page reuses the existing 404 design and is marked noindex
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Updated community IDs for various components in graph.json to reflect correct associations.
- Added new function `getPublishedDoctors()` in app/sitemap.js and established relationships with existing functions.
- Adjusted source locations for several functions in app/sitemap.js to ensure accurate mapping.
- Enhanced canonical URL handling in lib/getCanonicalUrl.js to prevent incorrect canonicalization for paginated specialty pages.
- Updated manifest.json with new modification times and AST hashes for affected files.
- Implemented canonical URL strategies for city-specific domains and entities.
- Added helper functions for domain and city resolution.
- Created tests for canonical URL generation and domain resolution.
- Introduced entity quality checks for doctors and clinics to ensure meaningful content.
- Developed unique introductory texts for listing pages to avoid duplicate content.
- Established robots.txt policies for listing pages to manage indexing based on user filters.
- Enhanced specialty content with dynamic introductions and FAQs to improve SEO.
The profile decided "نوبتدهی غیرفعال است" from `doctor.active` alone, while the
page already had `booking_locations` — the more precise source, since the backend
only returns locations that are genuinely bookable. The two could disagree, and
for a doctor bookable only at a clinic they did.
A shared `bookingState` helper now drives both the desktop card and the mobile
bar: any location means bookable, the label comes from the earliest
`next_available_at`, and locations with no capacity yet read as "فعلاً نوبت خالی
ندارد" rather than disabled. With no locations at all it falls back to the
previous `doctor.active` / `free_turn` fields.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The site offered a "personal practice" for a doctor who has no personal address
at all — the schedule existed but its shifts pointed at the clinic's address, so
there was nowhere to go. The backend now filters those out; this consumes the
filtered contract and adds the per-day dimension.
- getBookingLocations takes an optional date and the appointment page refetches
on it, merging available_on_date into the existing list rather than replacing
it, so browsing the calendar never resets the user's choice.
- The browsed day had to be lifted out of the Date step: selectedDate is only
set once a slot is confirmed, far too late to drive availability.
- A location closed on the chosen day renders disabled with «در این روز نوبت
ندارد», and when every location is closed the step says so instead of showing
an empty slot list. If the already-selected location closes, a notice appears
with a link back to the picker — silently showing nothing was the failure mode
worth avoiding.
- Doctor profile: workLocation in the Physician JSON-LD is limited to addresses
that appear in booking_locations, since schema.org presents them as places a
patient can attend. The address card still lists the others — they are real
practice details — tagged «بدون نوبتدهی آنلاین».
Verified end-to-end with a temporary unused address on the test doctor: the
visible card listed both and tagged the unused one, while workLocation carried
only the bookable one. The row was removed afterwards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
A doctor now has one booking schedule per context — the personal practice plus
one per clinic — and every booking endpoint takes an optional clinic_uuid where
omitting it means the personal practice, not a wildcard. This site sent none, so
a clinic-only doctor showed no availability at all and a doctor working in both
places silently booked into the wrong one.
- services/response.js: getBookingLocations + clinic_uuid on slots,
service-slots, booking-services and month-availability. The manual query
building is kept so the service_item_uuids[] serialisation does not change.
- AppointmentPage owns the selected location; booking_mode and services are
derived from it instead of a separate getBookingServices call, which drops a
request. Changing location clears the selected service, slot and date, since
a service from one location cannot be booked into another.
- New LocationSelect step, shown only when there is more than one location.
The list arrives sorted by earliest free slot, so the first item is the
default and is not re-sorted here.
- DatePicker drops its month cache when the location changes; otherwise the
previous location's disabled days stayed on the calendar.
- The slot address now comes from the selected location rather than
doctor.address, which does not contain clinic addresses.
- clinic_uuid rides through to the appointment payload, and
/appointment/[doctorId]?clinic_uuid=… preselects a location.
- Doctor page JSON-LD gains availableService from the bookable services.
openingHoursSpecification still needs a public weekly-hours endpoint.
Removed the dead locateVisit state, which was initialised true and never unset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET /api/v1/clinic-pro/doctor-addresses/{id} returns { data: { data: [...] } },
but getDoctorAddresses read json.data (the wrapper object, not the array), so
addresses.length/.map were undefined — the locations card + map never rendered
even when the doctor had an address. Extract json.data.data.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Doctor page map: the visible locations card read doctor.address (empty
from the detail endpoint) while coordinates live in the separately
fetched addresses. Thread `addresses` (with map.latitude/longitude) down
page → DoctorPage → DetailDoctor → Locations; card hidden when empty
- Claim modal: updated info-box text ("نوبتهای این پروفایل عمومی و غیرخاص
هستند")، added mobile field (validated, must match account), added ALTCHA
widget (submit disabled until captcha resolves; payload sent as `altcha`)
- Owner delete: services.deleteDoctor + a guarded two-step "حذف این پروفایل"
in the claim success screen (owner enforced server-side; 403/409 shown)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Updated `getStateInfo` to fetch site context for domains not in city.json, returning `repContext` with representative details.
- Implemented caching for site context requests to optimize performance.
- Modified doctor and clinic listing pages to pass the `domain` parameter when fetching data for global representatives.
- Adjusted metadata generation in layout and pages to reflect representative branding based on `repContext`.
- Added documentation for the new functionality in `.claude/prompt/global-rep-domain-site.md`.
- Add metadata to login and login-verify pages to prevent indexing.
- Update robots.txt to disallow additional sensitive paths.
- Enhance sitemap generation to filter by city and include accurate last modified dates.
- Refactor canonical URL generation to support multi-domain architecture, ensuring self-canonicalization for city domains.
- Remove deprecated CanonicalHandler component and streamline canonical URL handling.
- Introduce safe JSON-LD output to prevent XSS vulnerabilities.
- Add payment layout with appropriate metadata to prevent indexing.
- Conduct a comprehensive technical SEO audit and implement necessary fixes across the application.