Commit Graph
9 Commits
Author SHA1 Message Date
hamedandClaude Opus 4.8 43db753942 fix(secretary): allow insurance pages; add grantable subscription resource
Insurance pages redirected to the dashboard: the insurance-pricing/claims
routes never listed `secretary`, so RoleRoute bounced a secretary who had
insurances.view and saw the menu item. Added secretary + permission
['insurances','view'] to both routes; also gated my-financial with
['payments','view'] for consistency.

«خرید اشتراک» was owner-only with no permission toggle, so it could not be
granted. Added a `subscription` secretary resource (view/create) end-to-end:
- entity DEFAULT_PERMISSIONS + SecretaryPermissions type + both secretary forms.
- backend: SubscriptionController::my (view) and trial (create),
  PaymentController::initiateSubscription (create). resolveEntity in
  SubscriptionController was already secretary-aware.
- frontend: subscription + subscription/success routes accept secretary +
  permission; settings navs gate «خرید اشتراک» by ['subscription','view'].

Tests: subscription denied-by-default / allowed-when-granted. docs/api
secretary.md updated (resource list, enforcement map, JSON example).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 18:21:53 +03:30
hamedandClaude Opus 4.8 54c8b008bf fix(secretary): settings menu structure, clinic timeline access, patient delete gate
Three reported secretary-access bugs.

1) Settings menu structure. Phase B flat-listed staff/discounts/sms/tags/
   appointment_settings/clinic_doctors in the secretary's main sidebar. Mirror
   the doctor/clinic layout instead: only inventory + services stay in the main
   «مدیریت» nav; the rest live under a single «تنظیمات» entry
   (→ /admin/account-settings). Made both settings navs permission-aware for
   secretaries: SETTINGS_MENU (menuForRole now takes `can`) and
   PurchaseSubscriptionSidebar filter by a per-item `perm`/`alwaysOpen` instead
   of role only, so a secretary sees exactly their permitted settings pages and
   owner-only items (subscription, secretary-management) stay hidden.

2) Clinic secretary appointment timeline. AppointmentsPage treated a
   clinic-scoped secretary as a single-doctor profile: the doctor list was
   fetched/shown only for isClinic/isAdmin, so no doctor tabs, timeline, or
   booking. Now a clinic-scoped secretary is multi-doctor: fetches the doctor
   list, shows tabs, auto-selects the first doctor. The list comes from a new
   authenticated endpoint GET /api/v1/my/clinic-doctors returning only the
   secretary's ASSIGNED doctors — /clinic/doctor-list is on the public (no-JWT)
   firewall and cannot scope by user, so it would have leaked unbookable doctors.

3) Patient record delete. The `patients.delete` toggle was dead: every record
   delete (note/medical-record/attachment/call/message) was gated as
   `patients.update`. Mapped them to `patients.delete` so the toggle is honored
   and delete is controllable separately from edit.

New SecretaryAccessChecker::assignedClinicDoctorIds. Tests: doctor-list scoping,
patients.delete separation (denied/allowed). docs/api secretary.md +
appointment.md updated. Backend 286 + frontend 25 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 18:01:32 +03:30
hamedandClaude Opus 4.8 e8bf2ce9b1 feat(secretary): grant staff/discounts/sms/appointment_settings/clinic_doctors (phase B)
Extends the secretary permission system to five previously owner-only modules,
so a clinic/doctor can delegate each page to a secretary. All were unreachable
by secretaries before (role-based tenant resolution returned "unknown" → 403).

New permission resources (default-deny, three-place add: entity default,
SecretaryPermissions type, both MySecretariesPage + admin SecretariesPage):
staff, discounts, sms, appointment_settings (view/update only), clinic_doctors
(clinic-only — hidden from independent doctors via `clinicOnly` section filter).

Backend enforcement (SecretaryAccessChecker, three new reusable helpers):
- resolveOwnerEntity(): owner pair from active context — used by StaffController,
  DiscountController, SmsWalletController (now secretary-aware resolveEntity).
- canForDoctor(): per-doctor-scoped check (assigned doctor + toggle) — wired into
  AppointmentSettingsController::denyDoctorAccess.
- canForClinic(): clinic-scoped check — wired into ClinicController::detachDoctor,
  ClinicDoctorPermissionController (view/update), ClinicInvitationController
  (create/view/update/delete). clinic_doctors is clinic-context only.
Guards run ahead of any subscription gate; non-secretary roles pass unchanged.

Frontend:
- RoleRoute: staff, discounts, sms-wallet, appointment-settings (doctor+clinic
  variants), settings/clinic-doctors routes accept secretary + permission gate.
- Sidebar (secretary branch): five new items gated by can(); appointment_settings
  route follows active scope; clinic_doctors only in clinic scope.

