# 04 — Angular Frontend Architecture (Moon ERP)

Source tree analyzed: `/home/moonui/public_html/moon-erp` (`src/app`). All facts below were read directly from the files cited; inferences are marked **(inference)**.

## 1. Stack and project shape

From `package.json` and `src/app/CLAUDE.md` / project `CLAUDE.md`:

- **Angular `^21.1.0`**, standalone components only (no NgModules); components generated with `--standalone --style=scss --skip-tests` (tests disabled in `angular.json`).
- **NgRx `@ngrx/store ^21.0.1`** with `@ngrx/entity`; **PrimeNG `^21.1.1`** (+ `@primeng/themes ^21.0.4`, Aura); **ngx-translate `^17.0.0`**; **chart.js `^4.5.1`**; **jspdf `^4.2.0` + jspdf-autotable `^5.0.7`** for PDF generation.
- RTL-first bilingual (Arabic/English).

### `src/app/` layout

| Dir | Contents |
|---|---|
| `core/` | `config`, `constants`, `guards`, `interceptors`, `models`, `services`, `store`, `utils` |
| `features/` | ~80 feature directories (accounting, inventory, hr, sales, purchases, pos, crm, cmms, qms, production, **lis**, …), each lazy-loaded |
| `layout/` | `main-layout`, `sidebar`, `topbar` |
| `shared/` | `components/` (`data-table`, `form-dialog`, `page-header`, `module-nav`, `sales-filter-bar`, `ai-assistant`), `directives/` (`can.directive.ts`, `latin-digits.directive.ts`), `pipes/`, `print/`, `services/` (`confirm-delete.service.ts`) |

## 2. Core services pattern (`core/services/`)

- 65+ API services, one per resource, all `@Injectable({ providedIn: 'root' })`, `HttpClient` against `${environment.apiUrl}/{module}/{resource}`.
- **60 of them are `lis-*.service.ts`** (e.g. `lis-request.service.ts`, `lis-result.service.ts`, `lis-worklist.service.ts`, `lis-kanban.service.ts`, `lis-report-pdf.service.ts`, `lis-html-report.service.ts`, `lis-barcode-label.service.ts`, `lis-cashier.service.ts`, `lis-treasury.service.ts`, `lis-tax-invoice.service.ts`, `lis-critical-alert.service.ts` …) — the LIS service surface alone matches the ~120 LIS API endpoints documented in `CLAUDE.md`.
- Convention: `list` / `listAll` / `search` / `create` / `update` / `delete` plus workflow verbs (`enter`, `validate`, `approve`, `release`, `bulkRelease` on `LisResultService`; `collect`, `receive`, `reject`, `aliquot` on `LisSampleService`).
- `listAll()` exists because the API ignores `per_page` and caps at 25 rows — services auto-paginate with `forkJoin` (project `CLAUDE.md`).
- Auth: `core/interceptors/auth.interceptor.ts` adds the non-standard **`X-Authorization: Bearer <token>`** header and must skip `/assets/` requests (translation JSONs).

### NgRx (`core/store/`)

27+ feature slices (accounts, partners, products, hr, sales, purchases, … and `lis/`). Registered effects include `LisSectionsEffects, LisSpecimenTypesEffects, LisInvestigationCategoriesEffects, LisInvestigationsEffects, LisPatientsEffects, LisDoctorsEffects, LisRequestsEffects` (`app.config.ts`). Canonical state shape (`CLAUDE.md`):

```ts
interface FeatureState extends EntityState<Model> {
  meta: PaginationMeta | null;
  loading: boolean; saving: boolean; error: string | null; loaded: boolean;
}
```

Data flow: **Component → NgRx Action → Effect → Service → API → Effect → Reducer → Selector → Component**. Note: only LIS *reference data* (sections, investigations, patients, doctors, requests) goes through NgRx; the heavy operational screens (worklists, board, reception) call services directly with `signal()`/`computed()` local state (verified in `dept-worklist.component.ts`, `lis-board.component.ts`).

## 3. Routing and permissions

`src/app/app.routes.ts` — everything lazy via `loadComponent`/`loadChildren`:

- `/login`, `/register` → `guestGuard`.
- `/catalog` (public product catalog), `/p/:token` + `/portal/*` (public patient portal, `features/lis/patient-portal-public/`, no auth).
- `''` → `authGuard` + `MainLayoutComponent` with children `/core/*`, `/accounting/*`, and `/core/lis/*` (parent route `data: { permissions: ['lis.'] }`, line ~184).
- `/lab` → `loadChildren` → `features/lis/lis-standalone.routes.ts` (`lisStandaloneRoutes`) with its own `LisLayoutComponent` (sidebar nav groups, search, breadcrumbs, pending counts).
- `/external-lab-portal/*` → separate auth (`portal-auth.guard.ts`) and layout for B2B external labs.

### Guards (`core/guards/auth.guard.ts`)

