# Scout B — Clinic (HIS) Settings FE Map

Repo root: `/home/moonui/public_html/moon-erp`
Backend root (referenced for data-driven fields): `/home/moonui/moon-erp-be`

## 1. Does a Clinic settings screen/route exist today?

**YES — dedicated route, dedicated component.** It is NOT a generic core settings screen embedding clinic — it's a clinic-scoped screen that reads/writes through the **shared/generic `/core/settings` API**, filtered by `module=clinic`.

- **Route**: `path: 'settings'` inside `CLINIC_ROUTES`, guarded by `permissionGuard` with `data: { permissions: ['core.settings.view'] }` (deliberately switched from the old over-broad `clinic.*` prefix — see inline comment).
  - File: `/home/moonui/public_html/moon-erp/src/app/features/clinic/clinic.routes.ts:170-183`
  - Full resolved path: `/his/settings` (or whatever prefix `ClinicLayoutComponent` mounts under — route is a child of `CLINIC_ROUTES` root, lazy-loaded component `ClinicSettingsComponent`).
- **Component (TS)**: `/home/moonui/public_html/moon-erp/src/app/features/clinic/settings/clinic-settings.component.ts` (337 lines)
- **Template (HTML)**: `/home/moonui/public_html/moon-erp/src/app/features/clinic/settings/clinic-settings.component.html` (174 lines)
- **Styles (SCSS)**: `/home/moonui/public_html/moon-erp/src/app/features/clinic/settings/clinic-settings.component.scss` (212 lines)

This is a **fully generic, data-driven settings renderer** — it does NOT hardcode any clinic field. It fetches whatever `SettingDefinition` rows exist for `module=clinic` from the backend and auto-renders them grouped by `display_group`. There is **zero clinic-specific UI logic** beyond the account-picker heuristic (`setting_key.endsWith('_account_id')` at `clinic-settings.component.ts:215-217`) and a hardcoded fallback label/icon map for group keys that may not even be clinic's (`GROUP_LABELS`/`GROUP_ICONS` at `clinic-settings.component.ts:41-63`, includes generic guesses like `booking`, `billing`, `insurance`, `notifications` that **do not currently exist** in the clinic module's actual seeded data — see §2).

Sibling modules use the **exact same generic pattern** (same component shape, same SettingService, same SCSS classes) — e.g. `/home/moonui/public_html/moon-erp/src/app/features/sales/settings/sales-settings.component.ts:1-60` — confirming this is the house style for ALL module settings screens, not a clinic-specific one-off. Also present: `hr-settings`, `pos-settings`, `owner-settings` (owner-only namespace), `ai-settings`, `lis/nphies-settings`.

## 2. What setting groups/fields does it render TODAY?

The FE renders **whatever is seeded in the BE `SettingDefinition` table for `module='clinic'`**. Checked the seeder directly:

`/home/moonui/moon-erp-be/Modules/Core/database/seeders/SettingDefinitionSeeder.php:1775-1841` ("── Clinic (HIS) ─────" section) — **only 4 settings exist, in 2 groups**:

| # | setting_key | value_type | control rendered | display_group | line |
|---|---|---|---|---|---|
| 1 | `clinic.ar_account_id` | integer | account picker (`p-select`, filtered to `account_type==='detail'`) — matched via `_account_id` suffix heuristic | `clinic_accounting` | seeder:1776-1791 |
| 2 | `clinic.revenue_account_id` | integer | account picker (same) | `clinic_accounting` | seeder:1792-1807 |
| 3 | `clinic.allow_overbooking` | boolean | `p-toggleSwitch` | `clinic_general` | seeder:1809-1824 |
| 4 | `clinic.ordering_mode` | enum, allowed=`['intent','immediate']` | `p-select` dropdown (enum options auto-derived from `allowed_values`) | `clinic_general` | seeder:1826-1841 |

That is the **entire current clinic settings surface**. No pricing/print/prescription/clinical/sales-mode/scheduling fields exist at all today.

**Field-type dispatch logic** (`clinic-settings.component.ts:220-227`, `getFieldType()`):
- `value_type==='boolean'` → toggle
- key ends `_account_id` → account picker (GL account dropdown, populated from `AccountService.listAll()` filtered to leaf/`detail` accounts, `clinic-settings.component.ts:167-175`)
- `value_type==='enum'` + has `allowed_values` → `p-select` with auto-generated Title-Case labels (`getEnumOptions()`, `clinic-settings.component.ts:241-247`)
- `value_type==='integer'` → `p-inputNumber` (no grouping)
- `value_type==='decimal'` → `p-inputNumber` (0-3 fraction digits)
- default → plain `pInputText`

