- Implemented TourProgressController to handle API endpoints for tracking guided tours seen by users.
- Created UserTourProgress entity to store the highest version of tours seen by each user.
- Developed UserTourProgressRepository for database interactions related to user tour progress.
- Introduced TourProgressService to manage business logic for marking tours as seen and retrieving seen maps.
- Added comprehensive tests for API endpoints and entity behavior to ensure functionality and data integrity.
The holiday model was already right — national holidays global, a per-tenant
override in both directions, per-doctor and per-resource exceptions — but
nothing could create a national holiday. The only writer was an import
command, so the calendar the whole product inherits from had no owner.
Three admin-only routes give it one. POST upserts, because `date` is unique
and re-sending a day should rename it rather than surface a raw database
error; PATCH takes only the title, because moving a date means a different
holiday. The system admin has no work environment, so the list endpoint now
returns the calendar with an empty `overrides` for that role instead of the
403 `pair()` would raise — the person who maintains the calendar has to be
able to read it.
Both holiday tabs — the doctor's and the resource's — now open with the
official calendar above their own exceptions, from one shared card rather
than two copies that would drift. Each row can be opted out of with a single
click, which is the existing holiday-override endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Updated CoverageRule and related entities to include franchise_percent instead of franchise_rials.
- Modified Appointment entity to carry supplementary insurance ID alongside base insurance.
- Implemented SessionBillingService to ensure finalized invoices for insured patient sessions.
- Created InvoiceFinalized event to trigger claims creation upon invoice finalization.
- Added BackfillMissingClaimsCommand to generate claims for finalized invoices without existing claims.
- Developed tests to validate the new functionality for supplementary insurance handling in appointments and claims.
- Changed franchise_rials to franchise_percent in tenant_insurances and tenant_service_coverage tables.
- Reset old rial values to 0/NULL as they are not convertible to percentage.
feat(command): add SeedInsuranceScenarioCommand for seeding insurance data
- Implemented a command to seed supplementary insurance contracts, patients, and claims for a specified doctor.
- Includes functionality for purging existing scenario data and generating new entries with predefined contracts and patient scenarios.
- Updated date handling in BlogSeoFields and ScheduleSection to use Tehran timezone utilities for consistency.
- Introduced `toTehranClockTime`, `tehranWallClockToUnix`, and `todayIso` functions for accurate date representation.
- Modified various components to utilize these new utilities, ensuring that date strings are correctly formatted and timestamps are accurately converted.
- Enhanced API documentation to clarify the handling of date fields, emphasizing the importance of server-local midnight.
- Added tests to verify that date overrides and holidays maintain the correct day without shifting due to timezone discrepancies.
- Refactored role metadata styles in UserDetailPage for consistency with design tokens.
- Replaced hardcoded avatar colors with a utility function to generate gradients based on user ID.
- Improved InfoCard component styles for better hover effects and accessibility.
- Removed deprecated color classes and adjusted background gradients for various components.
- Updated theme token tests to reflect the removal of deferred files and ensure compliance with design standards.
- Added new avatarColors utility file to manage avatar gradient definitions.
An appointment can now carry the insurance it is billed with: the service kind
(outpatient/inpatient) and the basic insurance. Confirming it no longer hands the
whole amount to the patient — the visit is split through BillingCalculator with the
coverage percent of that service kind, and the choice travels to the encounter and
the invoice built from it.
The enabled service kinds are a tenant-wide setting (all of that tenant's
insurances share it), so a tenant covering only one kind is never asked which one:
the panel resolves it the same way the server does.
- add tenant_service_category_settings + TenantServiceCategoryService, exposed on
the existing insurance-pricing endpoint (service_categories,
default_service_category); at least one kind must stay enabled
- add appointments.insurance_service_category / insurance_base_id with
AppointmentInsuranceService validating them against the tenant's own settings
and active contracts (basic only), accepted by PATCH and by confirm
- snapshot the kind on patient_sessions and invoices; the visit's coverage rule is
resolved per kind (services keep using their own ServiceItem.service_category)
- lib/insuranceShares becomes the single client-side mirror of BillingCalculator,
shared by the confirm modal, the appointment edit page and the session form
- surface the selection: confirm modal (with live shares), turns timeline chip,
appointment edit page, patient record service card and invoice summary
- the session form shows the insurance block whenever the tenant has an active
contract and prefills the patient's own insurance, so it can be changed
- fix: the confirm modal showed a zero visit price when the appointment had none —
it now falls back to the tenant's free-visit price like the server
- fix: useServiceCategories read one level too shallow, so Persian labels never
arrived and raw enum keys leaked into the contract summary
- fix: BlogsPage test asserted the public blogs endpoint after the page moved to
the admin one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Introduced new SEO fields (meta_title, meta_description, primary_keyword, secondary_keywords, faq, internal_links, external_links, reading_time, canonical_url, og_image) to the Blog entity.
- Added scheduling capability with a scheduled_at field to manage automatic publishing of blog posts.
- Implemented representative ownership through a foreign key representation_id in the Blog entity, allowing representatives to manage their own posts.
- Updated BlogController and RepresentationBlogController to handle new fields and ensure proper data handling for SEO and scheduling.
- Created BlogWriter service to encapsulate the logic for applying SEO and scheduling fields to blog entities.
- Added PublishScheduledBlogsMessage and its handler to manage the publishing of scheduled blogs.
- Implemented ScheduledBlogPublisher service to publish drafts whose scheduled_at has arrived, respecting review status.
- Created migration to update the database schema with new fields and constraints.
- Added tests to ensure the correct functionality of new features, including SEO fields, representative scope, and scheduled publishing.
- Implemented a helper function `displayDoctorName` to prepend "دکتر" to doctor names for consistent display across the application.
- Updated various components (InviteDoctorModal, DashboardPage, DoctorDetailPage, DoctorsPage, etc.) to utilize the new helper for rendering doctor names.
- Modified the DoctorFormPage to automatically add the "دکتر" title in the UI without requiring user input.
- Fixed the EditSpecialtyPicker component to allow multiple specialty selections, resolving a UI bug where only one specialty could be selected at a time.
- Ensured that the backend strips the "دکتر" title from the name during pre-registration and doctor creation processes.
- Added tests for the new functionality, including checks for title handling and specialty selection logic.
- Updated API documentation to reflect changes in name handling and display logic.
- Adjusted the calculation of payable amounts in PaymentStep to align with server logic, ensuring overpayments are handled correctly.
- Enhanced DetailsStep to include consumables in the itemized cost breakdown, ensuring consistency with patient share calculations.
- Updated tests for SessionPaymentPage to validate new behavior regarding overpayments and consumable listings.
- Modified PatientController to register SessionPayment correctly when settling sessions via wallet, preventing double charges.
- Refactored WalletService to remove outdated methods and ensure wallet transactions reflect the correct amounts after discounts.
- Improved accessibility in SearchableSelect component by adding aria labels and ensuring proper role attributes for screen readers.
- Updated styles to ensure minimum touch targets meet WCAG guidelines for mobile usability.
- Added support for 'partial' payment status in MyPaymentsPage and related components.
- Updated API responses to include 'paid_rials' and 'summary' for invoices.
- Introduced InvoicePaymentStatus service to derive payment status based on actual payments.
- Enhanced tests to cover new payment scenarios including partial payments and payment methods.
- Updated documentation to reflect changes in payment status and API responses.
- Implement PriceInput component tests to validate Persian and Arabic numeral handling, input formatting, and controlled behavior.
- Create ServiceDetailPage component with detailed service information, including pricing, insurance coverage, and editing capabilities.
- Add API tests for service item detail retrieval and coverage synchronization with insurance contracts.
- Ensure proper error handling and user feedback for service item retrieval and coverage management.
Users typing on a Persian keyboard produced two distinct failures. Fields with
type="number" silently returned an empty string — the browser rejects Persian
digits, so the value was lost and saved as empty or zero. Text fields passed the
Persian characters straight through to the database, where a mobile stored as
۰۹۱۲… never matches 09… again. The secretary form hit the second case with no
validation at all.
Frontend:
- Adds digitsOnly() and the national-code schemas to lib/utils, plus lib/forms
with numericField()/latinDigitsField() wrappers for React Hook Form fields.
- Converts every type="number" input to type="text" inputMode="numeric" with
digit normalization; none remain. Fields that legitimately carry non-digits
(sheba, landline) only get the digits translated, keeping IR and separators.
- Points the patient national-code and mobile schemas at the shared normalizing
schemas, which accept Persian input instead of rejecting it.
- Drops two duplicate local digit converters in favour of the shared helper.
Backend:
- Adds NumericFieldNormalizerSubscriber, translating digits in whitelisted
numeric keys of JSON request bodies under /api/v1/ before controllers run, so
nobat724_front and clinic-pro-tauri are covered too. Translation only — no
characters are stripped, non-string values and other keys are untouched.
Three component tests asserted on role="spinbutton" and numeric input values;
both are properties of type="number", so they were updated to match the new
text inputs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The invitation flow never created an account for the invitee. accept() only
looked up an existing doctor by mobile, so for a brand-new invitee it marked
the invitation accepted and burned the token while leaving doctor_id NULL —
no login, no clinic link, and every doctor-facing endpoint 404ing afterwards.
- invite/accept now provision the users + doctors pair, claim the profile on
accept, link it to the clinic, and SMS generated credentials when the user
has no password. Existing passwords are never overwritten.
- accept runs in one transaction so an invitation can no longer be marked
accepted without its doctor profile and clinic link.
- changeStatus accepts `pending`, refreshing the token and re-sending the SMS
so reactivating a suspended invitation yields a link that actually works.
Answered invitations are rejected with 409.
- DELETE returns 200 with the standard envelope instead of a bodyless 204,
which made the admin panel show a false error toast; api.ts also stops
calling res.json() on empty responses.
- The clinic-doctors settings page sent the active context uuid as the clinic
uuid, so users holding both a doctor and a clinic context got 404 on every
invitation action. It now always resolves the clinic context.
- Adds app:invitations:repair to fix invitations already left orphaned.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The «اطلاعات پرونده» form only exposed basic insurance («نوع بیمه»). Add a
«بیمه تکمیلی» select next to it, wired to the supplementary insurances from
insurance-pricing. Backend PATCH /api/v1/patient/{uuid} already accepts
supplementary_insurance_id (UserProfile entity + controller) — frontend-only:
schema field, form select, profileToFormValues/formValuesToPayload mapping,
PatientProfileUpdate type, and the supplementary option passed from both the
case-file info tab (PatientDetailPage) and MyPatientsPage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild the /admin/insurance-pricing contracts UI to the Figma "مدیریت بیمه"
design and inject the coverage/franchise/ceiling fields the design omitted.
Backend:
- Add contract-level `kind` column to TenantInsurance (basic|supplementary),
defaulting to the catalog type; migration Version20260715093358.
- POST/PATCH /billing/tenant-insurances now accept effective_from,
effective_to, kind; PATCH also toggles is_active without clobbering the
user-set effective_to (unlike DELETE/deactivate).
- List returns the latest version of every insurance (active + inactive) via
TenantInsuranceRepository::findLatestByTenant, for the فعال/غیرفعال toggle.
Frontend:
- New InsuranceModal (ui/Modal + SearchableSelect + PersianDateInput) with the
seven fields; submit "ثبت بیمه".
- TenantInsuranceContracts rebuilt: header + search box, desktop table
(ردیف/نام/کد/نوع/وضعیت/عملیات) and mobile cards, status toggle -> PATCH.
- utils: isoToUnix/unixToIso helpers for contract dates.
Tests: TenantInsuranceContractApiTest (create/edit/toggle/list, 5 cases),
InsuranceModal + TenantInsuranceContracts vitest suites, docs/api updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend:
- Add profile columns field_of_study, province_id, city_id, postal_code,
referral_source (UserProfile + migration).
- Extend PATCH /api/v1/patient/{uuid} to persist all demographic fields
and return them in the patient profile payload.
- Support editable mobile (login identifier): validation, uniqueness,
User.setMobileNumber, new ERR_PROFILE_002.
- Update docs/api/patient.md.
Frontend:
- New reusable Input, Field, and PatientRecordInfoForm (RHF + Zod).
- usePatient/useUpdatePatient hooks and patientForm mapping helpers.
- Extend the existing "info" tab in MyPatientsPage to the full field set
via the shared form (province/city/insurance options, Jalali date).
Tests: Patient entity + PATCH integration (PHPUnit); form, hooks, and
mapping helpers (Vitest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Added support for sending OTP messages using Kavenegar's VerifyLookup method, ensuring compliance with specified token formatting and template usage.
- Updated OtpService to handle new template parameters and fallback mechanisms.
- Introduced ImageCropModal component for cropping images with a user-friendly interface.
- Created utility function for cropping images and generating downloadable files.
- Implement tests for Pagination, StatusBadge, ConfirmDialog, and MobileInput components.
- Add tests for useSubscription, usePaymentConfig, and usePwaInstall hooks.
- Create tests for API requests in the api module, including success and error handling.
- Add utility function tests for formatting and validating Iranian mobile numbers.
- Implement tests for BlogFormPage and BlogsPage to validate form submissions and data fetching.
- Add tests for LoginPage to ensure proper validation and state management.
- Create tests for authStore and uiStore to validate state management and functionality.
- Set up Vitest configuration and testing utilities for consistent testing environment.
- Implemented InviteDoctorModal component for inviting doctors to clinics.
- Updated ClinicDashboard to include a button for inviting doctors and handle modal state.
- Added createAppointment API endpoint in AdminApiController for scheduling appointments.
- Enhanced ClinicInvitationController to check user access when inviting doctors.
- Updated MyAppointmentsController to ensure unique appointment records.
- Added seed_test_data.php for populating test data including doctors, clinics, and appointments.
- Refactored styles to include new appointment status badges and updated font imports.
- Updated security configuration to include new API route for file uploads.
- Added new endpoints in AdminApiController for user statistics, toggling user status, updating user roles, and managing user details.
- Implemented doctor statistics and management endpoints, including toggling doctor status and creating new doctors.
- Enhanced user listing with filtering options for roles and status.
- Introduced DoctorFormPage component for adding new doctors with specialties selection.
- Integrated react-leaflet for mapping functionalities and added necessary dependencies.
- Updated package.json and package-lock.json to include new dependencies.
- Updated UsersPage to fetch users from the new admin endpoint.
- Enhanced user data structure to include 'name' and modified rendering logic.
- Added RepresentationDetailPage for detailed representation management.
- Created AdminApiController to handle user and representation CRUD operations.
- Implemented pagination and search functionality for users and representations.
- Updated user and representation data models to reflect new API structure.
- Implement SettlementsPage for managing settlement requests with approval and rejection functionalities.
- Create SmsPage for handling SMS templates, including creation, approval, rejection, and logging.
- Add UserDetailPage to display detailed information about users.
- Develop UsersPage for listing users with search, view, edit, and delete options.
- Introduce new types for User, SmsTemplate, SmsLog, and Settlement to support the new features.