- Added a new 'category' field to the InventoryItem entity and updated the database schema.
- Replaced free-text input for 'unit' and 'category' with select dropdowns in the AddItemModal.
- Introduced a new API endpoint to fetch metadata for units and categories.
- Updated inventory filtering logic to use the new 'category' field instead of 'consumable'.
- Enhanced validation for item creation and updates to ensure valid unit and category values.
- Updated tests to cover new functionality and ensure proper validation.
Show the قیمت (تومان) field with thousand separators as the user types
(e.g. 1200000 → 1,200,000). Stored value stays digit-only, so the
Toman→Rial conversion on submit is unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The «عملیات» dropdown was absolutely positioned inside the table container,
which has overflow:hidden for its rounded corners, so the menu was clipped
below the table. Render it through a Portal with fixed positioning computed
from the trigger's rect (RTL-aligned to the trigger's right edge), tracking
scroll/resize while open.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a per-tenant (doctor/clinic) Inventory domain and admin page, ported
from clinic-pro-tauri /inventory (which was static/mock) into a real feature.
Backend (src/Inventory/):
- Entities InventoryItem, InventoryPackage, InventoryPackageItem, scoped via
entity_type/entity_id like TenantTag. Item status is derived, package total
and availability derived at read time.
- InventoryService (stats, package assembly, availability), thin
InventoryController with CRUD for items and packages + categories endpoint.
- Migration + docs/api/inventory.md + functional tests (10 tests, 42 assertions).
Frontend (assets/admin/):
- InventoryPage with two tabs (کالاهای مصرفی / پکیج), stat cards, items table
(desktop + mobile cards), packages accordion, add/edit item and package
modals, search + category filter — pixel-matched to the tauri source.
- useInventory hook (TanStack Query), route + sidebar link for doctor/clinic.
- Vitest coverage (real data, empty state, modal, packages tab).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Created a new JSON file containing nodes and edges representing the structure of the insurance API documentation.
- The nodes include various API endpoints, their labels, file types, and source locations.
- The edges define relationships between the nodes, indicating how they are connected within the documentation.
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>
The custom inline overlay was mounted inside the SettingsLayout grid, so the
fixed positioning resolved against a transformed ancestor and the modal
rendered top-aligned and full-width. Use the shared Modal (portal to body)
for a correctly centered card; keep the tauri-styled form body + permission
accordions as its children.
Remove پرسنل (/admin/staff) and منشی ها (/admin/my-secretaries) from the
clinic and doctor main sidebars; surface them under the settings menu instead
(PurchaseSubscriptionSidebar + SETTINGS_MENU). Wrap StaffPage in SettingsLayout
so it renders inside the settings shell like MySecretariesPage already does.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add per-clinic payment methods (bank accounts + POS/card-reader devices)
under the "مدیریت پرداخت" settings tab at /admin/my-financial, ported from
clinic-pro-tauri's mock-only PaymentManagement tab into a real persisted
feature. These records are referenceable (by uuid) from patient invoices to
record which method a service payment was made with.
Backend (new src/PaymentMethod domain):
- BankAccount + Pos entities, repositories, PaymentMethodService (validation,
ownership scoping, create/update/toggle logic).
- Thin PaymentMethodController exposing /api/v1/my/payment-methods/{bank-accounts,pos}
(GET/POST/PUT + PATCH .../status), guarded to clinic/doctor/secretary/admin.
- Migration for bank_accounts + pos_devices tables.
- Functional tests (success + validation/404/403 + empty boundaries).
- docs/api/payment-method.md.
Frontend:
- Replace MyFinancialPage content with the payment-management UI (two tabs,
tables, add/edit modals, status toggle) using the admin design system.
- usePaymentMethods hook (TanStack Query) + presentational components.
- Update page test to cover tabs, data, empty state and the add modal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removed the maxWidth:1060 centered wrapper on PatientDetailPage that
boxed the page; tauri's FilesServices renders full-width in a plain Box.
Added a regression test asserting the root wrapper has no max-width cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the پروندهها header to mirror tauri files/head + files/inputs:
- title on its own row (font-bold, #525252, 16/18/20px).
- search field: 48px tall, #FAFAFA bg, #EFEFEF border, rounded-[6px], with a
#5559CE 40px search-icon box (SearchHeaderP).
- view toggle with the exact tauri SVGs (PatientsGridView table / Patients
CategoryView card), active bg #f4f5fd, #E0E0E0 border.
- filter button as a 62x48 #5559ce-bordered box (TurnsFilter icon).
- تشکیل پرونده as a 48h/137w #5559ce button (AddTurn icon + label).
Ported icons verbatim into components/icons/FilesToolbarIcons.tsx.
Frontend only. Tests + tsc + build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- default view is now the card grid (was table), matching tauri /files
(viewMode='card').
- rebuild the patient card to mirror tauri files/list/CardView exactly:
avatar-in-circle + name + ⋮ actions menu (view/edit) header, شماره پرونده /
موبایل rows, برچسبها footer with the inline tag popover. Same Tailwind
classes/colors/spacing (rounded-[6px], #EDEDED/#E0E0E0 borders,
grid md:grid-cols-4 gap-[12px]).
- per-view page size like tauri: 16 for card, 12 for table; reset page on
view switch.
- empty state text 'بیماری یافت نشد'.
Frontend only; no API change. Tests updated (default card, ⋮ menu, table
toggle, tag popover, filter apply) — all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the remaining pieces of tauri /files list into /admin/patients:
- inline tag assignment: the برچسبها cell (table + card) opens a popover to
assign/remove tenant tags without leaving the list. Uses existing endpoints
(GET /api/v1/tenant-tags + PATCH /api/v1/patient/{uuid} { tags:[uuid] }).
New component assets/admin/components/PatientTagsCell.tsx.
- advanced filter modal (PatientsFilterModal): admission date range, insurance,
service status (pending/completed), has-debt, gender, tags — wired to the
list query with an active-filter badge on the button.
Backend: GET /api/v1/patients gains tags/gender/insurance_id/admitted_from/
admitted_to/service_status/has_debt filters via a shared applyFilters() on
PatientRecordRepository (findByEntity + countByEntity stay consistent). Debt
and service status derive from unpaid sessions (payment_method='pending'),
documented in docs/api/patient.md.
Tests: tests/Patient/PatientListFilterTest.php (5) + PatientsListPage tag-popover
and filter-apply tests. Pre-existing LoginPage.test failures are unrelated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Align /admin/my-payments with the tauri /payments source (per user): the list
is now a flat, newest-first list of the tenant's recorded invoices — one row
per invoice — instead of the per-patient aggregation built from Figma.
Backend:
- replace InvoiceRepository::patientPaymentSummary aggregation with
tenantInvoices/countTenantInvoices (flat, joins patient name/national code).
- InvoiceService::patientPaymentList → tenantInvoiceList.
- BillingController: GET /api/v1/my/billing/patient-payments →
GET /api/v1/my/billing/payments returning
{ invoice_uuid, patient_uuid, patient_name, national_code, issued_at,
amount_rials, status } rows.
- node-2 patient invoices endpoint unchanged.
Frontend:
- useMyPayments: usePatientPayments → usePayments (flat PaymentRow).
- MyPaymentsPage columns match tauri DetailT: row #, avatar+name, national
code, date-time, amount paid, مشاهده (no status column); 'اضافه کردن بیمار'
links to /admin/patients/new. Filters (national code / status / Jalali date
range) kept.
Tests + docs/api/billing.md updated. Intentionally omitted tauri extras:
mobile Cards view and the advanced ModalFilter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port two nobat724 Figma screens into the admin SPA for the doctor/clinic
tenant panel:
- node 1 — لیست پرداختها (/admin/my-payments): per-patient payment summary
(invoice count, paid, remaining, derived status paid/unsettled/unpaid),
filters by national code / status / Jalali date range, pagination.
- node 2 — پرداختهای ثبتشده (/admin/my-payments/:patientUuid): a patient's
recorded invoices with patient header, service title, total, status badge,
and an expandable per-invoice item breakdown.
Backend (App\Billing):
- InvoiceRepository::patientPaymentSummary/countPatientPaymentSummary — DQL
aggregation grouped by patient record (arbitrary join Invoice→PatientRecord
→User), draft/void excluded, derived-status HAVING filters.
- InvoiceRepository::invoicesForPatient/count + InvoiceService methods that
shape rows and derive status.
- BillingController: GET /api/v1/my/billing/patient-payments and
GET /api/v1/my/billing/patients/{patientUuid}/invoices (thin, resolveEntity,
tenant-scoped, 403/404). Invoice::getIssuedAt / InvoiceItem::getTitle added.
- docs/api/billing.md documents both endpoints.
Frontend: useMyPayments hooks, MyPaymentsPage, MyPaymentDetailPage, routes in
App.tsx (doctor/secretary/clinic, blockClinicScope) and a sidebar entry.
Persian strings hardcoded per existing admin convention (no i18n infra).
Tests: tests/Billing/PatientPaymentsTest.php (8), useMyPayments + both page
tests (11). Note: pre-existing LoginPage.test failures are unrelated (proven
by stashing this change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- revert the previous merge of insurance coverage into the service edit
modal (per user): 'پوشش بیمه' is again a ⋮ menu action opening its own
ServiceInsuranceModal, kept within the services page
- delete the inline ServiceInsuranceSection; restore ServiceInsuranceModal
- fix ⋮ dropdown position: left:8 → insetInlineStart:8 so the menu anchors
under the ⋮ button (which sits at inline-start/right in RTL) instead of
the opposite side
- keep the --primary-subtle → --primary-soft token fix in the modal
- test: assert the ⋮ menu exposes ویرایش / تعرفههای سالانه / پوشش بیمه
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- extract ContractCard + contracts list from ServiceInsuranceModal into a
new inline ServiceInsuranceSection (no dialog wrapper; buttons type=button
so they don't submit the parent service form)
- ClinicServicesPage: drop the separate 'پوشش بیمه' menu action and its
standalone modal; render coverage inline under the service fields in the
edit modal (edit only — needs the item uuid; create shows a hint)
- delete now-unused ServiceInsuranceModal.tsx
- fix undefined --primary-subtle token → --primary-soft
- test: coverage renders inline inside the service edit modal
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- TagsSettingsPage modal: replace native color input with 4 preset
swatches, add 'وضعیت برچسب' active toggle, update labels and submit
button to mirror tauri AddPurchaseSubTabModal
- TenantTagController::create now honors an optional 'active' flag
(defaults true, non-breaking for existing callers)
- tests: backend active-flag case, frontend preset-color + inactive case
- docs/api/tag.md: document the new create 'active' field
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SettingsLayout rendered its own role-gated aside while the subscription page
rendered PurchaseSubscriptionSidebar, so /admin/subscription and other settings
pages (sms-wallet, ...) showed two different settings menus. Make SettingsLayout
render the shared PurchaseSubscriptionSidebar and have SubscriptionPage use
SettingsLayout too, so every settings page shows one identical menu. menuForRole
/ SETTINGS_MENU are kept for the mobile settings list (SettingsMenuPage).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
For the doctor and clinic roles, collapse the subscription section into one
'تنظیمات' item (Cog icon) pointing at /admin/subscription, matching the tauri
main sidebar. Remove the items that now live in the in-page settings submenu
(insurance-pricing, clinic-services, sms-wallet) from the main nav so they are
not duplicated. Secretary (no settings page) is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Give the subscription sidebar its own nav list copied from clinic-pro-tauri's
PurchaseSubscription navItems (order and labels), dropping 'مدیریت پزشک' (it
belongs in the main nav). Items are shown ungated to mirror the source; the
'تنظیمات' (security) section has no doctor/clinic route yet so it renders
disabled. SETTINGS_MENU / SettingsLayout (other pages) are left untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flip the settings menu to dir=rtl with right-aligned title, search and rows so
the Persian labels read right-to-left.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The page wrapped its grid in a second maxWidth:1180/margin:auto on top of the
shell's already-centered .content (max-width 1480), leaving a ~150px gap
between the settings menu and the main navigation. Make the page full-width so
the settings sidebar sits flush against the main nav; trim the sidebar column
to 248px to match the app nav width.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace wrapping flex (maxWidth cap) with a fills-the-row grid
(repeat(auto-fit, minmax(230px, 1fr))) so the three cards sit in one row
and grow to the content width — removes the empty gap and stray wrap
- Move the most-popular highlight to the professional (top) plan, matching
clinic-pro-tauri
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>