Rows are grouped into cards by `display_group`, sorted with `general`/`clinic_general` pinned first then alphabetical (`clinic-settings.component.ts:126-143`). Each group gets a color-coded icon via a **hardcoded CSS gradient map keyed by `display_group`** (`clinic-settings.component.scss:98-109`) — currently defines colors for `general`, `gl_accounts`, `booking`, `billing`, `appointments`, `insurance`, `notifications` (note: the seeder actually uses `clinic_accounting`/`clinic_general`, not `gl_accounts`/`general` — so those two groups fall through to the **unstyled purple fallback gradient**, `scss:107-109` — a latent cosmetic mismatch).

Labels/descriptions are bilingual, read straight off the definition (`label_ar`/`label_en`, `description_ar`/`description_en`), no client-side i18n key lookup per field (`getLabel()`/`getDescription()`, `clinic-settings.component.ts:229-239`).

## 3. How does it SAVE?

- **Service**: `SettingService` — `/home/moonui/public_html/moon-erp/src/app/core/services/setting.service.ts`
  - Read: `list(module)` → `GET {apiUrl}/core/settings?module=clinic` (`setting.service.ts:21-31,33-56`), unwraps `{definition, current_value}[]` shape into flat `Setting[]`.
  - Write: `update(data: UpdateSetting)` → **`PUT {apiUrl}/core/settings`** (single generic endpoint, NOT REST-per-key) with body `{ setting_key, value, branch_id?, user_id? }` (`setting.service.ts:64-66`, model at `/home/moonui/public_html/moon-erp/src/app/core/models/setting.model.ts:21-26`).
- **Component save flow** (`clinic-settings.component.ts:286-331`, `save()`):
  1. Diffs `editValues` against a load-time snapshot `originalValues` to find only changed keys (line 288-292).
  2. If nothing changed → info toast, no request (294-301).
  3. For each changed setting, fires **one `PUT` per key**, all in parallel via `forkJoin` (306-312) — value is coerced to `String(rawVal)` regardless of type (line 308) before sending.
  4. On success of ALL: success toast + full reload (`this.load()`); on ANY failure: single error toast showing the raw HTTP error message.
- **⚠️ No atomicity / no fail-fast**: each setting is an independent HTTP call. If 3 of 5 changed settings save and the 4th 422s, the UI has no partial-failure handling — `forkJoin` will error out the whole subscribe on the first failed request, but requests already in flight may still commit server-side. There is no client- or server-side transaction wrapping "save all settings together."
- **BE validation is minimal — no domain-level fail-fast at all.** `UpdateSettingRequest` (`/home/moonui/moon-erp-be/Modules/Core/app/Http/Requests/UpdateSettingRequest.php`) only validates `setting_key` (string, not `owner.*`), `value` (required, any type), `branch_id`/`user_id` (nullable ints). **No enum-membership check against `allowed_values`, no numeric range/negative-% check, no per-setting business rule.** Confirms owner's "💰 Financial (fail-fast)" requirement is **entirely unimplemented** — anyone can currently PUT a negative percentage or a non-existent GL account id key and it will save.

## 4. Six required groups — EXISTS vs MISSING

| Owner's required group | Status | Evidence |
|---|---|---|
| 💰 **Financial** (fail-fast: GL accounts, no negative %) | **PARTIAL — data exists, guard MISSING.** 2 GL account fields exist (`ar_account_id`, `revenue_account_id`, group `clinic_accounting`). No fail-fast validation anywhere (see §3) — no % fields at all yet, so "no negative %" can't even be tested; whatever gets added will need net-new BE validation rules. |
| 🏗️ **Sales modes**: lab_mode / rad_mode / pharmacy_source / ordering_mode | **PARTIAL — only `ordering_mode` exists** (`clinic.ordering_mode`, enum `intent`\|`immediate`, seeder:1826-1841). `lab_mode`, `rad_mode`, `pharmacy_source` are **MISSING entirely** — no seeder rows, no FE awareness of these keys anywhere in `clinic-settings.component.ts`. |
| 📅 **Operations**: scheduling_mode (slots\|open) / default_payment_method / allow_credit_balance / return-window | **MISSING**, with one partial substitute: `clinic.allow_overbooking` (boolean, seeder:1809-1824) is a *related but different* toggle (ignore slot capacity) — not the same as a `scheduling_mode: slots\|open` enum the owner wants. `default_payment_method`, `allow_credit_balance`, and any return-window setting do **not exist**. |
| 🖨️ **Print/format definitions** | **MISSING.** No print/format-related setting_keys, no FE section for it. (Barcode/print-format patterns exist elsewhere in the app for LIS — see `barcode-number-format.md` in KB — but nothing wired into clinic settings.) |
| 💊 **Prescription** | **MISSING.** No prescription-related setting_keys or FE fields at all. |
| 🩺 **Clinical fields** | **MISSING.** No clinical-fields configuration surfaced anywhere in settings. |