- `authGuard` — token check, then forces a fresh `/auth/me` round-trip (`profileFresh` flag, `markProfileFresh()`); cached localStorage user is treated as stale; on 401/403 cache+token are cleared and the user is sent to login. Denied users are routed to `LandingService.firstAllowedRoute()` or `/access-denied` (anti-redirect-loop design documented in file comments).
- `permissionGuard` — reads `route.data['permissions']` prefixes; matching is **segment/dot-boundary aware** via `permissionMatches` in `core/services/permission.service.ts`; super admin (`super-admin` role or legacy empty-arrays sentinel, `isSuperAdminUser`) bypasses everything; empty prefix list = allowed.
- `moduleGuard` — blocks URL-typing into system-wide deactivated modules (`SystemModulesService.isRouteEnabled`), complementing sidebar hiding.
- Button-level: structural directive `*appCan` (`shared/directives/can.directive.ts`) renders an element only if `PermissionService.can()` passes (string or any-of array); re-evaluates via `effect()` when the user loads. Explicitly documented as defence-in-depth — backend remains authoritative.

Route-data examples: `{ permissions: ['core.users'] }`, `{ permissions: ['lis.results'] }`, multi-prefix `{ permissions: ['core.products', 'inventory.products'] }`.

## 4. LIS screen structure (the production blueprint)

`features/lis/` has 60+ subdirectories plus its own `CLAUDE.md`. Three routing contexts: embedded (`/core/lis/*`), standalone (`/lab/*`), public portal (`/p/:token`).

### Operational screens (sizes via `wc -l`)

| Screen | File | Lines | Role |
|---|---|---|---|
| Dept worklist | `dept-worklist/dept-worklist.component.ts` | 2,848 | Per-section result entry (numeric/text/selection/formula rows, file/culture/histopath cells, flags) |
| Validation worklist | `validation-worklist/validation-worklist.component.ts` | 3,727 | Review & release: `validate / approve / release / print` |
| Collection worklist | `collection-worklist/collection-worklist.component.ts` | 1,646 | Phlebotomy: barcode scanning, tube grouping; mounted at `/lab/samples` |
| Reception | `reception/reception.component.ts` | 574 | Specimen receiving |
| Board | `board/lis-board.component.ts` | 315 | 6-stage progress matrix per request/section (`Stage`, `TestUnit`, `BoardCard` with `dist[]`, `elapsedH`) |
| Kanban | `kanban/lis-kanban.component.ts` | 1,490 | **Retired in standalone mode** — `lis-standalone.routes.ts` redirects `/lab/kanban → /lab/worklist` ("Kanban retired — superseded by Worklist after the workflow rework"); `/lab/results → /lab/worklist` likewise |
| Request wizard v2 | `request-wizard-v2/` | — | 4 steps: patient/external-lab → tests/packages → billing (individual/insurance/lab) → payment; used at `/core/lis/requests/new` and `:id/edit` |

### Server-driven worklist contract

`core/services/lis-worklist.service.ts` defines the worklist API contract: `WorklistCard`, `WorklistCardsResponse`, `WorklistRow` (display types `panel | subsection | test`), `WorklistRowsResponse`, `WorklistSearchResponse`, result flags `'' | 'LL' | 'HH' | 'L' | 'H' | 'N'`, `WorklistPrimaryAction = 'validate' | 'approve' | 'release' | 'print' | null`, and a runtime feature flag `USE_SERVER_WORKLIST = signal<boolean>(ENV_FLAG)` with `setUseServerWorklist()`. Architectural lesson: work-queue ordering/status logic moved server-side; the FE renders and dispatches actions.

### LIS-local shared widgets

`features/lis/shared/`: `bulk-action-bar`, `test-action-dialog`, `test-filter-bar` — module-scoped reusables sitting below the global `shared/` layer.

## 5. Translation (English-first convention)

- `app.config.ts` lines 266–272: `provideTranslateService({ loader: provideTranslateHttpLoader({ prefix: './assets/i18n/', suffix: '.json' }), fallbackLang: 'en' })`.
- Files: `src/assets/i18n/en.json` + `ar.json` — **7,494 leaf keys, 85 top-level namespaces** (measured). Keys are UPPER_SNAKE English namespaces: `APP, AUTH, NAV, COMMON, …, LIS`. The `LIS` namespace has **48 sub-namespaces** (`WIZARD, REQUESTS, SAMPLES, RECEPTION, RESULTS, KANBAN, QC, COMPLIANCE, PATIENT_PORTAL, STATUS, …`).
- `core/services/language.service.ts` sets `document.documentElement` `dir='rtl'` and toggles `body.rtl` when lang is `ar`.
- Convention for HIS/OBGY: add an `HIS` (and/or `OBGY`) namespace to both JSONs; author English first, Arabic as the shipped UI language.

## 6. Report / print engine

Two layers:

