08-portals-and-i18nwave: W5filled3 citations

Clinic Admin Portal — Single-Clinic Operations Hub

Audiences: clinical-buyer, developer, internal

Clinic Admin Portal — Single-Clinic Operations Hub

The most feature-dense of the eleven portals: one clinic administrator manages users, scheduling, HR, inventory, accounting, billing, communications, and clinical configuration for a single clinic — roughly forty route sections under one role-gated shell.

Business Purpose

The clinic administrator is the operational owner of a single clinic. Where the doctor/nurse portals are clinical and the org-admin portal is cross-clinic, the clinic-admin portal is the back-office control room for one clinic: it hires and schedules staff, configures online booking and appointment templates, runs the invoice/EU-billing pipeline, manages inventory and devices, keeps the chart of accounts, and configures the clinic's diagnosis-engine catalogue.

Because a clinic buyer's day-to-day administration lives here, this portal is the single largest route surface in ddx-web191 page.tsx files across ~40 top-level sections. It is built from the same one-factory/one-config pattern as every other portal (see Multi-Portal Architecture); its size comes from route breadth, not a divergent shell.

Audiences

  • Clinical buyer (clinic owner/office manager): this is the portal they evaluate — it is where "run my clinic" actually happens (staff, schedules, money, supplies, communications).
  • Developer: one RolePortalConfig (CLINIC_ADMIN_CONFIG) + one sectioned nav config govern the whole surface; new sections are added as route folders + nav entries, not a new shell.
  • Internal (ops/support): locates which section owns a given admin task (e.g. "where do I configure online booking?" → online-booking-config/).

Architecture

Shell composition (identical to every portal)

ddx-web/src/app/[locale]/portal/clinic-admin/layout.tsx is a two-line invocation of the shared factory:

typescript
export default createRoleLayout(CLINIC_ADMIN_CONFIG, ClinicAdminNav);

where ClinicAdminNav renders <PortalNavigation roleKey="clinic-admin" />. The factory (ddx-web/src/components/portal/createRoleLayout.tsx) runs the standard sequence: await paramsrequireRoleAccess('CLINIC_ADMIN_ONLY', locale) server-side RBAC gate → org fetch → brand/label resolution → <DDXHeader> + <PortalShell> + <AppStatusBar> + nav + SSE bootstrap. There is no clinic-admin-specific shell code — the depth is entirely in the routes.

Role config

CLINIC_ADMIN_CONFIG (ddx-web/src/lib/portal/role-configs.ts:117):

FieldValue
roleKeyclinic-admin
authRequirementCLINIC_ADMIN_ONLY
roleBadgeCLINIC_ADMIN, label key roles.clinic_admin, BuildingsIcon, warning variant
brandNameKeyclinicAdmin.title

Registered in the ROLE_CONFIGS registry (role-configs.ts:190).

The sectioned nav config ddx-web/src/lib/portal/nav-configs/clinic-admin.ts groups the ~40 route sections into major nav groups (label keys shown as authored in the config, mixing the admin:, clinicAdmin:, diagnosisEngine:, and common: namespaces):

Nav groupRepresentative sections (route folders under clinic-admin/)
Overviewdashboard
User Managementusers, approvals, invitations
Clinic Operationsappointments, appointment-templates, recurring-appointments, waitlist, waiting-room, online-booking-config, staff, departments, locations, holidays
Diagnosis Enginediagnosis-engine (diseases / symptoms / treatments)
Billing & Revenuepayment-settings, invoices (clinicAdmin:invoices), charge items, pricing, patient accounts, insurance claims, EU-billing (coverage / claims / reconciliation)
Human Resourceshr (employees, contracts, timesheets, leave, certifications, skills)
Inventoryinventory (stock, devices, device-definitions, supply-requests, deliveries)
Accountingaccounting (chart of accounts, journal entries, cost centers, budgets, fiscal periods)
Communicationsactivity-log, communications, incoming-mail, messenger, messenger-admin
Insights & Configanalytics, reports, branding, settings, cms, document-generation, flowagent

The label keys are authored inline in the nav config; several use admin:navigation.* keys (shared with the admin portal) rather than a dedicated clinicAdmin: key. This is intentional reuse — clinic-admin and admin share much administrative vocabulary — and is the reason CLINIC_ADMIN_CONFIG.brandNameKey resolves from the admin-family clinicAdmin.title.

Tech Stack & Choices

Same stack as every portal (see Multi-Portal Architecture): Next.js 16 App Router, server-component RBAC gate via requireRoleAccess, next-intl localePrefix: 'always', config-driven PortalNavigation. The only clinic-admin-specific choices are the route breadth and the clinicAdmin i18n namespace.

Rejected: a bespoke clinic-admin shell. Every attempt to special-case the largest portal would fork the design vocabulary; instead the factory pattern scales to 40 sections at the cost of route folders + nav entries only.

Data Flow

Identical three-gate flow to all portals: browser → proxy.ts (locale + session-cookie gate, trigram → X-Clinic-ID header) → [locale]/portal/clinic-admin/layout.tsx (requireRoleAccess('CLINIC_ADMIN_ONLY')) → per-section island fetches its entity data client-side through the /api/v1 BFF seam (per the client-fetch island architecture in ddx-web/CLAUDE.md), with the backend re-enforcing RBAC + tenant scope authoritatively.

Because clinic-admin touches billing, HR, and accounting endpoints, every one of those routes must be allowlisted in the clinic-admin role file under ddx-web/src/lib/api/rbac-permissions/ — an un-allowlisted endpoint 403s silently (empty section, no api.log entry).

Implicated Code

  • ddx-web/src/app/[locale]/portal/clinic-admin/layout.tsx — two-line createRoleLayout invocation; no bespoke shell.
  • ddx-web/src/app/[locale]/portal/clinic-admin/** — ~40 route sections, 191 page.tsx files.
  • ddx-web/src/lib/portal/role-configs.ts:117CLINIC_ADMIN_CONFIG (authRequirement: 'CLINIC_ADMIN_ONLY', brandNameKey: 'clinicAdmin.title').
  • ddx-web/src/lib/portal/nav-configs/clinic-admin.ts — the sectioned nav config.
  • ddx-web/src/locales/{en,de,fr}/clinicAdmin.json — the clinicAdmin namespace (one of the 102 namespaces; see i18n — Multi-Lingual Interface).

Operational Notes

  • Largest route surface in ddx-web — when auditing or refactoring portal routes, expect clinic-admin to dominate any file count; scope route sweeps per-section, not portal-wide.
  • Shared admin: nav keys — many nav entries reuse admin:navigation.* keys; a rename in the admin namespace ripples into the clinic-admin sidebar. Verify both portals when touching those keys.
  • New section = folder + nav entry + RBAC allowlist — adding a clinic-admin section is three coordinated edits: the route folder under clinic-admin/, the nav entry in nav-configs/clinic-admin.ts, and the endpoint(s) in the clinic-admin RBAC allowlist. Miss the last and the section renders empty with a silent 403.
    Clinic Admin Portal — Single-Clinic Operations Hub — Dudoxx Docs | Dudoxx