### COMMITS (4f15ef4..HEAD) f0b2d08 fix(clinic): م0 — gate settings on existing core.settings.view (not unseeded clinic.settings.manage) 47938ca fix(clinic): م0 — permissionGuard on all unguarded clinic routes + narrow settings guard ### DIFF STAT .../clinic-layout/clinic-layout.component.ts | 5 ++- src/app/features/clinic/clinic.routes.ts | 38 +++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) ### FULL DIFF (-U10) — EXCLUDING config.json diff --git a/src/app/features/clinic/clinic-layout/clinic-layout.component.ts b/src/app/features/clinic/clinic-layout/clinic-layout.component.ts index 925b7c3..9b4ad9a 100644 --- a/src/app/features/clinic/clinic-layout/clinic-layout.component.ts +++ b/src/app/features/clinic/clinic-layout/clinic-layout.component.ts @@ -152,22 +152,25 @@ const CLINIC_NAV: ClinicNavGroup[] = [ { labelKey: 'NAV.CLINIC_REPORTS', route: '/clinic/reports', icon: 'pi pi-chart-bar', permissions: ['clinic.reports.'], }, { labelKey: 'NAV.CLINIC_SETTINGS', route: '/clinic/settings', icon: 'pi pi-cog', - permissions: ['clinic.'], + // Matches the route guard: the settings screen uses /core/settings, + // governed by core.settings.view/.manage. Was over-broad `clinic.`. + permissions: ['core.settings.view'], }, + ], }, ]; @Component({ selector: 'app-clinic-layout', standalone: true, imports: [CommonModule, RouterModule, TranslateModule], templateUrl: './clinic-layout.component.html', styleUrl: './clinic-layout.component.scss', diff --git a/src/app/features/clinic/clinic.routes.ts b/src/app/features/clinic/clinic.routes.ts index 2bb405f..5bb843e 100644 --- a/src/app/features/clinic/clinic.routes.ts +++ b/src/app/features/clinic/clinic.routes.ts @@ -3,20 +3,22 @@ import { permissionGuard } from '../../core/guards/auth.guard'; import { ClinicLayoutComponent } from './clinic-layout/clinic-layout.component'; export const CLINIC_ROUTES: Routes = [ { path: '', component: ClinicLayoutComponent, children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', + canActivate: [permissionGuard], + data: { permissions: ['clinic.dashboard.view'] }, loadComponent: () => import('./dashboard/clinic-dashboard.component').then((m) => m.ClinicDashboardComponent), }, { path: 'appointments', canActivate: [permissionGuard], data: { permissions: ['clinic.appointment.view'] }, loadComponent: () => import('./appointments/clinic-appointments.component').then( (m) => m.ClinicAppointmentsComponent @@ -24,115 +26,149 @@ export const CLINIC_ROUTES: Routes = [ }, { path: 'booking', canActivate: [permissionGuard], data: { permissions: ['clinic.appointment.create'] }, loadComponent: () => import('./booking/create-visit.component').then((m) => m.CreateVisitComponent), }, { path: 'schedule', + canActivate: [permissionGuard], + data: { permissions: ['clinic.schedule.view'] }, loadComponent: () => import('./schedule/schedule-editor.component').then((m) => m.ScheduleEditorComponent), }, { path: 'encounter/:id', + canActivate: [permissionGuard], + data: { permissions: ['clinic.encounters.view'] }, loadComponent: () => import('./encounter/consultation.component').then((m) => m.ConsultationComponent), }, { path: 'doctors', canActivate: [permissionGuard], data: { permissions: ['clinic.doctors.view'] }, loadComponent: () => import('./doctors/clinic-doctors.component').then((m) => m.ClinicDoctorsComponent), }, { path: 'doctor-grades', + canActivate: [permissionGuard], + data: { permissions: ['clinic.doctor-grades.view'] }, loadComponent: () => import('./doctor-grades/doctor-grades.component').then((m) => m.DoctorGradesComponent), }, { path: 'departments', canActivate: [permissionGuard], data: { permissions: ['clinic.departments.view'] }, loadComponent: () => import('./departments/clinic-departments.component').then( (m) => m.ClinicDepartmentsComponent ), }, { path: 'rooms', + canActivate: [permissionGuard], + data: { permissions: ['clinic.rooms.view'] }, loadComponent: () => import('./rooms/clinic-rooms.component').then((m) => m.ClinicRoomsComponent), }, { path: 'services', + canActivate: [permissionGuard], + data: { permissions: ['clinic.service.view'] }, loadComponent: () => import('./services/clinic-service-list.component').then((m) => m.ClinicServiceListComponent), }, { path: 'pricing', + canActivate: [permissionGuard], + data: { permissions: ['clinic.pricing.view'] }, loadComponent: () => import('./pricing/pricing-matrix.component').then((m) => m.PricingMatrixComponent), }, { path: 'reception', + canActivate: [permissionGuard], + data: { permissions: ['clinic.orders.view'] }, loadComponent: () => import('./reception/reception-order.component').then((m) => m.ReceptionOrderComponent), }, { path: 'cashier', canActivate: [permissionGuard], data: { permissions: ['clinic.payments.'] }, loadComponent: () => import('./cashier/cashier-collect.component').then((m) => m.CashierCollectComponent), }, { path: 'revenue-split', + canActivate: [permissionGuard], + data: { permissions: ['clinic.split.view'] }, loadComponent: () => import('./revenue-split/revenue-split.component').then((m) => m.RevenueSplitComponent), }, { path: 'radiology/worklist', + canActivate: [permissionGuard], + data: { permissions: ['clinic.rad-worklist.view'] }, loadComponent: () => import('./radiology/rad-worklist.component').then((m) => m.RadWorklistComponent), }, { path: 'radiology/report/:studyId', + canActivate: [permissionGuard], + data: { permissions: ['clinic.rad-reports.enter'] }, loadComponent: () => import('./radiology/rad-report-editor.component').then((m) => m.RadReportEditorComponent), }, { path: 'radiology/procedures', + canActivate: [permissionGuard], + data: { permissions: ['clinic.rad-procedures.view'] }, loadComponent: () => import('./radiology/rad-procedures.component').then((m) => m.RadProceduresComponent), }, { path: 'insurance/payer-contracts', + canActivate: [permissionGuard], + data: { permissions: ['clinic.payer.view'] }, loadComponent: () => import('./insurance/payer-contracts.component').then( (m) => m.PayerContractsComponent ), }, { path: 'insurance/claims', + canActivate: [permissionGuard], + data: { permissions: ['clinic.claim.view'] }, loadComponent: () => import('./insurance/claims-list.component').then((m) => m.ClaimsListComponent), }, { path: 'reports', canActivate: [permissionGuard], data: { permissions: ['clinic.reports.view'] }, loadComponent: () => import('./reports/clinic-reports.component').then((m) => m.ClinicReportsComponent), }, { path: 'settings', canActivate: [permissionGuard], - data: { permissions: ['clinic.'] }, + // This screen reads/writes via the CORE /core/settings endpoints, which + // SettingController gates with `core.settings.view` (read) and + // `core.settings.manage` (write). Gate the route on the permission that + // actually governs the endpoint — `core.settings.view` — instead of the + // old over-broad `clinic.` prefix (which admitted ANY clinic permission). + // The owner role already holds it, and the BE .manage gate is the real + // enforcement boundary. A dedicated clinic-scoped settings permission + + // role presets are deferred to م2 (الإعدادات والصلاحيات). + data: { permissions: ['core.settings.view'] }, loadComponent: () => import('./settings/clinic-settings.component').then((m) => m.ClinicSettingsComponent), }, ], }, ];