**Generic engine — `shared/print/`** (ERP-wide documents: invoices, vouchers):
- `print.interfaces.ts` (110 lines): `GenericPrintData` (party/items/totals + `dir: 'rtl'|'ltr'`, `lang`, translator fn `t`, `extraFields`, `extraSections`) and `CustomTemplateConfig` (colors, font, show/hide toggles, header/footer text).
- `print-templates.ts` (470 lines): `GENERIC_TEMPLATES` map, default `'classic'`.
- `print.service.ts` (230 lines): resolves template id from settings, falls back to `classic`.
- Admin screen: `shared/print/print-settings/` mounted at `/core/print-settings` (perm `core.settings`).

**LIS report engine — `core/services/`** (medical result reports):
- `lis-report-pdf.service.ts` (1,620 lines): jsPDF + autotable; exports `ReportData/ReportSection/ReportResultRow/LabInfo/ReportSettings`; prints via hidden `iframe` + `contentWindow.print()`.
- `lis-html-report.service.ts` (1,311 lines): HTML report rendering with print CSS (`@media screen/print`, fixed footer), header config and histopath section ordering from `lis-lab-info.service.ts` (`ReportTemplate`, `HeaderConfig`, `PrintCommentConfig`, `DEFAULT_HISTO_SECTION_ORDER`).
- Plus `lis-barcode-label.service.ts` (specimen labels), `lis-print-report.service.ts`, `lis-result-report.service.ts`, and batch printing screen `features/lis/print-queue/` (parallel PDF generation).

## 7. Reusable building blocks for HIS/OBGY

- **`shared/components/data-table/data-table.component.ts`** (283 lines): `TableColumn` interface, loading/error/lazy states, filter `TemplateRef` slot, Excel/PDF export through `core/services/export.service.ts` (`ExportColumn`, `exportTransform`), column customization dialog. Drop-in for every HIS list screen.
- **`form-dialog`**, **`page-header`**, **`module-nav`** (`shared/components/`), **`confirm-delete.service.ts`**, PrimeNG `MessageService`/`ConfirmationService` toasts/confirms.
- **`*appCan`** directive + `permissionGuard` route data → permission model is purely conventional (`his.` prefix works with zero framework change).
- **Dual-context layout precedent**: `LisLayoutComponent` (403 lines per LIS `CLAUDE.md`) proves a clinical specialty can ship a fullscreen optimized workspace (`/lab/*`) while reusing the same components inside the ERP shell (`/core/lis/*`). An HIS would replicate this as e.g. `/hospital/*` or `/clinic/*` **(inference)**.
- **Public portal precedent**: token-link patient portal (`/p/:token`, `PatientPortalService`, QR/WhatsApp share via `PortalLinkDialogComponent`) is directly reusable for OB/GYN patients (appointments, ultrasound reports) **(inference)**.
- **Server-driven worklist contract** (`lis-worklist.service.ts`) is the right pattern for clinic queues (today's appointments, pending consultations, ultrasound worklist) **(inference)**.

## 8. Conventions a new HIS/OBGY UI must follow (checklist)

1. `features/his/` (or `features/obgy/`) directory, standalone components, `signal()` UI state, reactive forms.
2. `core/services/his-*.service.ts`, `providedIn: 'root'`, `list/listAll/search/create/update/delete` + workflow verbs; respect `X-Authorization` interceptor and 25-row pagination workaround.
3. `core/models/his-*.model.ts` interfaces mirroring API resources.
4. Routes: embedded under `/core/his/*` with parent `data: { permissions: ['his.'] }` + per-route `his.<resource>`; optional standalone layout module like `lis-standalone.routes.ts`.
5. `HIS` namespace in `en.json`/`ar.json`; English keys first; all UI strings via `| translate`.
6. Reuse `data-table`, `page-header`, `form-dialog`, generic print engine; build a specialty report service modeled on `lis-html-report.service.ts` for clinical documents (antenatal record, ultrasound report) **(inference)**.
7. NgRx slices only for reference data (clinics, services, fee schedules); direct service + signals for operational screens — matching how LIS evolved.

## 9. Facts bearing on the HIS-core vs separate-module decision (frontend view)

- LIS frontend is deeply lab-shaped (worklists, barcode, QC Levey-Jennings, machine results); its **patterns** transfer to HIS, its **screens** do not.
- LIS owns its own patient/doctor/invoice models (`lis-patient.model.ts` with `mrn`, `lis-doctor.model.ts`, `lis-invoice.model.ts`) — there is **no shared clinical patient master in the FE today**; a real HIS needs one consumed by both LIS and OBGY **(inference)**.
- The router + permission system already supports independently activated modules (`moduleGuard`, `module-activation` feature), multiple layouts, and module-prefixed permissions — adding OBGY as a separate specialty module is architecturally cheap on the frontend.
- The kanban→worklist pivot shows the team iterates workflow UIs against real usage; any OBGY UI inherited from the legacy system should be re-expressed in the worklist/wizard idiom rather than ported visually **(inference)**.
