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>
GET /api/v1/insurances returns success(['data' => items]) → the body is
{ data: { data: [...] } }, so after the interceptor unwraps once the
array lives at res.data.data, not res.data. The callers set the list to
the wrapper object, so insurance.find threw 'not a function'. Read
res.data.data and guard with Array.isArray in both the booking detail
form and the dashboard insurance loader.
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>
DefaultSelect passed options={undefined} to MUI Autocomplete while the
insurance lists were still loading (or failed), throwing 'Cannot read
properties of undefined (reading length)'. Default options to [].
The old categorys/insurance_type + supplementary_insurance routes were
removed server-side (ERR_MOVED), so the lists never loaded. Point
getInsuranceType/getSupplementaryInsurance at the current
/api/v1/insurances?type=basic|supplementary (authed; the booking detail
step and dashboard are both logged-in), returning a flat data array.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The booking login step showed only a generic error when /api/auth/token
returned 400, so an invalid or expired OTP looked like a broken page.
The backend returns the reason (ERR_AUTH_002 invalid / ERR_AUTH_003
expired) in errors[].message, forwarded by the route; show it via toast.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DatePicker now loads month-availability for the two displayed Jalali
months (mapped to the Gregorian months they span, fetched once and
cached per month) and disables any day in disabled_dates plus past
days. Auto-select skips disabled days. When the doctor has online
booking turned off, show a notice instead of the calendar. doctorUuid
comes from the route params.
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>
The two months rendered reversed (current month on the left). Pin the
calendar row to dir=rtl and render the base month first so the current
month sits on the right and the next month on the left, matching the
design. Pin each month header to dir=ltr so the nav arrows stay on the
expected outer edges regardless of the RTL flip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The day picker used a Popover-based JalaliDatePicker that only showed two
empty text inputs on the booking page — the calendar was hidden until you
clicked the input, so it never matched the design. Replace it with an
always-visible inline two-month calendar (InlineJalaliMonth) rendered side
by side: current month on the right, next on the left (RTL), weekday
headers, Fridays in red, disabled days greyed, selected day in orange,
shared month navigation on the outer edges. Preserves the existing
contract: setDate(unix timestamp), disabledDates[], auto-select nearest
available day.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The appointment info/location headers fell back to /default-doctor.jpg,
which does not exist in public/, so the Next image optimizer returned
400 for doctors without a photo. Point the fallback at the existing
/assets/images/doctor.png (same default used on the doctor page).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-ups found while sweeping for stale shapes:
- The date picker emits a unix timestamp, but appointment-slots needs
Y-m-d; convert with moment before calling (slots never loaded before).
- Appointment summary (information/Detail.js) used the auth-required
getDoctorAddress and selectedSlot.time; derive the address from the
loaded doctor.address by location_id and use selectedSlot.start_time.
- SendAppo: drop the dead postAppointment block, unused useParams/loading,
and a duplicate disabled prop on the button.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getUserProfile returns { success, data } (interceptor unwraps once), so
the profile fields live under res.data, not res directly — the form was
always populated empty. Extract a buildProfileData helper and use it in
both the mount and step-3 effects, removing the duplicated mapping.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POST /api/v1/payment/appointment expects { appointment_uuid, gateway,
frontend_address } and returns data.redirect_url. Send that body, redirect
to the gateway URL the backend returns (instead of hand-building one),
align the bank options with the real mellat/sep gateways, and remove the
hard-coded 10,000 toman amount (no real price available at this step).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POST /api/v1/appointment expects { doctor_uuid, slot_start, slot_end,
note } with unix timestamps, and returns the appointment uuid at
data.uuid (not data.id). Send the right body, read the uuid, and on a
409 (slot already booked) alert the user and return to slot selection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The slots API returns data.sessions[].slots[] with start_time (HH:MM),
is_available, and start/end unix timestamps — not the morning/evening +
time/status shape the UI assumed. Add lib/appointmentSlots.js to flatten
sessions into morning/evening by start_time, fetch with doctor.uuid, and
read item.start_time / item.is_available in the slot grid. Derive the
clinic address from the already-loaded doctor.address by location_id
instead of a separate auth-required call.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The doctor endpoint is triple-nested, so doctorRes.data left doctor.id
undefined and the booking page received a broken doctor object. Extract
with data?.data?.data and remove the disabledDates fetch to the
nonexistent /appointment/not-available route (404); pass [] so the date
picker enables all dates and lets the slots response gate availability.
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>
The routing modal shipped broken deep links: snapp.ir/route and
tapsi.ir/route paths don't exist (404), and balad used the wrong path
and params. Keep only apps with verified destination deep links and
fix their formats:
- Remove Snapp and Tapsi (no valid web destination route).
- Balad: balad.ir/location?latitude=&longitude= (was /map?lat=&lng=).
- Google Maps and Waze kept (already correct).
- Guard data.map destructuring against null.
- Dashboard turn detail: use maps/dir directions URL instead of a
plain q= pin to match the مسیریابی label.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The map iframe lived inside a collapsed (max-h-0, overflow-hidden)
accordion with loading=lazy, so it often never loaded after expanding.
Mount the iframe only when the location item is open so it loads fresh,
and raise the open clamp from max-h-screen to max-h-[800px] so the map
isn't clipped on shorter viewports.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The GET /api/v1/doctor/{uuid} response is double-nested
({ success, data: { data: {...} } }), but the page only unwrapped one
level, leaving every field (name, specialties, expertise, address)
undefined — so خدمات / موقعیت مکانی / نظرات rendered empty or broken.
- Extract the doctor object with res.data?.data?.data in both
generateMetadata and the page.
- Hide the خدمات block when expertise is empty (no fabricated data).
- Render the location map iframe and routing buttons only when the
address has real latitude/longitude (was building q=null,null).
- Fix the comments list rendering a stray 0 on empty arrays.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete data/information.json, data/userData.json, data/bank.json — the
panel and patient dashboard now read real data from the API, and no
render path references these files anymore. Verified via grep + build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
backend جدید clinicpro صفحهبندی را در پاسخ تحت کلید meta برمیگرداند
({totalRecords, totalPages, currentPage})، نه page. به همین دلیل
PaginationContent مقدار totalRecords نمیگرفت و count=NaN میشد و
صفحهبندی مخفی میماند. اصلاح در صفحات /doctors و /clinics.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
برخی پزشکان address ندارند؛ data?.address[0] روی آنها کرش میکرد
(Cannot read properties of undefined). اصلاح به data?.address?.[0]
در Address و Telefon صفحه پروفایل پزشک.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
بهینهساز تصویر داخلی Next با fetch بومی خودش تصویر را از ddev میگیرد و
گواهی self-signed را رد میکرد (UNABLE_TO_VERIFY_LEAF_SIGNATURE روی
/_next/image). افزودن NODE_TLS_REJECT_UNAUTHORIZED=0 فقط به اسکریپت dev.
build و start production دستنخورده و امن میمانند.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
API مسیرهای تصویر را نسبی برمیگرداند (/uploads/...) که next/image روی
دامنه localhost میجست و 404 میداد. helper جدید imageUrl مسیرهای نسبی
uploads را با NEXT_PUBLIC_API_URL کامل میکند (absolute و asset محلی
دستنخورده). اعمال روی لیست/جزئیات پزشک، نوبت، گالری کلینیک، آواتارها.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
backend جدید clinicpro جریان سهمرحلهای دارد:
send-code → verify-code (uuid+code) → oauth/token (uuid).
- token route حالا اول /api/v1/user/verify-code را صدا میزند سپس
/oauth/token با بدنه JSON {grant_type, uuid} (بهجای form-urlencoded
با client_id/secret/scope که فقط backend پروداکشن قدیم میپذیرفت)
- refresh route به مسیر درست /oauth/token/refresh با بدنه JSON
- تست E2E روی ddev محلی: access_token واقعی صادر شد
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
prop loading روی Button فقط در MUI v6+ پشتیبانی میشود؛ این پروژه v5 است
و هشدار "Received false for a non-boolean attribute loading" میداد.
- دکمههای login/verify: disabled + CircularProgress شرطی (بازخورد بصری حفظ شد)
- بقیه دکمهها: loading → disabled (ادغام با disabled موجود در صورت وجود)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
route های token و refresh از fetch بومی Node استفاده میکردند که گواهی
self-signed ddev محلی را رد میکرد و خطای 500 میداد. حالا از axiosInstance
استفاده میکنند و خطای واقعی backend را با status درست برمیگردانند.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
صفحات سرور (doctors, doctor/[slug], appointment/[doctorId]) از axios خام
استفاده میکردند که گواهی self-signed ddev محلی را رد میکرد
(unable to verify the first certificate). حالا از axiosInstance/fetchReq
در lib/req استفاده میکنند که در development آن را میپذیرد.
افزودن clinic-pro.ddev.site (http/https) به remotePatterns تصاویر.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- افزودن الگوی http برای api.clinic-pro.ir به next.config (عکسهای بیمه
با http برمیگردند و خطای 500 next/image میدادند)
- جایگزینی prop loading نامعتبر روی Button با disabled در SendReq/ButtonApply
(loading فقط در MUI v6+ پشتیبانی میشود؛ این پروژه v5 است و lab نصب نیست)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- buildPatientUser: ساخت آبجکت user از user-profile واقعی با کلیدهای
مورد انتظار کامپوننتها؛ comments/messages خالی و اعداد هدر صفر
(بدون داده ساختگی، چون endpoint ندارند)
- app/dashboard/page.js پروفایل را server-side از user-profile/{uuid} میگیرد
- حذف import userData.json mock از Content داشبورد
- نوبت/تراکنش/تب اطلاعات از قبل به API واقعی وصل بودند (بدون تغییر)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- انتقال adapterهای نماینده به lib/representationAdapters (server-safe،
بدون وابستگی client مثل js-cookie/react-toastify)
- turns: لیست از daily[] داشبورد ماهانه (ستون نام/تخصص خالی، بدون داده ساختگی)
- user-account: حساب بانکی واقعی از bank_account نماینده (map به آرایه تککارته)
- add-doctor: حذف import mock مرده (کامپوننت data مصرف نمیکرد)
- رفع dead import اشتباه از turns/page در clinic/doctors که build را میشکست
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
تبدیل صفحه dashboard پنل به Server Component که آمار ماهانه نماینده را
از API میگیرد (year/month میلادی جاری) و با adaptRepresentationDashboard
به propهای کامپوننت map میکند. حذف داده mock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
افزودن adaptRepresentationInfo که خروجی API نماینده را به propهای
کامپوننتهای نمایشی map میکند و حذف import داده mock از Content پنل.
فیلدهای آماری بدون معادل API خالی میمانند (بدون داده ساختگی).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
افزودن getRepresentationDashboardMonthly و getRepresentationDashboardYearly
به لایه request برای مصرف endpointهای داشبورد نماینده.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Security:
- Disable SSL verification only in development (lib/req.js)
- Wrap all JSON.parse(cookie) calls in try-catch via safeJsonParse utility
- Sanitize dangerouslySetInnerHTML in blog/clinic with sanitizeHtml utility
- Fix open redirect in payment page — validate URL origin before redirect
- Fix cookie cleanup on 401 — use js-cookie with correct domain scope
Performance:
- Wrap ItemDoctor with React.memo to prevent unnecessary re-renders
- Replace <img> with Next.js <Image> in blog Caption component
Functionality:
- Fix memory leak in Recode.js — store intervals in refs, cleanup on unmount
- Add null guard on retryIcon.current before classList manipulation
- Fix getParsedUserInfo in helper to handle malformed cookie gracefully
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix GPS map links always sending literal "latitude"/"longitude" strings
instead of actual coordinates in openLocation/Content.js
- Add api.clinic-pro.ir to next.config.js remotePatterns so production
images load correctly
- Fix appointment page: await params and getStateInfo (Next.js 15 pattern)
- Enable 401 handling in api.js: clear cookies and redirect to /login
- Move OAuth client_secret to server-side API routes (/api/auth/token,
/api/auth/refresh) so it is never bundled into client-side JavaScript
- Update SendReq, SubmitData, ButtonSendData to call API routes instead
of directly sending client_secret from the browser
- Update docker-compose.yml to use server-only CLIENT_SECRET env var
- Remove debug console.log from clinic doctors list component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace new Date(b.created) with toSafeDate() helper to guard against
invalid date strings from blog API responses
- Use a single NOW constant instead of new Date() per entry to avoid
serialization issues during static generation
- Remove unused imports (cityData, DOMAIN_CONFIG, etc.)
- Add CLAUDE.md with project architecture and development guidance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>