- Implemented BlogBodySanitizer to clean HTML content before saving articles, ensuring security against XSS attacks.
- Added tests for BlogBodySanitizer to verify that unsafe tags and attributes are stripped from the content.
- Introduced ApiLeastPrivilegeTest to ensure that unauthorized users cannot access sensitive API routes, maintaining strict access control.
The three hardcoded resource lists in the admin panel are gone. MySecretariesPage,
SecretariesPage and DoctorPermissionsModal now render from
GET /api/v1/permission-catalog, so a resource added to the backend registry shows
up in all of them with no frontend change. Each has a test that proves exactly
that by adding a resource to the mock and asserting it renders.
SecretaryPermissions was an interface with a field per resource, which made
"dynamic" impossible in TypeScript — every new resource would have been a compile
error. It is now an open map. Only two files consumed it.
The borrowed gates are corrected:
- five resource pages moved off appointment_settings onto their own 'resources'
- treatment-cases moved off appointments onto 'treatment'
- service-categories moved onto 'services', which is what ServiceCatalogController
actually manages (categories, item groups, service relations) — not resources
TreatmentCaseController had no permission gate at all, only IS_AUTHENTICATED_FULLY,
so any secretary could read and edit treatment cases. All seven of its actions are
now gated on treatment view/update.
ResourcePermissionTrait takes the resource from an overridable method instead of
hardcoding appointment_settings. HolidayController overrides it back, since the
holidays page really is appointment settings. The booking gate keeps its
appointments.view fallback so a secretary who may book is not blocked by a
resource-config permission.
Defaults were picked to preserve today's effective access, so no role gains or
loses a page from this move.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Updated DoctorDetailPage, MySecretariesPage, RecordNumberSettingsPage, RepresentationsPage, ResourcePoolsPage, ResourceTypesPage, SecretariesPage, SecretaryDetailPage, SettingsPage, SkillsPage, SmsWalletPage, and TagsSettingsPage to use the new Switch component instead of native checkboxes.
- Enhanced accessibility by ensuring the Switch component uses appropriate roles and labels.
- Added tests for the new Switch component to ensure functionality and accessibility compliance.
- Updated styles to accommodate the new Switch component design.
- Refactor multiple admin pages (BlogsPage, ClinicsPage, DoctorsPage, etc.) to utilize the new useUrlState hook for managing pagination, search, and filter states via URL.
- Ensure that the state persists in the URL, allowing users to return to the same state when navigating back from detail pages.
- Update relevant components to handle state changes appropriately and maintain clean URLs by removing default values.
- Add SlotPicker component for selecting appointment slots based on availability.
- Create tests for useUrlState to validate its functionality and ensure correct behavior when interacting with the URL.
- Update API documentation to reflect changes in appointment creation and slot selection processes.
- Introduced `online_share_enabled` and `online_share_percent` fields in the `doctor_secretaries` table to manage secretary shares from online appointments.
- Added `bank_account` field in the `profiles` table to store user-level IBANs for settlements.
- Created `secretary_earnings` table to track earnings per secretary from online appointments, including a foreign key relationship with `financial_breakdowns`.
- Implemented `SecretaryEarning` entity and repository for managing secretary earnings.
- Developed `SecretaryShareResolver` service to determine which secretaries earn from online payments.
- Added `UserIbanResolver` service to handle user IBAN retrieval and management.
- Created `HasIbansTrait` for entities to manage IBANs in a JSON format.
- Implemented tests for secretary earnings and API endpoints for managing secretary shares and IBANs.
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>
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>
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>
- 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.
- Removed the "دکتر" prefix from doctor names in various components and API responses to ensure consistency and clarity.
- Updated the AppointmentDetailPage, CommentsPage, DashboardPage, RatingsPage, SecretariesPage, and other relevant files to reflect the changes in doctor name formatting.
- Adjusted API documentation to align with the new naming conventions.
- Implemented validation to prevent the creation of clinics without a name and restricted users to a single clinic.
- Added tests to verify that doctor names are stored without titles and that clinic creation adheres to the new validation rules.
- Refactored PaymentsPage, RatingsPage, RepresentationDetailPage, RepresentationsPage, SecretariesPage, SettlementsPage, SmsPage, UserDetailPage, and UsersPage to use consistent class names for styling.
- Updated button styles to use new utility classes for primary, secondary, and danger buttons.
- Enhanced dark mode support across various components by adjusting text and background colors.
- Introduced new utility classes for form inputs, labels, and info rows to standardize styling.
- Implemented Zustand for persistent UI state management, including dark mode toggle functionality.
- Updated CSS to include new styles for skeleton loading and animations.
- Added optional dependencies for improved compatibility with different platforms.
- 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.