A refactored Angular dashboard built on top of the Sakai PrimeNG template, with improved architecture inspired by NG Matero.
- Angular 21 (standalone components, zoneless change detection)
- PrimeNG 21 + PrimeIcons +
@primeuix/themes(Aura preset) - TypeScript 5.9
- Tailwind CSS 4 and SCSS
- Transloco for localization (English / Arabic, including RTL)
- RxJS, Chart.js / ECharts, Socket.IO client, Quill, ngx-markdown, xlsx
git clone <your-repo-link>
cd <project-folder>
npm install
ng serveThen open: http://localhost:4200/
- Project Architecture
- Routing Map
- Feature Catalog (
src/app/domains) - Foundation Layer (
src/app/foundation) - Internationalization
- Deep-Dive Docs Index
- Angular Project Coding Guide
The codebase is split into two layers under src/app/:
| Layer | Path | Contains |
|---|---|---|
| Domains | src/app/domains/ |
One folder per business feature (routable pages, feature-local components/services/models). This is where product logic lives. |
| Foundation | src/app/foundation/ |
Cross-cutting code every domain depends on: core/ (app shell, guards, interceptors, layout, settings) and shared/ (the reusable UI kit — tables, dialogs, toolbars, pipes…). |
Bootstrap/root files live directly under src/: app.routes.ts (root Routes), app.config.ts (providers), app.component.ts, main.ts.
src/app/config/domain.config.ts is the single source of truth for the sidebar menu. It models the app as three nested levels:
| Level | Meaning | Example |
|---|---|---|
| Domain | Section header in the sidebar | identity, transport, exchange |
| Module | One Angular feature module (= one folder in src/app/domains/) |
org, vsl, msg |
| Sub-module | A page/feature inside that module | organizations, vessels, messages |
Each ModuleConfig entry carries routePath, an i18n label, a PrimeIcons class, and a loadRoutes() function that lazy-imports that module's feature.ts/*.routes.ts. To add a new domain: add an entry to the DOMAINS array. To disable a module without deleting code: flip its enabled flag.
| Alias | Resolves to |
|---|---|
@/* |
src/* |
@features/* |
src/app/domains/* (legacy alias name kept for compatibility — domains used to live under features/) |
provideZonelessChangeDetection()— the app does not use Zone.js.provideAppInitializer()— loadsProjectConfigService(active project/tenant config) before bootstrap.provideRouter()withwithHashLocation(),withComponentInputBinding(),withViewTransitions(), andwithPreloading(SelectivePreloadingStrategy).provideHttpClient(withFetch(), withInterceptors([authInterceptor, cacheInterceptor, errorInterceptor])).providePrimeNG()with the Aura theme preset;provideTransloco()foren/ar.
Root routes (src/app.routes.ts):
| URL | Guard | Loads |
|---|---|---|
/auth/** |
guestGuard |
domains/iam/iam.routes.ts — login, forget/reset password (no layout shell) |
/iam/** |
authGuard |
domains/iam/iam-management.routes.ts — users/roles/permissions (inside layout shell) |
/ → redirects to /dashboard |
authGuard |
AppLayout shell, with children below |
/dashboard |
(shell child) | domains/dashboard/dashboard.routes.ts |
/org/** |
(shell child) | domains/org/org.routes.ts |
/grp/** |
(shell child) | domains/grp/grp.routes.ts |
/prc/** |
(shell child) | domains/prc/prc.routes.ts |
/msg/** |
(shell child) | domains/msg/msg.routes.ts |
/vessels/** |
(shell child) | domains/vsl/vsl.routes.ts |
/infra/** |
(shell child) | domains/infra/infra.routes.ts |
/hlp/** |
(shell child) | domains/hlp/hlp.routes.ts |
/sol/** |
(shell child) | domains/sol/sol.routes.ts |
/builder/** |
(shell child) | domains/builder/builder.routes.ts |
/demo-preview |
— | domains/demo-preview/demo-preview.routes.ts (currently an empty route table — see note in the Feature Catalog) |
** |
— | foundation/core/notfound/pages/not-found.component.ts |
AppLayout (src/app/foundation/core/layout/component/app.layout.ts) is the authenticated shell — topbar + sidebar + footer + router outlet. Auth screens render outside it.
Each entry below lists the route, the main page file + class, and what the page does. Domains are grouped the way they appear in the sidebar (per domain.config.ts); a few domains exist in code but aren't wired into the sidebar yet — those are called out separately.
KPI/analytics landing page shown at /dashboard.
| Route | File | Summary |
|---|---|---|
/dashboard |
pages/dashboard.component.ts → DashboardComponent |
12-column responsive grid composing five widgets. |
| — | components/stats-widget.component.ts |
Top-row KPI stat cards. |
| — | components/recent-sales-widget.component.ts |
Recent sales table widget. |
| — | components/best-selling-widget.component.ts |
Best-selling items widget. |
| — | components/revenue-stream-widget.component.ts |
Revenue trend chart widget. |
| — | components/notifications-widget.component.ts |
Recent activity/notifications feed widget. |
| Route | File | Summary |
|---|---|---|
/grp/categories |
categories/pages/categories.ts → CategoriesComponent |
Multi-level hierarchical tree table (entities → categories → sub-categories) with drill-down navigation, a dedicated create/edit dialog (category-form-dialog.component.ts), filtering by module/table/status, full-text search, and Excel import/export/PDF export. |
Related files: categories/models/category.model.ts, categories/services/categories.data.service.ts, categories/services/category-excel.service.ts, categories/utils/tree.utils.ts, categories/utils/status.utils.ts, categories/utils/filter.utils.ts, categories/helpers/categories.columns.ts.
| Route | File | Summary |
|---|---|---|
/org/organizations |
organizations/pages/organizations.component.ts → OrganizationsComponent |
Hierarchical tree table of organizations with nested members; full CRUD, multi-level expansion, filtering by type/status, role-based member assignment, CSV export. Signal-driven state with computed filters. |
/org/communities |
communities/pages/communities/communities.ts → CommunitiesPage |
Paginated table of communities (name, code, status, category); create/edit/delete dialogs, image upload, status tags, row-action menu linking into messaging and organizations. |
/org/contacts |
contacts/pages/contacts/contacts.ts → ContactsPage |
Generic CRUD table (via DataTablePageComponent) for contacts, backed by a REST API with JSON fallback. |
Other files: communities/components/community-details/community-details.ts, communities/models/community.model.ts, organizations/models/organizations.model.ts, organizations/services/organizations-data.service.ts.
Note: there are two
community.service.tsfiles —communities/services/community.service.tsandorg/services/community.service.ts. Confirm which is canonical before extending communities further.
Authentication (public, outside the shell) and authorization (user/role/permission management, inside the shell).
| Route | File | Summary |
|---|---|---|
/auth/login |
authentication/login/presentation/pages/login/login.page.ts → LoginPage |
Reactive login form (username/password, remember-me) wired through a clean-architecture stack (domain/, application/, infrastructure/, presentation/) down to JWT storage. |
/auth/forget-password |
authentication/forget-password/pages/forget-password.ts → ForgetPassword |
Email-based password-reset request form (stub pending API integration). |
/auth/reset-password |
authentication/reset-password/pages/reset-password.ts → ResetPassword |
Password-reset completion form with confirm-password cross-field validation. |
/iam/users |
authorization/users/presentation/pages/users/users.ts → UsersPage |
CRUD data table for user records, also built on the clean-architecture layering (domain/entities, application/use-cases, infrastructure/repositories). |
/iam/roles |
authorization/roles/pages/roles.ts → RolesPage |
CRUD table of roles (create/edit/view/delete), links out to role details; flat pages/services/models pattern + Excel export (role-excel.service.ts). |
/iam/roles/:id |
authorization/role-details/pages/role-details.ts → RoleDetailsComponent |
Role detail view: metadata, a permissions tree for that role, and assigned users with edit/unassign actions. |
/iam/permissions |
authorization/permissions/pages/permissions.ts → PermissionsComponent |
Hierarchical tree table (entities → permissions → fields) with per-permission role checkboxes, bulk edit, CSV export, and filtering by module/status/action. |
Architecture note: authentication/login and authorization/users use a clean-architecture style (domain/, application/, infrastructure/, presentation/ folders); roles, role-details, and permissions use the simpler flat pages/services/models pattern used everywhere else in the app.
Vessel master data, port visits, and vessel-related communication topics.
| Route | File | Summary |
|---|---|---|
/vessels/vessels |
vessels/pages/vessels.ts → Vessels |
CRUD vessel registry; columns/fields auto-discovered from API metadata, plus a manual-create or data-source-import mode. Row actions link into messages and visits. |
/vessels/visits |
visits/pages/visits/visits.ts → VisitsComponent |
Table of port visits (vessel/berth/ETA/ATD, status, agent), with a visit-details side panel and row actions that jump to filtered messages. |
/vessels/topics |
topics/pages/topics/topics.ts → TopicsPage |
Minimal table (edit/view/delete, no create) of vessel-communication topic definitions. |
Other files: vessels/services/vessels.service.ts, vessels/services/vessels-excel.service.ts, visits/models/visit.model.ts, visits/services/visits.service.ts, visits/constants/visit.constants.ts, visits/helpers/visit.helpers.ts, visits/components/visit-details/visit-details.component.ts, topics/models/topic.model.ts, topics/services/topic.service.ts, topics/constants/topic.constants.ts.
Inbound/outbound message exchange and the schemas that define message structure/validation.
| Route | File | Summary |
|---|---|---|
/msg/messages |
messages/pages/messages/messages.ts → Messages |
Table of messages (sender/receiver/status), filterable by status, with actions to view the related conversation, resend, or delete. |
/msg/messages/:id |
messages/pages/message-details/message-details.ts → MessageDetailsComponent |
Full detail view built from the shared detail-page kit: sender/receiver party cards, sent/processed/received timestamps, metadata sidebar (code/hash/idempotency), process timeline, attachments, payload viewer. |
/msg/schemas |
schemas/pages/schemas/schemas.ts → Schemas |
Table of message schemas with search/filter by status/category/process/format; create/edit/delete dialogs. |
/msg/schemas/:id |
schemas/pages/schemas-details/schema-details.ts → SchemaDetailsComponent |
Schema detail view: owner/reviewer party cards, metadata sidebar (format, complexity, dependencies, tags), created/updated/published timestamps, process steps, FAQ accordion, payload viewer. |
Other files: messages/models/message.model.ts, messages/services/messages-mock.service.ts, messages/constants/message.constants.ts, schemas/models/schema.model.ts, schemas/services/schemas.service.ts, schemas/services/schema-mock.service.ts, schemas/constants/schema.constants.ts, schemas/constants/schemadetails.constants.ts.
Process orchestration and the multi-party conversations that run within a process.
| Route | File | Summary |
|---|---|---|
/prc/processes |
processes/pages/processes/processes.ts → ProcessesListComponent |
CRUD table of process definitions. |
/prc/conversations |
conversations/pages/conversations.ts → ConversationsPageComponent |
CRUD table of conversation instances (multi-party exchanges linked to a process/stage/port), with status/category enrichment. |
Other files: conversations/models/conversation.model.ts, conversations/services/conversation.service.ts, conversations/constants/conversation.constants.ts, conversations/helpers/conversation.helpers.ts.
| Route | File | Summary |
|---|---|---|
/infra/servers |
servers/pages/servers/servers.ts → Servers |
Server list page (currently a minimal/placeholder view). |
/infra/server-details |
server-details/pages/server-details/server-details.ts → ServerDetails |
Real-time server-monitoring dashboard: Socket.IO connection streaming CPU/RAM/Disk metrics into live ECharts graphs, with summary cards, color-coded severity thresholds, and a rolling 120-point history buffer. |
| Route | File | Summary |
|---|---|---|
/hlp/documentation |
documentation/pages/documentation.ts → Documentation |
Renders Markdown technical documentation (via ngx-markdown), loading getting-started.md by default and intercepting in-page links to other .md files. |
/hlp/guide |
guide/pages/guide.ts → Guide |
User guide page (minimal/in-progress). |
/hlp/faqs |
faq/pages/faq.ts → Faq |
FAQ accordion (PrimeNG Panel) grouped by category (Account, Billing, Support). |
Internal tooling for scaffolding/configuring new platform instances — browsing metadata, generating modules, and theming.
| Route | File | Summary |
|---|---|---|
/builder/platforms |
platforms/pages/builder-platforms.ts → BuilderPlatformsPage |
Browses all platform projects (table/card view) with logos, descriptions, and the ability to switch the active project. |
/builder/metadata |
metadata/presentation/pages/metadata/metadata.ts → Metadata |
Collapsible domain → module → entity browser driving into entity detail via DialogShellComponent. |
/builder/metadata/:entityName |
metadata/presentation/pages/attributes/attributes.ts → Attributes |
CRUD table (via TableComponent) for an entity's attributes — multilingual name fields, type, validation, enums, lookups, foreign keys. |
/builder/generator |
generator/pages/builder-generator.ts → (module picker) |
Module picker for scaffolding a new project (same data shape as sol's domains page — choose which domains/modules a new platform ships with). |
/builder/configuration |
configuration/pages/builder-configuration.ts |
Reuses the sol app-configuration wizard (branding/theme/customize/setup) for the platform being built. |
These exist under src/app/domains/ and have working routes in app.routes.ts/their own route files, but domain.config.ts doesn't list them as sidebar entries — they're reached via direct links (e.g. from builder) rather than the main menu.
Branding, theming, and per-tenant configuration for a platform instance.
| Route | File | Summary |
|---|---|---|
/sol/app-config |
configuration/presentation/configuration/app-configuration.component.ts → AppConfigurationComponent |
4-step wizard: Branding → Theme & Appearance → Customize → Project Setup (Domains). |
/sol/platforms/:id |
platforms/presentation/platforms/platforms.ts → Platforms |
Single project's detail view (logo, metadata) with an "Apply" action to make it the active project. |
Wizard steps: configuration/presentation/branding/branding.ts (tabs: branding-identity, branding-metadata, branding-social, branding-pwa, branding-components), configuration/presentation/theme-appearance/theme-appearance.component.ts (+ themes.component.ts, custom-theme.component.ts, theme-appearance.store.ts), configuration/presentation/customize/customize.ts, configuration/presentation/domains/domains.ts (module/feature picker). Backed by configuration/infrastructure/branding.service.ts and config-data.service.ts.
pages/crud.component.ts → CrudComponent — the original Sakai-template product CRUD demo (PrimeNG p-table with paging/sorting/search/export). Routes exist in crud.routes.ts but the domain is not registered in app.routes.ts, so it's effectively dead/demo code left over from the template.
Registered at root path /demo-preview in app.routes.ts, but demo-preview.routes.ts currently exports an empty route array, and demo-preview.component.ts is an unused <router-outlet> shell. Code comments state the intent is for /demo-preview to redirect into /dashboard inside the real AppLayout shell (so builder's "live preview" shows the real topbar/sidebar) — but that redirect isn't actually wired up yet, so navigating here currently has no effect.
| Area | File(s) | Purpose |
|---|---|---|
| Providers | core.providers.ts |
Root provider array (currently wires the settings APP_INITIALIZER hook). |
| Guards | guards/auth.guard.ts, guards/guest.guard.ts |
Gate authenticated vs. guest-only routes (currently stubbed to return true pending backend). |
| Interceptors | interceptors/auth.interceptor.ts |
Attaches the bearer token to requests (skips public URLs), retries once via token refresh on 401, logs out on refresh failure. |
interceptors/cache.interceptor.ts (+ services/http-cache.service.ts) |
In-memory GET response cache keyed by URL; invalidates everything under a resource prefix on POST/PUT/DELETE. | |
interceptors/error.interceptor.ts |
Retries transient 5xx/network errors with backoff before rethrowing. | |
| Layout shell | layout/component/app.layout.ts |
Root authenticated shell: topbar + sidebar + footer + router outlet. |
layout/component/app.topbar.ts, app.sidebar.ts, app.footer.ts |
Shell chrome. | |
layout/component/app.menu.ts, app.menuitem.ts |
Recursive sidebar menu renderer driven by domain.config.ts. |
|
layout/component/app.configurator.ts, app.floatingconfigurator.ts |
Theme/layout configuration drawer + its floating launcher button. | |
layout/component/ProfileMenu.ts, project-switcher.ts |
Topbar user menu; multi-project/tenant switcher. | |
layout/service/layout.service.ts |
Layout state signals (menu mode, dark mode, overlay/mobile state) + persistence. | |
| Models | models/layout.model.ts, models/project-config.model.ts |
LayoutConfig and multi-project config typings. |
| Services | services/auth.service.ts |
Auth state (tokens, current user, login signals), login/refresh/logout. |
services/page-title.service.ts |
Sets the browser tab title per route. | |
services/project-config.service.ts |
Loads/holds the active project/tenant configuration used at bootstrap. | |
| Settings | settings/settings.component.ts, settings.service.ts |
User-preferences panel — font size, density, theme preset, light/dark, language, menu mode — persisted via UserPreferencesApiService. |
| Preloading | strategies/selective-preloading.strategy.ts |
Preloads routes flagged data: { preload: true } (e.g. dashboard) after first navigation. |
| Theme builder | theme-builder/theme-builder.component.ts, .service.ts, theme-personality.service.ts, personality-picker.component.ts |
Live theme customization (color/preset/personality picking, light/dark) shown inside the configurator panel. |
| i18n | transloco/transloco-loader.ts |
Custom Transloco HTTP loader for per-language JSON. |
| Misc pages | empty/pages/empty.component.ts, notfound/pages/not-found.component.ts |
Blank placeholder page; 404 page (used as the root ** route). |
| Utils | utils/color-palette.util.ts |
Color math helpers for the theme builder. |
Documented in depth under
docs/shared/— see the Deep-Dive Docs Index. Summary:
| Component / Service | File | Use when |
|---|---|---|
<app-table> |
components/table/table.ts |
The default data table: toolbar (title, add, search, filters, export), per-row actions, bulk selection + bottom bar, optional card/grid view. Fully data-driven via TableColumn[]. |
<app-data-table-page> |
components/data-table-page/generic-table-page.component.ts (+ generic-table.service.ts, generic-excel.service.ts, generic-table-page.config.ts) |
Higher-level wrapper that auto-builds an <app-table> from API metadata (ApiMetaColumn[]) — scaffold a CRUD page with no manual column config. |
<app-tree-table> |
components/tree-table/tree-table.ts (+ tree-table.models.ts) |
Hand-built 2–3 level hierarchy table when you need fine control (sorting, filtering, nested actions) — the canonical user, Permissions, uses this. |
| Generic tree table | components/generic-tree-table/generic-tree-table-page.component.ts (+ generic-tree-table.service.ts, generic-tree-excel.service.ts, generic-tree-page.config.ts, tree-table-builder.service.ts, base-tree-api.service.ts) |
The tree equivalent of data-table-page — auto-builds a nested tree table from API metadata. |
<app-dialog-shell> |
components/dialog-shell.ts |
Standard create/edit modal (header + content slot + Cancel/Save footer). Never use raw p-dialog directly. |
<app-popup-shell> |
components/popup-shell/popup-shell.ts |
Lightweight dismiss-on-outside-click overlay with no dialog chrome — for popovers, not forms. |
<app-shared-toolbar> |
components/toolbar/shared-toolbar.component.ts (+ components/search-mode/search-mode.component.ts) |
Standalone page toolbar (title/add/search modes/filters/export) for layouts that don't use <app-table>'s built-in one. |
<app-shared-bottom-bar> |
components/bottom-bar/shared-bottombar.component.ts |
Floating bulk-action bar shown when rows are selected; built into <app-table> but usable standalone. |
<app-filter> |
components/filter/filter.component.ts (+ models/filter.models.ts) |
Jira-style searchable multi-select filter chip; implements ControlValueAccessor. |
<app-card> |
components/card/card.ts |
Reusable card with header/body/footer slots and optional click affordance. |
| Detail-page kit | components/detail-page/ (detail-page-shell, detail-card, detail-field, page-header, metadata-sidebar, timestamp-sidebar, party-card, process-timeline, payload-viewer, attachment-list, dp-validation-messages) |
Composable building blocks for entity detail views (used heavily by msg/messages and msg/schemas detail pages). |
<app-actions-menu> |
components/actions-menu/actions-menu.component.ts |
Drop-in 3-dot row/card action menu. |
<app-delete-button> |
components/delete-button/delete-button.component.ts |
Delete trigger with built-in confirm dialog; icon/label variants. |
<app-lang-switcher> |
components/lang-switcher/lang-switcher.ts |
EN ↔ AR toggle wired to Transloco. |
| Theme switcher | components/theme-switcher.ts |
Light/dark toggle. |
| Page loading | components/loading/page-loading.component.ts + .service.ts |
Global loading spinner/skeleton, toggled imperatively from any component. |
| Pipes | pipes/severity.pipe.ts, pipes/capitalize.pipe.ts, pipes/readableDateTime.pipe.ts |
Status→tag-severity mapping (EN/AR aware); string capitalization; locale-aware date/time formatting. |
BaseApiService |
services/base-api.service.ts |
Abstract base every domain service extends — unwraps the API response envelope and maps raw items to typed models via mapItem(). |
TableBuilderService |
services/table-builder.service.ts |
Converts API meta_data into TableColumn[] — what powers data-table-page and generic-tree-table-page. |
| Utils | utils/table.utils.ts, utils/match-modes.constants.ts |
Filter-option helpers (uniqueOptions()); PrimeNG match-mode constants. |
| Models | models/table.models.ts, models/filter.models.ts, models/models.ts |
TableColumn, ToolbarFilterDefinition, JiraFilterOption, and shared barrel exports. |
The app is bilingual (English / Arabic, with RTL) via Transloco. Every user-visible string must go through Transloco — no hardcoded strings in templates or TypeScript. See docs/transloco-i18n.md and docs/INTERNATIONALIZATION.md for the full setup and conventions.
| Doc | Covers |
|---|---|
docs/APP-ARCHITECTURE.md |
Earlier architecture write-up (predates the features/ → domains/ rename — folder names in it are outdated, but the core/shared/feature reasoning still applies). |
docs/module-build-journey.md |
Step-by-step recipe for building a brand-new module end to end (folders → model → service → routes → component → template → SCSS → i18n keys → route wiring). |
docs/building-a-page.md / docs/shared/building-a-page.md |
How a sidebar entry in domain.config.ts maps to actual route/component code; flat-table vs. tree-table decision guide. |
docs/coding-rules.md |
TypeScript/Angular coding conventions used across the project. |
docs/performance.md |
Performance techniques (OnPush, lazy loading, preloading, signals) specific to this architecture. |
docs/api-layer-and-data-handling.md |
How HTTP requests are made and how server data is mapped into component models (BaseApiService pattern). |
docs/DATA_TABLE_PAGE_README.md |
Usage guide for <app-data-table-page>. |
docs/UNIFYING-UI.md |
UI consistency guidelines across the project. |
docs/shared/table.md, tree-table.md, dialog-shell.md, toolbar.md, bottom-bar.md, filter.md, card.md |
Per-component API reference for the shared UI kit. |
docs/shared/TABLE-BUILDER.md |
TableBuilderService — turning raw API responses into table config. |
docs/shared/import-export.md |
Recipe for adding CSV/Excel/PDF export and import to a module. |
Note: some of these docs (e.g.
docs/shared/table.md,docs/shared/bottom-bar.md) still reference the pre-refactor pathsrc/app/shared/...; the components now live undersrc/app/foundation/shared/...as listed in the Foundation Layer section above.
These components are mandatory. Never bypass them with raw PrimeNG equivalents.
| Component | File | Use when |
|---|---|---|
<app-popup-shell> |
popup-shell/popup-shell.ts |
Custom overlay without dialog chrome |
<app-delete-confirm-popup> |
delete-confirm-popup/... |
Per-row inline delete confirmation |
<app-delete-button> |
delete-button/... |
Bulk delete or programmatic row delete |
<app-actions-menu> |
actions-menu/... |
Standalone 3-dots row menu |
<app-tree-table> |
tree-table/tree-table.ts |
Hierarchical data (2 or 3 levels) |
| You want to… | Use |
|---|---|
| Flat data table with toolbar, filters, pagination | <app-table [data]="..." [columns]="..."> |
| Table + cards/grid toggle | <app-table [showLayoutToggle]="true"> + #cardTemplate |
| Custom card per row in grid view | Project <ng-template #cardTemplate let-item> inside <app-table> |
| Card 3-dots menu | One shared <p-menu #cardMenu> + openCardMenu() swapping model |
| Open create/edit dialog | <app-dialog-shell [(visible)]="..." (save)="..."> |
| Per-row action menu | [rowActions]="myArrowFn" on <app-table> |
| Bulk delete bottom bar | Built into <app-table> — enable with [showBulkDelete]="true" |
| Toolbar filter chips | [toolbarFilters]="..." on <app-table> |
| Status tag coloring | [severityMap]="..." on <app-table> or | severity pipe |
| 2-level hierarchy (parent → children) | <app-tree-table [nestedConfig]="cfg"> |
| 3-level hierarchy | <app-tree-table> with nested nestedConfig |
| Lightweight overlay / context popup | <app-popup-shell (dismissed)="..."> |
| Per-row inline delete confirm | <app-delete-confirm-popup> |
| Bulk / bottom-bar delete with confirm | <app-delete-button> |
| Reduce unnecessary re-renders | changeDetection: ChangeDetectionStrategy.OnPush on every component |
| Local UI state | signal<T>() — not plain variables |
| Derived/computed values | computed() — not getters |
| Unsubscribe from Observables | takeUntilDestroyed(destroyRef) |
| Mock data before backend is ready | JSON file in public/api/ + BaseApiService |