Commit Graph
657 Commits
Author SHA1 Message Date
hamed 0a2a2bfba3 fix(datePicker): hide next button for inactive months on larger screens 2026-07-23 19:43:02 +03:30
hamedandClaude Opus 4.8 cd32f6c8bd fix(doctor): 404 deactivated doctor profile page
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>
2026-07-23 19:39:38 +03:30
hamed ccbd028fee Add JSON representation of search fix and SEO prompts for Nobat724 2026-07-22 16:49:34 +03:30
hamed a39b90c88c feat: normalize doctor name query to enhance search functionality and improve URL building 2026-07-20 12:33:22 +03:30
hamed ac3a84d55d feat: implement search URL building and enhance search functionality in Fields and RedirectLink components 2026-07-20 12:28:52 +03:30
hamed 984511653b feat: update specialty URL generation logic and improve indexing strategy for specialties 2026-07-20 11:54:03 +03:30
hamed 30c3fe8637 feat: enhance SEO for listing and specialty pages, fix search autocomplete issue 2026-07-20 10:26:03 +03:30
hamed 9011b230b3 Update manifest.json and add new AST cache files for clinic contact info and maintenance mode prompts
- Updated mtime and ast_hash for several files in manifest.json
- Added new AST cache files for the clinic contact info fix and maintenance mode client prompts
2026-07-19 22:06:34 +03:30
hamedandClaude Fable 5 daf38c8631 feat(maintenance): show maintenance page when the API is in maintenance
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>
2026-07-19 22:01:45 +03:30
hamed 18484f974d feat: update doctor display logic to prioritize display_name and enhance metadata description with address details 2026-07-19 19:57:05 +03:30
hamed 1522ee8b73 feat: enhance clinic contact information display with new helper functions for phone, city, and state 2026-07-19 16:44:06 +03:30
hamed 10b5a19576 feat: add MapView component for displaying maps with markers 2026-07-19 16:43:59 +03:30
hamed e8a7bdf2fc feat: enhance doctor and clinic components with doctorTitle helper and improve rendering logic 2026-07-19 16:17:24 +03:30
hamed 4c8377f439 feat: add doctorTitle helper function and update references in metadata and FAQ 2026-07-19 15:40:24 +03:30
hamed b3ffba82d4 fix: update source locations and confidence scores in graph.json; update mtime in manifest.json for multiple components 2026-07-19 14:01:05 +03:30
hamed 29b8819021 refactor(docker): update Dockerfile for Next.js 15 and enhance build stages 2026-07-19 13:19:43 +03:30
hamed 672a7d89bf Fix community IDs and update sitemap functionality
- 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.
2026-07-19 09:32:00 +03:30
hamed 97e990982f Update manifest.json with new mtime and ast_hash for sitemap.js 2026-07-19 09:10:09 +03:30
hamed 8eb55679b2 feat(sitemap): optimize doctor URL fetching and add sitemap index threshold warning 2026-07-19 09:10:04 +03:30
hamed 488e5eb0a4 Add JSON files for blog city scoping and SEO post-deploy verification prompts
- Created a new JSON file for the blog city scoping activation prompt, including nodes and edges representing the document structure.
- Added a new JSON file for the SEO post-deploy verification prompt, detailing nodes and edges related to SEO verification tasks.
2026-07-19 09:02:23 +03:30
hamed 4d7b87ee1a feat(blog): enhance metadata generation and blog fetching by city context 2026-07-19 09:02:14 +03:30
hamed 88782e70c2 test(extractEntityCityId): prioritize address over top-level city in entity extraction 2026-07-19 08:54:00 +03:30
hamedandClaude Opus 4.8 21f5f07bc6 chore(prompt): record SEO audit and split remaining work
P1-P11 of the live SEO audit are implemented and verified against a local
production build, so the audit file becomes a reference rather than a
task list: it now records what shipped, the root causes that differed
from the original hypotheses, and the deliberate trade-offs.

Remaining work is split into smaller prompts, ordered by dependency:

- seo-post-deploy-verification: the acceptance criteria were "curl on
  production" but were only run against a local build
- blog-city-scoping-activate: blocked on the backend blog city column
- sitemap-simplify-with-city: drops the 35-sweep workaround once the
  doctors list exposes city

Each names its blocking dependency and carries reference numbers so a
regression is visible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 08:09:21 +03:30
hamed c64a4a7d69 refactor(specialties): simplify specialty link generation logic 2026-07-19 07:52:58 +03:30
hamed fd48b48613 feat: add canonical URL handling and entity quality checks
- 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.
2026-07-19 07:52:50 +03:30
hamedandClaude Fable 5 36816eded2 fix(doctor): derive booking state from booking_locations
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>
2026-07-18 16:25:37 +03:30
hamedandClaude Opus 4.8 dfcf62735b chore(graphify): rebuild code graph after day-aware locations
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 14:44:51 +03:30
hamedandClaude Opus 4.8 b37096048c feat(booking): show only locations that can actually be booked that day
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>
2026-07-18 14:44:49 +03:30
hamedandClaude Opus 4.8 f4dd73e55e chore(graphify): rebuild code graph after opening-hours change
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 13:53:31 +03:30
hamedandClaude Opus 4.8 0eb172517f 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>
2026-07-18 13:53:28 +03:30
hamedandClaude Opus 4.8 8d0902f35c chore(graphify): rebuild code graph after booking-location change
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 13:48:53 +03:30
hamedandClaude Opus 4.8 eba0c6a5ba feat(booking): let patients choose the booking location
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>
2026-07-18 13:48:31 +03:30
hamedandClaude Opus 4.8 effa264027 docs(prompt): add booking-locations multi-context prompt
Companion to the clinicpro context-separation change. A doctor now has one
booking schedule per context (personal practice + one per clinic), and every
booking endpoint takes an optional clinic_uuid where omitting it means the
personal practice — not a wildcard.

