The account tab read basic_insurance.id (object) but the profile returns
basic_insurance_id (number), yielding [NaN], and never loaded the medical
history (other). Map basic_insurance_id/supplementary_insurance_id and
merge profile.other on load, and keep the profile uuid even for an empty
profile so PATCH saves work. Verified round-trip (name→label, insurance,
other) against the live API.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getParsedUserInfo() can return null (cookie missing/unparseable), so
reading userInfo.realName crashed the dashboard with a 500. Default to
an empty object and fall back to the user prop's name or 'کاربر'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the backend now returning 200 (lazy-created profile) instead of
404, align the consumers: the profile lives at res.data.data (the
endpoint double-nests), so the booking detail and dashboard read that
instead of res.data / the raw envelope. Drop the obsolete 404 special
handling (empty editable form now comes from the 200 payload), seed the
dashboard empty state when the profile has no real data yet, and make
the profile POST fall back to PATCH on 409 (already lazy-created).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The header decided between profile and login button only from the
server-passed logged prop (read once via cookies() in StLayout), so a
user who logged in client-side still saw ورود | ثبت نام until a hard
reload. Seed isLogged from the server prop (correct first paint, no
hydration mismatch) then sync it from the access_token cookie on mount
and on every route change, so login/logout reflect immediately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oauth/userinfo returns { success, data: {...} }, so after the interceptor
unwraps once the user lives at res.data, not res. getInfo checked
res.uuid (undefined), so it never set the userInfo cookie or redirected —
the /login page just sat there after entering the OTP. Read res.data,
store the user object (with a username alias for mobile_number so the
appointment flow keeps working), redirect via location.href, and show a
toast instead of silently staying when userinfo fails.
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>
The backend already returns is_available:false for past slots, but a
slot can lapse after the list is loaded. Disable any slot whose start
(unix seconds) is before now in the time grid, so a just-passed slot
can't be clicked. The grid disabling makes a SendAppo-level guard
redundant — a past slot can no longer be selected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Public wrapper for GET /api/v1/appointment-settings/month-availability
/{doctor_uuid}?year=&month= so the calendar can learn which days are
bookable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- getAppointmentSlots uses doctor_uuid + date (was doctor_id, which the
backend rejects with دکتر یافت نشد).
- postAppointmentPayment posts to /api/v1/payment/appointment (was the
nonexistent /api/v1/payment).
- Remove getAppointmentNotAvailable: the not-available route does not
exist (404); disabled dates come from the slots response.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>