Rework slot-mode service selection: pick a section, its services show as a
checkbox list, and chosen services collect into a removable 'selected'
chip list that persists when switching to another section (services from
multiple sections accumulate). Changing the section no longer clears the
selection. Add a test covering multi-section accumulation and chip removal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Appointments could only reference a single service (ManyToOne). Add an
appointment_service_items join table (ManyToMany) so an appointment can
carry several services; the first stays the primary service_item for
backward compatibility, and toArray now also returns service_items[].
Both create endpoints (my/appointment, admin/appointment) accept
service_item_uuids[] and attach all of them. A new duration_from_services
flag gates the slot_end recompute: service-booking mode sends it true
(slot_end = start + Σ durations); slot mode omits it so the manual end
time is preserved. The admin endpoint previously ignored services entirely.
Frontend: in slot mode the single service dropdown becomes a checkbox list
filtered by the selected section (multi-select); service mode sends the
duration flag. Migration + backend/entity tests + docs updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
National code is stored on the profiles table, not users, so the patients
search returned null user_national_code for patients that actually have one
— which blocked selecting an existing patient on the appointment create
form. Backfill user_national_code from the profile (batch query) in the
list endpoint. On the create page, show a picked patient's stored national
code read-only and only prompt for input when the record genuinely lacks
one. Add backend tests and update docs/api/patient.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New reusable DigitInput normalizes Persian/Arabic digits to English on
input, strips non-digits, enforces maxDigits, and is always LTR + numeric
keyboard. Use it for the phone and national-code fields on the appointment
create page (phone previously kept raw Persian digits; national code lost
Persian digits to the \D strip).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backend requires a valid national code for every appointment, but a picked
patient record may have none stored. Show a selected-patient card with an
editable national-code field (prefilled from the record) so submit can
enable. Add happy-path and boundary tests for the picked-patient flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Show name/phone/national-code fields only after clicking 'مراجعه کننده جدید';
hide the existing-patient search while in new-patient mode, with a
'انتخاب از لیست موجود' link to switch back. Update tests to click the
toggle before filling the fields.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move switch to its own row; render amount field (wrapped in .field for a
proper bordered box) and wallet-charge button on a following aligned row.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Introduced a new booking mode in WeeklySchedule to support service-based appointments.
- Updated SlotCalculatorService to calculate available start times based on selected service durations and buffer times.
- Enhanced AppointmentController to handle service items during booking, calculating slot_end on the server side.
- Implemented validation to ensure at least one bookable service exists for doctors in service mode.
- Added new API endpoint to retrieve available appointment slots based on selected services.
- Updated MyAppointmentsController to accept service items during appointment creation.
- Modified ServiceItem entity to include a bookable flag, allowing services to be marked for scheduling.
- Created migration to add bookable column to service_items table.
- Added tests for service-based slot calculations and validation logic.
- Implemented a new endpoint `/api/v1/my/appointment/patient-lookup` to search for patients by mobile number before booking an appointment.
- Updated the `NewAppointmentModal` component to utilize the new patient lookup feature, allowing for direct booking if the patient is found with a national code.
- Enhanced the appointment booking form to handle mobile input normalization and display relevant fields based on the search results.
- Added tests for the new patient lookup functionality, ensuring proper behavior for found and not found cases, as well as validation for mobile input.
- Updated sidebar tests to reflect changes in the sidebar component structure and functionality.
Admin-side booking (POST /api/v1/my/appointment and
/api/v1/admin/appointment) resolved the patient User by mobile only, so
one person booked under two mobiles produced two User rows — and two
case-files, since PatientRecord is keyed on user_id. National code is the
real unique identity (User.national_code is already unique); a person may
have several mobiles.
Booking now requires + validates patient_national_code and resolves the
patient national-code-first (then mobile) via a shared PatientResolver, so
the case-file stays unique per national code even across mobiles. Reusing a
mobile already bound to a different national code returns 422
ERR_PROFILE_MOBILE_TAKEN. The admin create form and NewAppointmentDrawer
gain a national-code field and send it; both had a dead patient-picker URL
(/api/v1/patient) fixed to the real /api/v1/patients, whose payload already
carries user_national_code for autofill.
Docs (appointment.md, admin.md) and tests updated; new
AppointmentNationalCodeTest covers success, single-file reuse, missing,
invalid, and identity-conflict cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild admin Topbar to match clinic-pro-tauri header:
- Icon order: theme -> settings -> bell (was theme -> bell -> settings)
- Settings gear now navigates to the role-based settings page
instead of opening the personalization panel
- Search placeholder changed to «جستجو», dropped the ⌘K hint
- Add ProfileMenu: avatar + user name + context/role subtitle + caret,
with a dropdown (profile / payments / settings / personalization / logout).
Links are role-aware; personalization keeps the existing settings panel.
Frontend-only, no API changes. Adds ProfileMenu.test + Topbar.test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the earlier mis-scoped orange on the personalization panel and
instead expose rgb(241,119,50) as a selectable 'رنگ اصلی' swatch. When
picked it overrides --primary/-600/-700/-soft/-soft2/-ring inline with
exact hex, so every var(--primary) consumer across the whole admin
(buttons, active nav, rings) becomes that orange. Non-fixed hues keep
the oklch hue pipeline (inline overrides removed on switch).
- uiStore: BrandHue interface + optional fixed hex; shared applyBrand()
helper handles fixed vs hue, dark-aware soft tints
- AdminLayout: reuse applyBrand() (no divergence with store)
- Topbar: swatch shows the exact fixed color
- tests: fixed-color apply, dark soft, and override-cleanup on switch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply rgb(241,119,50) exclusively to the personalization (شخصیسازی)
settings panel — its heading and active segmented buttons — matching
the product requirement that this section use the dedicated orange
while the rest of the panel keeps tauri's purple.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the admin panel's visual system to the tauri source app:
- neutralize color tokens (border/surface-2/3, text 1-3) from the
bluish indigo tint to tauri's neutral grays (#efefef/#3b3b3b/#7e7e7e/#a1a1a1)
- add --nav-active-bg (#f8f8ff light) for the sidebar active item,
matching tauri active-link-side
- enable Vazirmatn ss02 stylistic set on body (tauri typography)
- responsive topbar heights 56/67/79/90 exactly as tauri
Settings sub-sidebar active orange (#f17732 = rgb(241,119,50)) and
ClinicPro branding/section grouping kept per product decision.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AppointmentDetailPage now derives the appointment's local day from slot_start
and points both the «بازگشت» button and the «نوبتها» breadcrumb to
/admin/appointments?date=<that day>, so viewing/acting on an appointment from
any day returns to that day instead of today.
Test: the «نوبتها» breadcrumb links back to the appointment's date.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AppointmentsPage now reads an optional ?date=YYYY-MM-DD query param as its
initial selected date. AppointmentEditPage and AppointmentCreatePage navigate
back to /admin/appointments?date=<the appointment's day> on save (and the edit
page's back link carries the date too), so the user returns to the day they
were viewing instead of today.
Test: AppointmentsPage honors ?date= and fetches that day.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Multi-doctor clinic profile (clinic/admin) shows the doctor tabs for
multi-doctor management; clinic auto-selects the first doctor so the timeline
loads immediately (as in the reference). Admin keeps the «همه» tab.
- Independent doctor profile (doctor role, incl. clinic-scoped guest) shows no
tabs and only its own schedule — no clinic/doctor selection.
- Toolbar reordered to match the reference: date + service select
(«سرویس مورد نظر را انتخاب کنید...») + view toggle on the right; filter +
«افزودن نوبت» on the left. The in-toolbar doctor picker is removed (doctor
selection is via tabs); the personnel select is replaced by a service filter
bound to the existing itemUuid filter (client-side, no new endpoint).
Tests: added clinic-profile tests (tabs shown, no «همه», first doctor
auto-selected) and independent-doctor test (no tabs, service filter shown).
No API endpoints changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>