This site sends no clinic_uuid anywhere, so today it shows no availability at
all for clinic-only doctors and silently books into the wrong location for
doctors who work in both. The prompt covers the new
/api/v1/appointment-booking-locations contract, threading the selected location
through the booking state, and the JSON-LD follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 13:38:02 +03:30
hamed 6f9640cea6 fix(payment): adjust date formatting to use UTC offset for consistency 2026-07-16 00:30:20 +03:30
hamed 165a0a2240 feat(appointment): implement service-based booking flow with service selection and slot adaptation 2026-07-15 23:48:40 +03:30
hamed d5845bf84d refactor(dateTime): streamline slot handling and improve session management 2026-07-15 19:16:24 +03:30
hamed 07d6526f74 fix(doctors): ensure active filter is correctly parsed as a number 2026-07-14 11:25:43 +03:30
hamed 58f05f8f4f feat(login): add redirect parameter to login links for better user experience 2026-07-12 10:06:15 +03:30
hamed 42ac6645d5 feat(doctor profile): implement unclaimed doctor handling and UI adjustments 2026-07-12 10:00:03 +03:30
hamed ce05e464c4 Merge branch 'feature/doctor-claim' 2026-07-11 20:12:07 +03:30
hamed 8f1c63d3b1 Refactor code structure for improved readability and maintainability 2026-07-11 17:50:54 +03:30
hamed f9c1f2994e Implement feature X to enhance user experience and optimize performance 2026-07-11 17:03:38 +03:30
hamedandClaude Opus 4.8 ed985f55fb feat(doctor map): replace Google Maps iframe with Leaflet (OSM)
The Google Maps iframe was blocked by CSP (frame-src falls back to
default-src 'self'). Switched the doctor-page location map to react-leaflet
with OpenStreetMap tiles — no iframe, and CSP already allows https image
tiles (img-src 'self' https:). Marker icons are bundled from the leaflet
package (no CDN). MapView is dynamically imported (ssr:false) since Leaflet
needs window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:29:17 +03:30
hamedandClaude Opus 4.8 c007ffa2b3 fix(doctor page): addresses were mis-extracted from double-nested response
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>
2026-07-11 15:25:39 +03:30
hamedandClaude Opus 4.8 813225ff6d fix(doctor claim): mobile field is the account number, read-only
Mobile is prefilled from the logged-in user's cookie (userInfo.mobile_number)
and rendered read-only; removed it from editable form state/validation and
send that number in the claim body. Backend already enforces it must match
the account, so the field can't diverge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:16:27 +03:30
hamedandClaude Opus 4.8 f584f581e5 fix(auth): correct login detection + return to origin after login
- isUserLoggedIn() checked the access_token cookie, which is never set
  (access_token lives in memory / tokenStore; only userInfo + uuid are
  cookies). It therefore always returned false — the claim modal (and
  comment auth checks) kept showing the login prompt even when logged in.
  Now reads the userInfo cookie.
- Claim modal login link carries ?redirect=<current path>; after OTP login
  SendReq returns to that path (guarded to internal "/..." only, blocks
  protocol-relative //) instead of always going to "/".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:12:58 +03:30
hamedandClaude Opus 4.8 4fed9a8c57 feat(doctor): fix map render + claim modal captcha/mobile + owner delete
- 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>
2026-07-11 15:04:28 +03:30
hamedandClaude Opus 4.8 d8ab26b4b7 feat(doctor): claim-profile section + modal for unclaimed IRIMC-imported doctors
- ClaimProfileSection (components/doctor/claim): shown only when
  doctor.owner_status === "unclaimed"; banner explains the profile is not
  yet managed by the doctor, button "تأیید و مدیریت این پروفایل"
- Modal: login prompt when logged out; otherwise first/last name,
  national code, Jalali birth-date (existing JalaliDatePicker) — posts to
  POST api/v1/doctor/{uuid}/claim (identity verified server-side via API.ir;
  no client call to API.ir, no token exposure)
- States: loading, per-field validation, server error (Persian envelope
  message), double-submit guard, success welcome message + redirect
- Shared component across main domain and all representative subdomains
- services/response.js: getDoctorClaimInfo / postDoctorClaim

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 11:44:40 +03:30
hamed 2fd0eec156 fix: update Altcha component to properly set attributes and handle state changes 2026-07-10 14:55:12 +03:30
hamed 58a69e3345 fix: remove unused FA_STRINGS and update attribute setting for language in Altcha component 2026-07-10 11:57:15 +03:30