akveo/ngx-admin
May 5, 2026
Scope: This Developer Onboarding Guide covers 100 screens of the ngx-admin application. The screens assessed include: Earning Card, D3, Chartjs, Tiny Mce (theme component), Dashboard, Charts, Footer, Search Input, Header, Echarts, Temperature Dragger, Solar, E Commerce, Status Card, Chart Panel Summary, Country Orders, Room Selector, Traffic, Weather, Chart Panel Header, Contacts, Rooms, Player, Chart (country orders), Electricity Chart, Kitten, Temperature, Ckeditor, User Activity, Front Side (profit card), Visitors Analytics, Slide Out, Traffic Cards Header, Traffic Bar, Back Side (traffic reveal card), Front Side (traffic reveal card), Progress Section, Back Side (profit card), Form Layouts, Spinner Sizes, Progress Bar, Spinner, Interactive Progress Bar, Buttons, Spinner Color, Spinner In Buttons, Forms, Spinner In Tabs, Datepicker, Maps, Bubble, Form Inputs, Dialog Name Prompt, Window, Window Form, Smart Table, Toastr, Tooltip, Grid, Ui Features, Tables, Showcase Dialog, Search Map, Icons, Pages, Tree Grid, Popovers, Typography, Charts Panel, Extra Components, Visitors Analytics Chart, Electricity, Map (country orders), Security Cameras, Nebular Select, Charts (charts panel), Modal Overlays, Back Side (earning card), Legend Chart, Alert, Traffic Reveal Card, Map (search map), Front Side (earning card), Calendar, Calendar Kit, Visitors Statistics, Miscellaneous, Profit Card, Search (search map), Editors, Chat, Search Fields, Tiny Mce (editors), Day Cell, Leaflet, Month Cell, Not Found, Form Inputs (extra components), Dialog, and Gmaps. Components, patterns, and features not included in the assessed documentation are outside the scope of this document.
Document disclosure: Generated by Inkwell Forge — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.
What this application does: ngx-admin is an Angular-based administrative dashboard application built on the Nebular UI framework by Akveo. It serves as both a production-ready admin template and a living component showcase. Its primary users are developers building internal tools and administrators managing data — the application demonstrates IoT-style dashboards (device status, solar energy, temperature/humidity controls), e-commerce analytics (earnings, traffic, country orders, visitor statistics), and a comprehensive UI component library (charts, maps, editors, forms, tables, calendar, and modal overlays).
Tech stack at a glance:
@nebular/theme) — Primary UI component library providing cards, menus, dialogs, forms, and the theming systemngx-echarts) — Primary charting engine for area, bar, pie, and line chartsng2-charts) — Secondary charting library used for horizontal bar charts@swimlane/ngx-charts — Used for the D3-style advanced pie chart@asymmetrik/ngx-leaflet) — Interactive map renderingng2-smart-table — Feature-rich data grid with inline CRUDHow this guide is organized: Sections marked [Tutorial] walk you through an experience. Sections marked [How-to] give you procedures for specific tasks. Sections marked [Reference] are lookup tables. Sections marked [Explanation] provide context and rationale.
Who to ask:
[Team Lead: name][Code Owner for dashboard/e-commerce features: name][Code Owner for @theme and @core modules: name][Not documented — WHO: Team Lead; WHAT: Names and contact details for each code owner area listed above; WHERE: Replace the placeholder names in the "Who to ask" list above]
[Tutorial]
This section takes you from a fresh machine to a running local development environment.
Before you start, install the following tools. Verify each version after installation.
| Tool | Required Version | Install Command |
|---|---|---|
| Node.js | 18.x or 20.x LTS | nvm install 20 (using nvm) |
| npm | 9.x or 10.x (bundled with Node) | Included with Node.js |
| Angular CLI | 16.x or 17.x | npm install -g @angular/cli |
| Git | Any recent version | git-scm.com |
(inferred from Angular framework conventions — verify against package.json engines field and .nvmrc if present)
Verify your setup:
node --version
npm --version
ng version[Not documented — WHO: Team Lead; WHAT: The exact Node.js version required (check .nvmrc or package.json engines field); WHERE: Replace the version range in the Prerequisites table above]
git clone [repository-url]
cd ngx-admin
npm install[Not documented — WHO: Team Lead; WHAT: The repository URL (GitHub/GitLab/Bitbucket); WHERE: Replace [repository-url] in the clone command above]
Expected output from npm install: a list of installed packages with no fatal errors. Peer dependency warnings are common with Angular projects and can generally be ignored unless they relate to @nebular/theme or @angular/core.
The application uses Angular's environment.ts pattern for environment-specific configuration.
src/environments/environment.ts and src/environments/environment.prod.ts exist. If they do not, copy from the example files:cp src/environments/environment.example.ts src/environments/environment.ts[Not documented — WHO: Team Lead; WHAT: Whether an environment.example.ts file exists and what variables it contains; WHERE: Adjust the copy command above if the file naming differs]
src/environments/environment.ts and set the following values:export const environment = {
production: false,
googleMapsApiKey: '[placeholder]', // Ask your team lead for this value
};Google Maps API Key: The Gmaps screen (/maps/gmaps) requires a Google Maps JavaScript API key. Ask your team lead for the development API key.
[Not documented — WHO: Team Lead; WHAT: All environment variables required beyond googleMapsApiKey, and whether a .env file pattern is used in addition to environment.ts; WHERE: Add any additional variables to the environment configuration block above]
Based on the assessed documentation, this application uses mock/in-memory data services for all data displayed in the dashboard, e-commerce, and extra-components sections. The abstract service classes (e.g., SolarData, ElectricityData, SmartTableData, SecurityCamerasData) are backed by concrete mock implementations registered in an Angular module — no external database connection is required to run the application locally.
(inferred from the abstract service pattern and the absence of any HTTP endpoint documentation across all 100 assessed screens — verify against src/app/@core/mock/ or equivalent directory)
[Not documented — WHO: Team Lead; WHAT: Whether a real backend API or database is used in any environment, and if so, what connection strings or migration commands are needed; WHERE: Add database setup steps here if a backend exists]
ng serveOr, if the project uses a custom npm script:
npm start(inferred from Angular CLI conventions — verify against package.json scripts field)
Expected output:
** Angular Live Development Server is listening on localhost:4200 **
✔ Compiled successfully.
http://localhost:4200./pages/dashboard.[Screenshot: The ngx-admin dashboard home page showing the sidebar navigation and the e-commerce dashboard with status cards and charts]
If any of these steps fail, see Section 7: Debugging & Troubleshooting.
This application has no background job runners, WebSocket servers, or separate API processes that need to be started independently. All data is served from in-memory mock services within the Angular application itself.
(inferred from the absence of any WebSocket, polling, or external API documentation across all assessed screens — verify with your team lead)
[Not documented — WHO: Team Lead; WHAT: Whether any separate backend process, API server, or WebSocket service needs to be started for full functionality; WHERE: Add startup commands here if a backend service exists]
[Reference]
src/
├── app/
│ ├── @core/ — Core module: data services, utilities, mock providers
│ │ ├── data/ — Abstract data service classes (service contracts)
│ │ ├── mock/ — Concrete mock implementations of data services
│ │ └── utils/ — Shared utilities (LayoutService, AnalyticsService, SeoService)
│ ├── @theme/ — Theme module: shared layout components
│ │ ├── components/ — Reusable shell components (header, footer, search-input, tiny-mce)
│ │ ├── layouts/ — Page layout wrappers (one-column, two-column)
│ │ └── styles/ — Global SCSS variables and theme overrides
│ ├── pages/ — All routed feature pages
│ │ ├── dashboard/ — IoT dashboard screens
│ │ ├── e-commerce/ — E-commerce analytics screens
│ │ ├── charts/ — Chart showcase screens
│ │ ├── editors/ — Rich text editor screens
│ │ ├── extra-components/ — UI component showcase screens
│ │ ├── forms/ — Form showcase screens
│ │ ├── maps/ — Map showcase screens
│ │ ├── miscellaneous/ — Utility screens (404)
│ │ ├── modal-overlays/ — Dialog/overlay showcase screens
│ │ ├── tables/ — Table showcase screens
│ │ ├── ui-features/ — UI feature showcase screens
│ │ └── pages.component.ts — Root pages shell with sidebar menu definition
│ └── app.module.ts — Root Angular module
├── assets/
│ ├── map/ — GeoJSON files for map screens
│ ├── skins/ — TinyMCE skin assets
│ └── images/ — Static image assets
└── environments/
├── environment.ts — Development environment config
└── environment.prod.ts — Production environment config
(inferred from Angular/Nebular project conventions and the file paths referenced across all assessed screens — verify against the actual repository structure)
| File | Purpose | When You'll Touch It |
|---|---|---|
src/app/pages/pages.component.ts |
Root authenticated shell; defines the MENU_ITEMS sidebar navigation array |
When adding a new top-level page or section to the sidebar |
src/app/pages/pages-menu.ts |
Exports the MENU_ITEMS constant — the full sidebar navigation tree |
When adding a new route to the sidebar menu |
src/app/@core/data/ |
Abstract service class definitions (data contracts) | When adding a new data domain or modifying an existing service interface |
src/app/@core/mock/ |
Concrete mock service implementations | When adding mock data for a new screen or modifying existing demo data |
src/app/@theme/components/header/header.component.ts |
Global application header with theme switcher and user menu | When modifying the header, adding nav items, or changing theme behavior |
src/app/@theme/components/footer/footer.component.ts |
Global application footer | When updating attribution, copyright year, or social links |
src/app/pages/dashboard/dashboard.component.ts |
IoT dashboard page; demonstrates theme-reactive card sets | When modifying the main dashboard layout or adding new dashboard widgets |
src/app/pages/e-commerce/e-commerce.component.ts |
E-commerce section shell | When adding new e-commerce sub-pages |
src/app/pages/charts/echarts/echarts.component.ts |
ECharts area chart showcase | When modifying the ECharts integration pattern |
src/app/pages/tables/smart-table/smart-table.component.ts |
Smart Table with inline CRUD | When modifying the data grid pattern |
src/app/pages/maps/leaflet/leaflet.component.ts |
Leaflet map integration | When modifying the Leaflet map pattern |
src/app/pages/modal-overlays/dialog/dialog.component.ts |
Dialog showcase; demonstrates all NbDialogService patterns |
When implementing a new dialog |
src/environments/environment.ts |
Development environment variables (API keys, feature flags) | When adding a new environment variable |
angular.json |
Angular CLI workspace configuration (build targets, asset paths) | When adding new global scripts, styles, or asset directories |
| What | Convention | Example |
|---|---|---|
| Component files | kebab-case.component.ts |
earning-card.component.ts |
| Component class names | PascalCase + Component suffix |
EarningCardComponent |
| Component selectors | ngx- prefix + kebab-case |
ngx-earning-card |
| Service files | kebab-case.service.ts |
layout.service.ts |
| Abstract data class files | kebab-case.ts (no .service) |
solar.ts, earning.ts |
| Module files | kebab-case.module.ts |
dashboard.module.ts |
| Route paths | kebab-case |
/e-commerce/earning-card |
| Template files | Same name as component | earning-card.component.html |
| Style files | Same name as component | earning-card.component.scss |
| Constants | SCREAMING_SNAKE_CASE |
MENU_ITEMS |
| Interfaces/Types | PascalCase |
CardSettings, PieChart, TrafficBar |
| Enums | PascalCase |
NgxLegendItemColor |
| Private class properties | camelCase |
themeSubscription, alive |
@Input() properties |
camelCase |
chartValue, selectedCurrency |
@Output() emitters |
camelCase |
selectEvent, positionChanged |
[Explanation]
What it is: ngx-admin is a single-page application built with Angular, using Angular's module-based architecture and the Angular Router for all navigation.
Why it was chosen: Angular provides a complete, opinionated framework with built-in dependency injection, a powerful template compiler, and a mature ecosystem — well-suited for a large dashboard application with many interconnected components. The Nebular UI library is purpose-built for Angular, making the combination a natural fit.
How it affects your work: All pages are Angular components registered as routes. Navigation is declarative (via routerLink or NbMenuService.navigateHome()) rather than imperative. The application uses nested routing extensively: shell components like ChartsComponent, FormsComponent, and MapsComponent contain only a <router-outlet> and act as URL namespace containers for their child routes. When you add a new page, you register it as a child route in the appropriate feature module's routing configuration.
Where to see it: src/app/pages/pages.component.ts (root shell), src/app/pages/charts/charts.component.ts (shell component example), src/app/pages/pages-menu.ts (full route/menu mapping).
What it is: The application uses local component state (class properties) as its primary state management mechanism. There is no global state store (no NgRx, Akita, or similar). Shared state between components is managed through Angular's dependency injection system via services.
Why it was chosen: For a dashboard application where most screens are independent data displays, local state is sufficient and avoids the complexity of a global store. The Nebular theme system (NbThemeService) acts as a shared reactive state source for cross-cutting concerns like the active theme and responsive breakpoints.
How it affects your work: Each component manages its own data. When you need to share state between a parent and child, use @Input()/@Output() bindings. When you need to share state across unrelated components, create or use an existing service. Do not reach for NgRx unless the team has explicitly decided to adopt it.
Where to see it: DashboardComponent (local state with solarValue and statusCards), HeaderComponent (service-injected reactive state via NbThemeService), CountryOrdersComponent (parent-to-child data flow via @Input()).
What it is: The application uses an abstract service class pattern for all data access. Each data domain (solar, electricity, earnings, etc.) has an abstract class in src/app/@core/data/ that defines the service contract, and a concrete implementation in src/app/@core/mock/ that provides the actual data. All data is returned as RxJS Observable streams.
Why it was chosen: This pattern decouples the UI components from the data source. Swapping mock data for a real HTTP API requires only replacing the concrete service registration in the Angular module — no component code changes are needed. This is a standard Angular dependency injection pattern.
How it affects your work: When you need to fetch data in a component, inject the abstract class (not the concrete implementation). Subscribe to the returned Observable and manage the subscription lifecycle using the takeWhile/alive pattern (see Section 5.4). Never call subscribe() without a cleanup strategy.
Where to see it: src/app/@core/data/solar.ts (abstract class), DashboardComponent (consumer), SmartTableComponent (consumer of SmartTableData).
What it is: Authentication is enforced at the Angular Router level via route guards (e.g., AuthGuard implementing CanActivate). No component in the assessed codebase performs its own authentication checks.
Why it was chosen: Centralizing auth enforcement in route guards follows the separation of concerns principle — components focus on rendering, guards focus on access control. This also prevents partial renders of protected content before a redirect fires.
How it affects your work: When you add a new route, you do not add auth logic to the component. Instead, you apply the appropriate guard to the route definition in the routing module. If a screen should be accessible only to specific roles, apply a role guard at the route level.
Where to see it: The routing module (not assessed — ask your team lead for the guard implementation). The pattern is referenced in every screen's access control section.
[Not documented — WHO: Team Lead; WHAT: The name and location of the AuthGuard and any role-based guards; WHERE: Add the file path here for developer reference]
What it is: The application uses Nebular (@nebular/theme) as its primary UI component library. Nebular provides cards, buttons, menus, dialogs, forms, icons, and layout primitives. Custom application components use the ngx- selector prefix.
Why it was chosen: Nebular is purpose-built for Angular admin dashboards and integrates deeply with the theming system. It provides accessible, themeable components that adapt automatically to the four supported themes (default, dark, cosmic, corporate).
How it affects your work: Before building a custom component, check whether Nebular already provides what you need (nb-card, nb-button, nb-select, nb-calendar, etc.). Use Nebular components for layout and form primitives. Build custom components only for domain-specific UI (e.g., ngx-temperature-dragger, ngx-earning-card).
Where to see it: Every screen in the application uses Nebular components. src/app/@theme/components/ contains the application's own shared components built on top of Nebular.
What it is: The application supports four visual themes — default, dark, cosmic, and corporate — managed by Nebular's NbThemeService. Components that need theme-aware styling subscribe to NbThemeService.getJsTheme() or NbThemeService.onThemeChange() to receive color tokens at runtime.
Why it was chosen: A reactive theme system allows the entire application's visual appearance to change without a page reload. Chart libraries (ECharts, Chart.js, ngx-charts) cannot be styled with CSS alone, so they receive color values programmatically from the theme service.
How it affects your work: Any component that renders a chart, custom SVG, or canvas element must subscribe to the theme service to apply correct colors. Always unsubscribe from theme observables in ngOnDestroy. Never hardcode colors in chart configuration — always source them from config.variables.
Where to see it: EchartsAreaStackComponent, ChartjsBarHorizontalComponent, D3AdvancedPieComponent, DashboardComponent (theme-dependent card sets).
What it is: The assessed codebase has minimal explicit error handling. Most components do not implement catchError on their Observable subscriptions, do not show error state UI, and do not log to any monitoring service.
Why it was chosen: (inferred from the ngx-admin demo template origin — verify against team standards) This is a known gap in the template's implementation, not a deliberate architectural decision. The application is primarily a UI showcase, and production error handling has not been implemented.
How it affects your work: When you add new features that make real API calls, you are responsible for adding error handling. Use RxJS catchError on subscriptions, add an isError state variable to your component, and display a user-facing error message. Do not leave subscriptions without error callbacks in production code.
Where to see it: The absence of error handling is documented in every screen's error handling section. The SmartTableComponent is a representative example of the pattern to avoid.
[Reference + Explanation]
Type: Routing Pattern
When to use: When you need to create a URL namespace for a group of related child routes without adding any visual chrome of your own.
The pattern:
// charts.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'ngx-charts',
// Template contains ONLY a router-outlet — nothing else
template: '<router-outlet></router-outlet>',
})
export class ChartsComponent {}
// Class body is intentionally empty — no state, no services, no logicKey rules:
<router-outlet></router-outlet> — no wrappers, no headers, no layout elementsngx- prefix conventionExample in the codebase: ChartsComponent, FormsComponent, MapsComponent, TablesComponent, ExtraComponentsComponent, ModalOverlaysComponent, EditorsComponent, UiFeaturesComponent.
Type: Component Pattern
When to use: When a component's sole job is to render data passed in via @Input() with no data fetching, no service injection, and no side effects.
The pattern:
// chart-panel-summary.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'ngx-chart-panel-summary',
templateUrl: './chart-panel-summary.component.html',
})
export class ChartPanelSummaryComponent {
// All data comes from the parent via @Input()
@Input() summary: { title: string; value: number }[] = [];
// No constructor, no services, no lifecycle hooks
// No @Output() unless the component needs to communicate user actions upward
}Key rules:
@Input() — never inject data services directly@Output() + EventEmitter to communicate user actions upward to the parentsubscribe() inside a presentational component@Input() values to safe empty states (e.g., [], '', false) to prevent template errors before the parent provides dataChangeDetectionStrategy.OnPush for performance (not currently applied in the codebase but recommended)Example in the codebase: ChartPanelSummaryComponent, StatusCardComponent, TrafficBarComponent, ECommerceLegendChartComponent, FsIconComponent.
Type: Data Fetching Pattern
When to use: When a component needs to load data from a backend (or mock). This is the standard pattern for all data access in this codebase.
The pattern:
// 1. Define the abstract contract in @core/data/
// src/app/@core/data/my-feature.ts
export abstract class MyFeatureData {
abstract getItems(): Observable<MyItem[]>;
}
// 2. Implement the mock in @core/mock/
// src/app/@core/mock/my-feature.service.ts
@Injectable()
export class MyFeatureService extends MyFeatureData {
getItems(): Observable<MyItem[]> {
return of(MOCK_ITEMS); // or an HttpClient call
}
}
// 3. Register in the core module
// src/app/@core/core.module.ts
providers: [
{ provide: MyFeatureData, useClass: MyFeatureService },
]
// 4. Inject the abstract class in the component
@Component({ ... })
export class MyPageComponent implements OnDestroy {
items: MyItem[] = [];
private alive = true;
constructor(private myFeatureService: MyFeatureData) {}
ngOnInit() {
this.myFeatureService.getItems()
.pipe(takeWhile(() => this.alive))
.subscribe(items => this.items = items);
}
ngOnDestroy() {
this.alive = false;
}
}Key rules:
Observable<T> from service methods — never Promise<T> or raw valuescatchError for production code — the current codebase omits this and it is a known gapExample in the codebase: SolarData / DashboardComponent, ElectricityData / ElectricityComponent, SmartTableData / SmartTableComponent.
Type: Component Pattern
When to use: Any time a component subscribes to an Observable that may outlive the component (theme changes, layout changes, data polling, long-lived streams).
The pattern:
@Component({ ... })
export class MyComponent implements OnDestroy {
// The lifecycle flag — initialized to true
private alive = true;
constructor(private themeService: NbThemeService) {}
ngOnInit() {
// Pipe takeWhile before subscribe
this.themeService.getJsTheme()
.pipe(takeWhile(() => this.alive))
.subscribe(config => {
// handle theme change
});
}
ngOnDestroy() {
// Setting alive to false completes all takeWhile subscriptions
this.alive = false;
}
}Key rules:
OnDestroy when subscribing to any ObservabletakeWhile(() => this.alive) or takeUntil(this.destroy$) — never leave a bare subscribe() without cleanupalive = false in ngOnDestroy, not ngOnInittakeWhile approach requires one additional emission after ngOnDestroy before the subscription fully completes — for high-frequency streams, prefer takeUntil with a SubjectECommerceProgressSectionComponent sets this.alive = true in ngOnDestroy instead of false — this is a bug, not a pattern to followExample in the codebase: DashboardComponent, HeaderComponent, EchartsAreaStackComponent, CountryOrdersComponent.
Type: Data Fetching Pattern + Component Pattern
When to use: Any time you render a chart (ECharts, Chart.js, ngx-charts) that must adapt its colors to the active Nebular theme.
The pattern:
@Component({ ... })
export class MyChartComponent implements AfterViewInit, OnDestroy {
option: any = {}; // ECharts option object
private alive = true;
constructor(private theme: NbThemeService) {}
ngAfterViewInit() {
// Use delay(1) to ensure the DOM is ready before ECharts initializes
this.theme.getJsTheme()
.pipe(
delay(1),
takeWhile(() => this.alive),
)
.subscribe(config => {
const eTheme: any = config.variables.myChartNamespace;
// Build the full options object using theme variables
this.option = {
// ... chart config using eTheme.someColor, eTheme.someFont, etc.
};
});
}
ngOnDestroy() {
this.alive = false;
}
}Key rules:
getJsTheme() in ngAfterViewInit, not ngOnInit — the chart container must exist in the DOM firstdelay(1) to avoid ExpressionChangedAfterItHasBeenChecked errorsconfig.variables — never hardcode hex values in chart optionstakeWhile(() => this.alive) for cleanup(chartInit) and call echartsInstance.resize() in response to LayoutService.onSafeChangeLayoutSize()Example in the codebase: EchartsAreaStackComponent, SolarComponent, TrafficChartComponent, ElectricityChartComponent.
Type: Component Pattern
When to use: When a card widget has two faces (front and back) that the user can toggle between.
The pattern:
@Component({
selector: 'ngx-my-card',
templateUrl: './my-card.component.html',
// Template uses [class.flipped]="flipped" on the card container
})
export class MyCardComponent {
flipped = false; // false = front face, true = back face
toggleView() {
this.flipped = !this.flipped;
}
}Key rules:
boolean to represent the two states — never use a string enum for a binary toggleflipped state is local and ephemeral — it resets to false on every component instantiationlocalStorageExample in the codebase: EarningCardComponent, ProfitCardComponent.
Type: Component Pattern
When to use: When you need to open a modal dialog, prompt the user for input, or display a confirmation.
The pattern:
// In the parent component that opens the dialog:
@Component({ ... })
export class MyPageComponent {
constructor(private dialogService: NbDialogService) {}
openDialog() {
const ref = this.dialogService.open(MyDialogComponent, {
context: { title: 'My Dialog Title' }, // Maps to @Input() on the dialog component
});
// Subscribe to the return value if needed
ref.onClose.subscribe(result => {
if (result) {
// handle the returned value
}
});
}
}
// The dialog component itself:
@Component({ ... })
export class MyDialogComponent {
@Input() title: string; // Receives context values as @Input()
constructor(protected ref: NbDialogRef<MyDialogComponent>) {}
dismiss() {
this.ref.close(); // Close with no return value
}
submit(value: string) {
this.ref.close(value); // Close and return a value to the opener
}
}Key rules:
NbDialogRef<T> as protected in the dialog componentref.close() with no argument for cancel/dismiss actionsref.close(value) to return data to the openerref.onClose to receive the returned valuecontext option — Nebular maps context properties to @Input() bindings on the dialog componentExample in the codebase: DialogComponent, ShowcaseDialogComponent, DialogNamePromptComponent.
[How-to]
When: You need to add a new routed page to the application.
Steps:
Create the component directory under the appropriate feature folder:
mkdir src/app/pages/my-feature/my-new-pageGenerate the component using Angular CLI:
ng generate component pages/my-feature/my-new-page --module=my-feature(inferred from Angular CLI conventions — verify the module name against the existing feature module file)
Open the component's .ts file and set the selector to follow the ngx- prefix convention:
@Component({
selector: 'ngx-my-new-page',
templateUrl: './my-new-page.component.html',
styleUrls: ['./my-new-page.component.scss'],
})Register the route in the feature module's routing file (e.g., my-feature-routing.module.ts):
{ path: 'my-new-page', component: MyNewPageComponent },Add the menu item to src/app/pages/pages-menu.ts so it appears in the sidebar:
{
title: 'My New Page',
icon: 'some-icon-outline',
link: '/pages/my-feature/my-new-page',
},Navigate to http://localhost:4200/pages/my-feature/my-new-page to verify the page loads.
Watch out for: If the page renders blank, check that the route is registered in the correct routing module and that the module is imported in the parent module. If the sidebar item does not appear, verify the link path exactly matches the registered route.
When: You need to connect a screen to a real backend API instead of mock data.
Steps:
Locate the abstract data class for the relevant domain in src/app/@core/data/. If one does not exist, create it:
// src/app/@core/data/my-domain.ts
export abstract class MyDomainData {
abstract getItems(): Observable<MyItem[]>;
}Create a concrete HTTP service in src/app/@core/mock/ (or a new services/ directory if your team prefers):
@Injectable()
export class MyDomainService extends MyDomainData {
constructor(private http: HttpClient) { super(); }
getItems(): Observable<MyItem[]> {
return this.http.get<MyItem[]>('/api/my-items').pipe(
catchError(err => {
console.error('Failed to load items', err);
return of([]);
}),
);
}
}Register the concrete service in the core module, replacing the mock:
{ provide: MyDomainData, useClass: MyDomainService },Ensure HttpClientModule is imported in the root or core module.
Watch out for: The existing mock services do not use HttpClient. If you are replacing a mock, you must add HttpClient to the constructor. Also ensure your API base URL is configured in environment.ts rather than hardcoded in the service.
When: You need to change the data returned by an existing mock service, or add a new method to an existing data contract.
Steps:
Open the abstract class in src/app/@core/data/ and add the new method signature:
abstract getItemById(id: string): Observable<MyItem>;Open the concrete implementation in src/app/@core/mock/ and implement the new method:
getItemById(id: string): Observable<MyItem> {
return of(MOCK_ITEMS.find(item => item.id === id));
}Inject the abstract class in the component that needs the new method and call it:
this.myDomainService.getItemById(this.route.snapshot.params['id'])
.pipe(takeWhile(() => this.alive))
.subscribe(item => this.item = item);Watch out for: TypeScript will enforce that the concrete class implements all abstract methods. If you add a method to the abstract class, the compiler will error until the concrete class also implements it. This is intentional — it prevents the UI from calling a method that has no implementation.
When: You need to build a form that collects user input and validates it before submission.
Steps:
Import ReactiveFormsModule in the feature module.
Inject FormBuilder in the component constructor:
constructor(private fb: FormBuilder) {}Define the form group in ngOnInit:
ngOnInit() {
this.form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
});
}Bind the form in the template:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input nbInput formControlName="name" placeholder="Name">
<ng-container *ngIf="form.get('name').invalid && form.get('name').touched">
<p class="caption status-danger">Name is required (min 2 characters)</p>
</ng-container>
<button nbButton type="submit" [disabled]="form.invalid">Submit</button>
</form>Handle submission in the component:
onSubmit() {
if (this.form.valid) {
// call your data service
}
}Watch out for: The existing form showcase screens (/forms/form-layouts, /forms/form-inputs) are static demonstrations with no real form logic. Do not use them as implementation references — they show visual patterns only. For a working reactive form example, look at WindowFormComponent as a starting point (though it also lacks validation and is a known gap).
When: You need to protect a new route so only authenticated users can access it.
Steps:
Locate the route guard file (ask your team lead for the path — see the note in Section 4.4).
Add the guard to the route definition in the feature routing module:
{
path: 'my-new-page',
component: MyNewPageComponent,
canActivate: [AuthGuard], // or RoleGuard, etc.
}Do not add any authentication logic inside the component itself. The component should render unconditionally once activated.
Watch out for: If you apply a guard to a parent route (e.g., the pages route), all child routes inherit the guard automatically. Verify whether the parent route already has a guard before adding one to a child route — you may be duplicating protection unnecessarily.
[Not documented — WHO: Team Lead; WHAT: The exact file path and class name of the AuthGuard and any role-based guards; WHERE: Replace the AuthGuard reference in step 2 above]
When: You need to run the test suite before committing or reviewing a pull request.
Steps:
Run all unit tests:
ng test(inferred from Angular CLI conventions — verify against package.json scripts field)
Run tests in headless mode (for CI):
ng test --watch=false --browsers=ChromeHeadlessRun a single test file:
ng test --include='**/my-component.spec.ts'Watch out for: No co-located test files were found in the assessed source bundle. The test suite may be minimal or absent. Ask your team lead about the current test coverage expectations and whether Karma/Jasmine or Jest is configured.
[Not documented — WHO: Team Lead; WHAT: The current state of the test suite, whether Jest or Karma is used, and the expected test coverage threshold; WHERE: Update the test commands above if Jest is used instead of Karma]
When: A screen is not displaying data and you suspect an API call is failing.
Steps:
Open the browser's DevTools (F12) and navigate to the Network tab.
Filter by XHR/Fetch and reload the page. Look for requests with 4xx or 5xx status codes.
If no network requests appear, the screen may be using mock data. Check whether the component injects an abstract data class and whether the concrete implementation makes HTTP calls or returns static data.
If a request is failing with a 401, verify that the authentication guard is correctly configured and that the user's session is valid.
If a request is failing with a CORS error, verify that the backend API allows requests from http://localhost:4200.
Add temporary console.log statements to the subscription's error callback to capture the error object:
this.myService.getData()
.pipe(takeWhile(() => this.alive))
.subscribe({
next: data => this.data = data,
error: err => console.error('Data load failed:', err),
});Remove the console.log before committing.
Watch out for: Most existing components do not have error callbacks on their subscriptions. If a service call fails silently, the component will simply display empty data with no indication of the failure. This is a known gap — see Section 4.7.
When: You need to add a new ECharts visualization to a page.
Steps:
Create the component following the Theme-Aware Chart Pattern.
Add the echarts directive to the template:
<div echarts [options]="option" (chartInit)="onChartInit($event)" class="echart"></div>Define the chart height in the component's SCSS:
.echart {
height: 300px;
}Subscribe to LayoutService.onSafeChangeLayoutSize() to handle responsive resizing:
constructor(
private theme: NbThemeService,
private layoutService: LayoutService,
) {}
ngAfterViewInit() {
this.layoutService.onSafeChangeLayoutSize()
.pipe(takeWhile(() => this.alive))
.subscribe(() => this.resizeChart());
this.theme.getJsTheme()
.pipe(delay(1), takeWhile(() => this.alive))
.subscribe(config => this.setOptions(config.variables.myNamespace));
}
onChartInit(ec: any) {
this.echartsInstance = ec;
}
resizeChart() {
if (this.echartsInstance) {
setTimeout(() => this.echartsInstance.resize(), 0);
}
}Define the chart namespace in the Nebular theme variables if you need custom colors. Otherwise, use existing color tokens like config.variables.primary, config.variables.success, etc.
Watch out for: ECharts will not render if the container <div> has zero height. Always define a height in the component's SCSS. The setTimeout(..., 0) in resizeChart() is a known workaround for ECharts not correctly calculating dimensions synchronously — keep it.
[How-to]
Symptoms: The Leaflet or Google Maps container is visible but the map tiles do not load, or the map container has zero height.
Cause: Leaflet requires the map container <div> to have an explicit CSS height. If the SCSS file does not define a height, Leaflet renders into a zero-height container and appears blank. A secondary cause is the tile URL using http:// on an HTTPS-served application, triggering mixed-content blocking.
Fix:
.scss file and verify a height is defined:
:host {
display: block;
height: 400px; // or use a percentage/vh unit
}
// or on the map container class:
.map-container {
height: 400px;
}http:// to https://:
// In leaflet.component.ts
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { ... })map.invalidateSize() if the map was already initialized.Symptoms: A chart renders correctly on first load but shows no data or incorrect colors after the user switches the application theme.
Cause: The chart's option object was built once using the initial theme's color tokens and is not being rebuilt when the theme changes. The getJsTheme() observable emits on every theme change, but the subscription may have been placed in ngOnInit instead of ngAfterViewInit, or the option object is not being reassigned.
Fix:
ngOnInit to ngAfterViewInit.this.option (not just mutates nested properties):
// Correct: reassign the entire option object
this.option = { ...newOptions };
// Incorrect: mutating nested properties may not trigger re-render
this.option.series[0].data = newData;takeWhile(() => this.alive) is applied so the subscription stays active across theme changes.Symptoms: Angular's development mode logs a warning about a subscription not being cleaned up, or you observe that a component's callback fires after the component has been destroyed (visible as errors like "Cannot read property of null" after navigation).
Cause: A component subscribes to an Observable but does not implement OnDestroy or does not set alive = false in ngOnDestroy. The subscription continues to fire after the component is removed from the DOM.
Fix:
OnDestroy on the component class.private alive = true as a class property.takeWhile(() => this.alive) before every subscribe() call.this.alive = false in ngOnDestroy.Special case: If you see this.alive = true in an ngOnDestroy method, that is a bug (present in ECommerceProgressSectionComponent). It should be this.alive = false.
Symptoms: The TinyMCE editor area is blank or the toolbar does not appear. The browser console may show tinymce is not defined or a 404 for skin assets.
Cause: TinyMCE is loaded via a CDN script tag and requires the CKEDITOR_BASEPATH / CKEDITOR_BASEPATH equivalent (window['CKEDITOR_BASEPATH']) to be set before the library loads. If the CDN is unreachable, or if the assets/skins/lightgray directory is missing from the build output, the editor will fail silently.
Fix:
assets/skins/lightgray exists in the src/assets/ directory and is included in angular.json's assets array.ckeditor.loader.ts (or the equivalent TinyMCE loader file) is imported in the editors module before the component is instantiated.Symptoms: You open a dialog using NbDialogService, subscribe to ref.onClose, but the emitted value is always undefined even when the user clicks "Submit".
Cause: The dialog component is calling this.ref.close() with no argument (the cancel/dismiss path) instead of this.ref.close(value) (the submit path). This commonly happens when the submit button is not correctly bound to the submit() method, or when the template reference variable for the input is not correctly resolved.
Fix:
(click)="submit(nameInput.value)" where nameInput is a #nameInput template reference on the <input> element.submit() method calls this.ref.close(value) with the value, not this.ref.close().ref.onClose.subscribe(result => { if (result) { ... } }).Symptoms: Angular's development mode throws ExpressionChangedAfterItHasBeenChecked when a chart component initializes, typically pointing to the option binding on the echarts directive.
Cause: The theme subscription fires synchronously during ngAfterViewInit, setting this.option after Angular has already completed change detection for that cycle.
Fix: Apply delay(1) to the theme observable to defer the option assignment by one tick:
this.theme.getJsTheme()
.pipe(
delay(1), // This one-tick delay resolves the ExpressionChangedAfterItHasBeenChecked error
takeWhile(() => this.alive),
)
.subscribe(config => {
this.option = buildChartOptions(config.variables);
});This pattern is already applied in SolarComponent, TrafficChartComponent, and ElectricityChartComponent — follow their implementation.
[Reference]
| # | Gotcha | Why It Matters | What To Do Instead |
|---|---|---|---|
| 1 | ngOnDestroy sets alive = true instead of false |
ECommerceProgressSectionComponent has a bug where ngOnDestroy sets this.alive = true, meaning the subscription is never cleaned up. |
Always set this.alive = false in ngOnDestroy. Copy the pattern from DashboardComponent or EchartsAreaStackComponent. |
| 2 | Subscribing to getJsTheme() in ngOnInit instead of ngAfterViewInit |
Chart libraries need the DOM container to exist before they can initialize. Subscribing in ngOnInit fires before the view is rendered, causing charts to fail silently. |
Always subscribe to theme observables that drive chart initialization in ngAfterViewInit. |
| 3 | Hardcoding colors in chart options | The application supports four themes. Hardcoded colors will look wrong in dark/cosmic/corporate themes. | Always source colors from config.variables inside the getJsTheme() subscription callback. |
| 4 | Mutating option nested properties instead of reassigning |
ECharts and Angular's change detection may not detect mutations to nested object properties. The chart will not re-render. | Always reassign this.option to a new object reference: this.option = { ...newConfig }. |
| 5 | Missing delay(1) on theme subscription in ngAfterViewInit |
Without the 1ms delay, Angular throws ExpressionChangedAfterItHasBeenChecked in development mode because the option binding changes after change detection completes. |
Apply .pipe(delay(1), takeWhile(() => this.alive)) to all theme subscriptions in ngAfterViewInit. |
| 6 | Leaflet map container has no CSS height | Leaflet renders into a zero-height container and appears blank. No error is thrown. | Always define an explicit height on the map container element in the component's SCSS. |
| 7 | TinyMCE keyup-only change detection |
The TinyMCEComponent only emits content changes on keyup events. Mouse-only formatting changes (clicking Bold, inserting a table) are silently ignored. |
If you need to capture all editor changes, add listeners for change and input events in addition to keyup. |
| 8 | echartsIntance typo |
Multiple chart components use echartsIntance (missing the 's') as the property name for the ECharts instance. Searching for echartsInstance will not find these properties. |
When working with these components, search for echartsIntance. Do not fix the typo without updating all references. |
| 9 | horizontalBar chart type is deprecated in Chart.js v3+ |
ChartjsBarHorizontalComponent uses the horizontalBar type, which was removed in Chart.js v3. Upgrading Chart.js will break this component. |
If upgrading Chart.js, replace type: 'horizontalBar' with type: 'bar' and add indexAxis: 'y' to the options. |
| 10 | Social media links in the footer use href="#" |
All four social icon links in FooterComponent point to # (the current page). They are non-functional placeholders. |
Update the href values to real social media profile URLs before deploying to production. |
| 11 | getJsTheme() subscription in the constructor |
KittenComponent and HeaderComponent subscribe to getJsTheme() in the constructor rather than ngOnInit. This is a minor anti-pattern that can complicate testing. |
For new components, subscribe in ngOnInit or ngAfterViewInit. |
| 12 | No error handling on Observable subscriptions | The vast majority of data service subscriptions have no error callback or catchError operator. Failed API calls fail silently. |
For any subscription that makes a real HTTP call, add catchError and an error state variable to show the user a meaningful message. |
[Tutorial]
This tutorial walks you through adding a new read-only dashboard widget — a task that exercises the most common patterns in this codebase. By the end, you will have created a new component, registered it as a route, added it to the sidebar, and connected it to a data service.
Add a new page to the Dashboard section that displays a simple "Device Status Summary" — a list of device names and their current status (online/offline), sourced from a mock data service.
ngx- selector conventionpages-menu.tstakeWhile/alive lifecycle patternStep 1: Create the component
ng generate component pages/dashboard/device-summary --module=dashboardExpected result: Four files are created (device-summary.component.ts, .html, .scss, .spec.ts) and the component is declared in DashboardModule.
Step 2: Set the selector and template
Open device-summary.component.ts and update the selector:
@Component({
selector: 'ngx-device-summary',
templateUrl: './device-summary.component.html',
styleUrls: ['./device-summary.component.scss'],
})
export class DeviceSummaryComponent implements OnDestroy {
devices: { name: string; online: boolean }[] = [];
private alive = true;
constructor(private deviceService: DeviceStatusData) {}
ngOnInit() {
this.deviceService.getDevices()
.pipe(takeWhile(() => this.alive))
.subscribe(devices => this.devices = devices);
}
ngOnDestroy() {
this.alive = false;
}
}Step 3: Write the template
Open device-summary.component.html:
<nb-card>
<nb-card-header>Device Status Summary</nb-card-header>
<nb-card-body>
<div *ngFor="let device of devices">
<span>{{ device.name }}</span>
<span [class.text-success]="device.online"
[class.text-danger]="!device.online">
{{ device.online ? 'Online' : 'Offline' }}
</span>
</div>
</nb-card-body>
</nb-card>Step 4: Create the abstract data service
Create src/app/@core/data/device-status.ts:
import { Observable } from 'rxjs';
export abstract class DeviceStatusData {
abstract getDevices(): Observable<{ name: string; online: boolean }[]>;
}Step 5: Create the mock implementation
Create src/app/@core/mock/device-status.service.ts:
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { DeviceStatusData } from '../data/device-status';
@Injectable()
export class DeviceStatusService extends DeviceStatusData {
getDevices(): Observable<{ name: string; online: boolean }[]> {
return of([
{ name: 'Front Door Camera', online: true },
{ name: 'Thermostat', online: true },
{ name: 'Garage Sensor', online: false },
]);
}
}Step 6: Register the service in the core module
Open src/app/@core/core.module.ts and add to the providers array:
{ provide: DeviceStatusData, useClass: DeviceStatusService },Step 7: Register the route
Open the dashboard routing module (e.g., src/app/pages/dashboard/dashboard-routing.module.ts) and add:
{ path: 'device-summary', component: DeviceSummaryComponent },Step 8: Add the sidebar menu item
Open src/app/pages/pages-menu.ts and add to the dashboard section:
{
title: 'Device Summary',
icon: 'monitor-outline',
link: '/pages/dashboard/device-summary',
},Step 9: Navigate to the new page
Open your browser and navigate to http://localhost:4200/pages/dashboard/device-summary.
Expected result: A Nebular card with the title "Device Status Summary" and a list of three devices with their online/offline status.
Confirm the following before considering the task complete:
Once you are comfortable with this pattern, try these more complex tasks:
of(...) in the mock service with interval(5000).pipe(switchMap(() => of(updatedDevices))) to simulate periodic data refresh.catchError and display an error message in the template when the service fails.Angular — The TypeScript-based SPA framework used to build this application. Provides component-based architecture, dependency injection, the Angular Router, and the Angular CLI.
Nebular (@nebular/theme) — An open-source Angular UI component library by Akveo, built on Eva Design System. Provides nb-card, nb-button, nb-menu, nb-dialog, nb-calendar, nb-select, and many other components, plus the theming system (NbThemeService).
Apache ECharts — An open-source JavaScript charting library (Apache 2.0 license) used for the majority of charts in this application. Accessed via the ngx-echarts Angular directive wrapper.
ngx-echarts — An Angular directive library that wraps Apache ECharts, enabling declarative chart configuration via [options] and [merge] input bindings and a (chartInit) output event.
Chart.js — A JavaScript charting library used for the horizontal bar chart (/charts/chartjs). Accessed via the ng2-charts Angular wrapper.
ng2-charts — An Angular wrapper for Chart.js that provides the <chart> component directive.
@swimlane/ngx-charts — An Angular-native SVG charting library used for the D3 advanced pie chart (/charts/d3).
Leaflet — An open-source JavaScript library for interactive maps. Used via the @asymmetrik/ngx-leaflet Angular wrapper.
@asymmetrik/ngx-leaflet — An Angular directive library wrapping Leaflet, providing the leaflet attribute directive and [leafletOptions] binding.
TinyMCE — A widely-used open-source WYSIWYG rich text editor. Loaded via CDN in this application (version 4.x, lightgray skin).
CKEditor — A second WYSIWYG rich text editor. Loaded via CDN (version 4.6.2, full-all build).
RxJS — Reactive Extensions for JavaScript. Used throughout the application for all asynchronous data flows, subscription management, and event streams.
ng2-smart-table — An open-source Angular data grid library providing the <ng2-smart-table> component with built-in CRUD, filtering, sorting, and pagination.
NbDateService<Date> — A Nebular service providing locale-aware date arithmetic utilities (addDay, getMonthStart, getMonthEnd, getMonthName). The <Date> type parameter specifies native JavaScript Date objects.
NbThemeService — Nebular's theme management service. Provides getJsTheme() (Observable of current theme variables), onThemeChange() (Observable of theme name changes), onMediaQueryChange() (Observable of breakpoint changes), and currentTheme (synchronous current theme name).
NbDialogService — Nebular's service for programmatically opening modal dialog overlays. Returns an NbDialogRef<T> for each opened dialog.
NbDialogRef<T> — A reference object injected into dialog components. Provides close(result?) to dismiss the dialog and onClose Observable to receive the return value in the opener.
NbMenuService — Nebular's menu management service. Provides navigateHome() to navigate to the configured home route and onItemClick() to react to menu item selections.
NbSidebarService — Nebular's service for controlling the sidebar's open/compact/closed state. Used in HeaderComponent to toggle the sidebar.
NbMediaBreakpointsService — Nebular's service providing a map of named breakpoints (xs, sm, md, lg, xl) to pixel widths.
NbIconLibraries — Nebular's service for registering and retrieving icon packs (SVG and font-based). Used in IconsComponent to register Font Awesome and Ionicons.
LayoutService — An internal application utility service that broadcasts layout size change events (e.g., when the sidebar is toggled). Provides onSafeChangeLayoutSize() — a debounced (350ms) Observable used by chart components to trigger resize.
LocationStrategy — An Angular service (@angular/common) that abstracts URL strategy. Used in TinyMCEComponent and TemperatureDraggerComponent to resolve the application's base href for asset paths and SVG fragment URLs.
NbCalendarDayPickerComponent — A Nebular base component that renders a grid of day cells for a single month. Extended by CalendarKitMonthCellComponent to create a custom month cell.
NbCalendarMonthModelService — A Nebular service that computes the week-row layout for a given month.
TranslationWidth — An Angular @angular/common enum controlling the verbosity of locale-aware date/time strings (Wide = full name, Abbreviated = short, Narrow = single character).
LocalDataSource — A class from ng2-smart-table that acts as an in-memory data adapter, handling filtering, sorting, and pagination client-side.
NbCalendarRange<Date> — A Nebular interface representing a date range with start: Date and end: Date properties, used as the model for nb-calendar-range.
NbCalendarCell<D, S> — A Nebular interface that custom calendar cell components must implement. Requires select: EventEmitter<S> and selectedValue: S.
NbComponentStatus — A Nebular union type ('primary' | 'success' | 'info' | 'warning' | 'danger' | 'basic' | 'control') mapping to theme-defined colors.
NbComponentSize — A Nebular union type ('tiny' | 'small' | 'medium' | 'large' | 'giant') for component sizing.
NbComponentShape — A Nebular union type ('rectangle' | 'semi-round' | 'round') for border-radius styling.
NbGlobalPosition — A union of NbGlobalPhysicalPosition and NbGlobalLogicalPosition used to specify toast/overlay placement.
NbToastrService — Nebular's service for displaying toast notification overlays.
NbToastrConfig — Nebular's configuration interface for toast options (status, duration, position, hasIcon, destroyByClick, preventDuplicates).
NbWindowService — Nebular's service for opening floating, draggable window overlays (distinct from modal dialogs).
NbWindowRef — A reference object injected into window content components, providing close() to dismiss the window.
NbTreeGridDataSource<T> — A Nebular data source class for the tree grid component, handling tree expansion and sorting.
NbTreeGridDataSourceBuilder<T> — An Angular injectable service used to construct NbTreeGridDataSource instances from TreeNode<T>[] arrays.
NbSortDirection — A Nebular enum with values ASCENDING, DESCENDING, and NONE.
NbSortRequest — A Nebular interface representing a sort event with column (string) and direction (NbSortDirection) properties.
Shell Component — A component whose template contains only <router-outlet></router-outlet> and whose class body is empty. Used to create URL namespaces for groups of child routes without contributing any UI. Examples: ChartsComponent, FormsComponent, MapsComponent.
Presentational Component — A component that holds no state, injects no data services, and renders purely based on @Input() bindings. Communicates upward only via @Output() EventEmitters. Examples: ChartPanelSummaryComponent, StatusCardComponent, TrafficBarComponent.
Abstract Service Pattern — The data access pattern used throughout this application. An abstract class in @core/data/ defines the service contract; a concrete class in @core/mock/ provides the implementation. Components inject the abstract class, enabling the concrete implementation to be swapped without changing component code.
takeWhile/alive Pattern — The RxJS subscription lifecycle management pattern used in this codebase. A private alive = true boolean is used as the predicate for takeWhile(() => this.alive). Setting alive = false in ngOnDestroy completes all subscriptions. See Section 5.4.
Theme-Aware Chart Pattern — The pattern for building charts that adapt to the active Nebular theme. Subscribe to NbThemeService.getJsTheme() in ngAfterViewInit with delay(1), source all colors from config.variables, and reassign the full option object on each emission. See Section 5.5.
Card Flip Pattern — A UI pattern where a card widget has two faces toggled by a single boolean state variable (flipped). The CSS flip animation is handled in SCSS; no Angular animations module is used. See Section 5.6.
ngx- prefix — The component selector namespace convention used throughout this application. All custom Angular components use ngx- as a prefix (e.g., ngx-earning-card, ngx-status-card). This distinguishes application components from Nebular components (nb-) and native HTML elements.
@theme module — The Angular feature module at src/app/@theme/ containing shared layout components (header, footer, search-input, tiny-mce wrapper) and layout wrappers used across the entire application.
@core module — The Angular feature module at src/app/@core/ containing abstract data service classes, mock implementations, and utility services (LayoutService, AnalyticsService, SeoService).
delay(1) workaround — A one-tick async delay applied to theme subscriptions in ngAfterViewInit to prevent ExpressionChangedAfterItHasBeenChecked errors. This is a known timing workaround, not a general pattern to apply everywhere.
setTimeout(..., 0) resize workaround — A zero-delay setTimeout used in chart resizeChart() methods to defer the ECharts resize() call until after the DOM has settled. Documented as a known workaround in multiple chart components.
shareReplay — An RxJS operator used in LayoutService to multicast the layout size change stream to multiple subscribers (one per chart component on a page) without creating multiple upstream subscriptions.
Earning Card — A UI widget in the e-commerce dashboard displaying earnings/revenue metrics. Has a front face (live-updating chart) and a back face (pie chart breakdown). Toggled via the Card Flip Pattern.
Profit Card — A UI widget in the e-commerce dashboard displaying profit metrics. Has a front face (animated bar chart) and a back face (area chart). Toggled via the Card Flip Pattern.
Traffic Reveal Card — A UI widget in the e-commerce dashboard displaying traffic analytics. Has a front face (traffic list with delta indicators) and a back face (traffic bar chart). Toggled via the Card Flip Pattern.
Status Card — A dashboard widget representing a smart home device (Light, Roller Shades, Wireless Audio, Coffee Maker). Displays an icon, title, and ON/OFF toggle state. Clicking the card toggles the state locally.
Solar Value — A numeric value representing current solar energy output, displayed as a gauge/donut chart on the dashboard. Sourced from the SolarData abstract service.
kWh (Kilowatt-hour) — The unit of electrical energy used in the Electricity dashboard screen. Represents the amount of energy consumed by a device over time.
delta — A change indicator used in multiple e-commerce widgets. Contains up: boolean (direction of change) and value: number (magnitude). Used to render up/down arrow indicators.
period — A string representing a time granularity for chart data aggregation. Valid values across the application are 'week', 'month', and 'year'.
alive — A boolean lifecycle flag used with the takeWhile RxJS operator. Set to true on construction, set to false in ngOnDestroy to complete all active subscriptions.
flipped — The boolean state variable used in Card Flip Pattern components. false = front face visible; true = back face visible.
currentTheme — A string property on theme-aware components holding the active Nebular theme name ('default', 'dark', 'cosmic', 'corporate').
echartsIntance — (Note: intentional typo in the codebase) The stored reference to the live Apache ECharts chart instance, obtained via the (chartInit) event. Used to call resize() imperatively. Search for this exact spelling when working with chart components.
PositionModel — A simple data class representing a geographic coordinate pair (lat, lng). Default values (53.9, 27.5667) correspond to Minsk, Belarus. Used in the Search Map feature.
CardSettings — A TypeScript interface defining the configuration for a dashboard status card: title (display name), iconClass (Nebular icon identifier), and type (Nebular color/status variant).
LiveUpdateChart — An interface representing the full data payload for the Earning Card front face: liveChart (time-series data points), delta (change direction and magnitude), and dailyIncome (total earnings for the day).
PieChart — An interface representing a single slice of the Earning Card back face pie chart: { value: number, name: string }.
OrdersChart — A data model for the Charts Panel orders chart: chartLabel: string[] (x-axis labels) and linesData: number[][] (one array per line series).
ProfitChart — A data model for the Charts Panel profit chart: chartLabel: string[] and data: number[][] (one array per bar series).
TrafficList — A data structure for the Traffic Reveal Card front face: a date, a total value, a delta object, and a comparison object with previous/next period data.
TrafficBar — A data structure for the Traffic Reveal Card back face: data: number[], labels: string[], and formatter: string.
FSEntry — The domain model for the Tree Grid screen, representing a file system entry with name, size, kind (either 'dir' or a file type string), and optional items (child count for directories).
TreeNode<T> — A generic interface representing a node in a tree structure with data: T, optional children: TreeNode<T>[], and optional expanded: boolean. Used by the Tree Grid screen.
NbCalendarRange — See Technologies section.
botReplies — A static array of rule objects used by the Chat screen's ChatService.reply() method. Each rule contains a regExp for matching user input and a reply template object defining the bot's response type and content.
MENU_ITEMS — The exported constant array of NbMenuItem objects in pages-menu.ts that defines the complete sidebar navigation structure for the authenticated section of the application.
Smart Home / Home Automation — The application domain for the Dashboard section. The dashboard monitors and controls home devices (lights, shades, audio, coffee maker) and energy systems (solar, electricity).
E-commerce Section — The area of the application (/e-commerce/...) dedicated to online store management analytics, including earnings, traffic, country orders, visitor statistics, and profit metrics.
Extra Components — A section of the application (/extra-components/...) dedicated to showcasing supplementary UI components (calendar, progress bar, spinner, alert, chat, form inputs) as a living style guide.