Tests: SecretaryResourceEnforcementTest — denied-by-default + allowed-when-granted
for all five (18 total). Sidebar.test — B-resource gating + clinic_doctors scope
rule. docs/api/secretary.md resource list, enforcement map, JSON example updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 17:29:51 +03:30
hamedandClaude Opus 4.8 9d46577181 feat(secretary): add services permission resource + panel gating (phase A)
Secretaries could reach neither the services module (EntityContextResolver
does not recognise a secretary as clinic owner, so they resolved to
`unknown` → 403) nor had any toggle to grant it. Add `services` as a
first-class secretary permission resource, enforced end-to-end.

Backend
- DoctorSecretary::DEFAULT_PERMISSIONS: new `services` resource (default-deny).
- SecretaryAccessChecker::resolveOwnerEntity(): reusable owner (clinic/doctor)
  resolution from the secretary's active context, for controllers whose data
  is fetched by [entityType, entityId] and whose generic resolver is not
  secretary-aware.
- ClinicServiceController: resolveEntity() is now secretary-aware; every action
  (sections, items, tariffs — 13 total) guards with `services` view/create/
  update/delete via denyUnlessGranted, ahead of the subscription gate.

Frontend
- SecretaryPermissions type + MySecretariesPage + SecretariesPage: `services`
  section so owners can grant it.
- Sidebar (secretary branch): services / inventory / tags menu items gated by
  can(resource, 'view').
- RoleRoute: a secretary now needs the page's `permission` to open it (direct
  URL entry included); clinic-services, inventory, tags-settings routes accept
  secretary + permission gate.

Tests
- SecretaryResourceEnforcementTest: services denied-by-default, allowed-when-
  granted, create-denied-while-view-granted.
- Sidebar.test: secretary menu gating for services/inventory/tags.

Docs: secretary.md + clinic-services.md updated with the `services` resource
and the resolveOwnerEntity note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 17:10:54 +03:30
hamed 5c4976d65f feat: Implement secretary permissions enforcement across multiple resources
- Added SecretaryAccessChecker to manage resource access for secretaries.
- Integrated permission checks for payments, inventory, and tags in relevant controllers.
- Updated PaymentController and PaymentMethodController to enforce secretary permissions.
- Enhanced TenantTagController to check permissions for tag management actions.
- Introduced tests for secretary resource enforcement, ensuring proper access control.
- Updated DoctorSecretary entity to include inventory and tags permissions.
- Created a comprehensive audit document for secretary permissions coverage and enforcement.
- Fixed potential crashes in SecretaryDashboard when rendering without doctor data.
2026-07-23 16:36:35 +03:30
hamed b659b84a7f feat(secretary): implement grouping of secretaries by doctor and sync profile data across links 2026-07-18 10:54:37 +03:30
hamed 1779e0d6de feat(secretary): implement multi-doctor assignment for clinic secretaries
- Added functionality to assign a single secretary to multiple doctors within a clinic, allowing for scoped access to appointments.
- Introduced `SecretaryService` to handle the logic for assigning and syncing doctors for a secretary.
- Updated `SecretaryController` to support multi-doctor assignment via new endpoints and modified existing ones.
- Enhanced `DoctorSecretary` entity to include secretary UUID in its serialized output.
- Implemented repository methods to facilitate the retrieval and management of doctor-secretary relationships.
- Adjusted appointment filtering in `MyAppointmentsController` to ensure secretaries only see appointments for assigned doctors.
- Created tests to validate the new multi-doctor assignment functionality and appointment access restrictions.
- Updated frontend components to support multi-select for doctors in the secretary management UI.
2026-07-18 08:49:04 +03:30
hamedandClaude Opus 4.8 89e23a2c0d feat: port secretaries tab from tauri to admin my-secretaries page
- Redesign MySecretariesPage pixel-perfect to clinic-pro-tauri (active/previous
  tabs, desktop table, mobile cards, add/edit/view modal with permission
  accordions, deactivate confirm)
- Permission sections based on existing admin pages (appointments, patients,
  payments, insurances, addresses, clinic_info)
- Extend DoctorSecretary with national_code + address columns (+migration);
  wire create/update in SecretaryController; add patients/payments to
  DEFAULT_PERMISSIONS
- Extend Secretary/SecretaryPermissions types; update admin SecretariesPage
- Backend + frontend tests; update docs/api/secretary.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 12:23:17 +03:30
hamedandClaude Opus 4.8 ccb71e4371 perf(secretary,billing): kill N+1 in secretary + claims lists (M8, M9)
M8: DoctorSecretary::toArray() lazy-loaded secretary/doctor/clinic per row;
fetch-join them in findByDoctorScope/findByClinic (shared listWithRelations()).

M9: enrichClaims() lazy-loaded each claim's items collection and called
insuranceRepo->find() per claim. Fetch-join items in findByTenant (Paginator,
fetchJoinCollection) and batch-fetch insurance names once.

Regressions (query count constant vs row count): SecretaryListNPlusOneTest,
ClaimsListNPlusOneTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:43:35 +03:30