components/appointment/ had every colour hard-coded, so the flow rendered
identically in either theme even after the wiring was fixed. It now reads from
CSS custom properties.
These are not new colours. The :root values are byte-for-byte the hex codes
that were already in the components — twenty-one files, mapped one to one — so
light mode is unchanged. The dark values are derived from those same colours:
surfaces and borders darkened, text inverted, and the two brand colours (the
indigo and the orange) lightened rather than replaced, because both lose
contrast against a dark surface at their original values.
Verified in a headless browser with prefers-color-scheme forced dark:
data-theme lands on <html> and --ap-surface resolves to #1b1b20 rather than
white. Six one-off colours remain hard-coded — a success green, an error red
and similar — each used exactly once and none of them a surface.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The service picker summed duration_minutes itself while the backend already
returns total_duration_minutes. Two sources of truth: when the formula changes to
solo/additional minutes, the site would keep showing the old number and the
patient would see a duration that does not match their appointment.
The picker runs before the date step and appointment-service-slots needs a date,
so it is called with today. total_duration_minutes does not depend on the date —
the backend computes it before touching that day's shifts, so the number is right
even when today is closed and start_times comes back empty.
The label reads "مدت تقریبی" until the server number arrives, then "مدت کل".
A missing field or a failed request falls back to the client sum with a
console.warn rather than blanking the step.
Staleness is derived from the selection key instead of reset in the effect body,
which also clears the set-state-in-effect lint warning.
Task: clinicpro/docs/new_feture/taskes/task-00b-nobat724-service-mode/
Co-Authored-By: Claude Opus 5 (1M context) <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>
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>
Rebuild the payment screen as a clear confirm-and-pay card: appointment
summary (doctor, patient, Jalali date/time), a prominent amount row
(15,000 تومان from the backend's 150,000 rials), and a countdown with a
progress bar tied to the booking's real expires_at. Fetch
/api/v1/payment/config: in test mode show a 'درگاه آزمایشی' notice and a
'پرداخت آزمایشی' button (gateway select hidden, since the backend forces
MockGateway); otherwise show the bank gateway select. Expired state
offers re-selecting a time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the mock for-another button with a real toggle: switching keeps
the user's own data, clears the form to editable patient fields (phone
becomes an input, adds علت مراجعه), and can switch back. SubmitData now
sends for_self plus patient_* only when booking for someone else, skips
the self-profile PATCH/POST in that case, stores the booking expires_at,
and surfaces a clearer 409 message. Thread appointmentExpiresAt through
the wizard to the payment step.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>