**Bottom line: only 2 of the 6 requested groups have ANY representation today (Financial has the 2 GL accounts; Sales modes has only `ordering_mode`), and even those existing groups are missing most of their required sub-fields.** The other 4 groups (Operations, Print/format, Prescription, Clinical fields) are 100% net-new — no BE seeder rows, no FE group keys, no icons/labels reserved for them.

## 5. Design identity to reuse / best reference component

**Design system**: PrimeNG (Aura theme) + CSS variables (`--surface-card`, `--surface-border`, `--surface-ground`, `--text-color`, `--text-color-secondary`) — **not a literal "teal" hardcoded theme**; colors come from the active PrimeNG theme tokens plus a **per-group hardcoded gradient palette** for group icon chips (`clinic-settings.component.scss:98-109`): slate/gray for general, indigo for GL accounts, blue for booking, green for billing, teal (`#0d9488`/`#0f766e`) for appointments, amber for insurance, orange for notifications, purple fallback for anything unmapped.

**Structural pattern (the thing to clone)** — "settings group card" layout:
- Outer `.settings-container` (max-width 900px, vertical stack, 20px gap) — `clinic-settings.component.scss:53-58`
- Each `display_group` → one `.settings-group` card: rounded 12px, `surface-card` bg, subtle border+shadow, hover lift — `scss:62-73`
- Card header: colored icon chip (38x38, gradient by group) + title + "`N` settings" count — `scss:75-110`, html:47-59
- Card body: `.settings-list` → repeated `.setting-row` (grid: label column `1fr` / control column fixed `260px`, responsive collapses to 1 column under 640px) — `scss:130-146,201-211`
- Each row: label+description on the left, PrimeNG control on the right, switched by `getFieldType()` — html:63-155
- Sticky-bottom-right **single Save button** (`p-button` severity success, loading spinner bound to `saving()`) — html:160-171, `scss:193-197`
- Uses `<app-page-header>` for the title bar (`title`, `subtitle`, `showRefresh` inputs + `refresh` output) — `PageHeaderComponent` at `/home/moonui/public_html/moon-erp/src/app/shared/components/page-header/page-header.component.ts`, used at `clinic-settings.component.html:3-8`.
- Loading/error/empty states are hand-rolled per component (spinner / exclamation icon + retry button / empty icon) — html:10-39 — not a shared component, just copy-pasted markup+SCSS across every `*-settings.component`.

**Best component to clone**: `/home/moonui/public_html/moon-erp/src/app/features/clinic/settings/clinic-settings.component.{ts,html,scss}` itself is already the cleanest, most current instance of this pattern (confirmed structurally identical to `sales-settings`, `hr-settings`, `pos-settings` — so it IS the house convention, not an outlier). For a م2 rebuild, cloning this exact file trio and swapping in real field-specific components per group (rather than the fully-generic dispatcher) is the natural upgrade path — the card/row/icon-chip/save-bar visual shell can stay, but the "auto-render whatever the API returns" approach will need to become more structured to support fail-fast validation, non-boolean/enum/account/number/text control types (e.g. a slots|open picker with contextual sub-fields, a return-window duration input, a print-template chooser) that the current generic dispatcher (`getFieldType()`) has no cases for.

**i18n**: only 5 keys currently reserved: `CLINIC_SETTINGS.{TITLE,SUBTITLE,SAVED,NO_CHANGES,NO_SETTINGS}` — `src/assets/i18n/en.json:11193-11199` / `ar.json:11193-11199`. No per-field keys (labels come from the DB, not i18n.json).
