Developer Onboarding Guide

myowjaYOY/ngx-admin

Generated by DocAgent — automated codebase documentation analysis. Based on analysis of 3 screens. Subject matter expert review is recommended before distribution.

April 19, 2026

ngx-admin

Developer Onboarding Guide

July 2026


1. Welcome & Overview

Scope: This Developer Onboarding Guide covers 100 screens of the ngx-admin application. The screens assessed are: Editors (/editors), Contacts (/dashboard/contacts), Electricity (/dashboard/electricity), Kitten (/dashboard/kitten), Rooms (/dashboard/rooms), Traffic (/dashboard/traffic), Country Orders (/e-commerce/country-orders), Chart (/e-commerce/country-orders/chart), Back Side (/e-commerce/traffic-reveal-card/back-side), Map (/e-commerce/country-orders/map), Progress Section (/e-commerce/progress-section), Traffic Reveal Card (/e-commerce/traffic-reveal-card), Legend Chart (/e-commerce/legend-chart), Front Side (/e-commerce/profit-card/front-side), Traffic Cards Header (/e-commerce/traffic-reveal-card/traffic-cards-header), User Activity (/e-commerce/user-activity), Calendar Kit (/extra-components/calendar-kit), Chat (/extra-components/chat), Progress Bar (/extra-components/progress-bar), Form Inputs (/extra-components/form-inputs), Day Cell (/extra-components/calendar/day-cell), Month Cell (/extra-components/calendar-kit/month-cell), Nebular Select (/extra-components/form-inputs/nebular-select), Extra Components (/extra-components), Alert (/extra-components/alert), Visitors Analytics (/e-commerce/visitors-analytics), Spinner In Buttons (/extra-components/spinner/spinner-in-buttons), Maps (/maps), Spinner Color (/extra-components/spinner/spinner-color), Leaflet (/maps/leaflet), Pages (/pages), Tables (/tables), Dashboard (/dashboard), Icons (/ui-features/icons), E Commerce (/e-commerce), Footer (/@theme/components/footer), Chart Panel Header (/e-commerce/charts-panel/chart-panel-header), Spinner (/extra-components/spinner), Chart Panel Summary (/e-commerce/charts-panel/chart-panel-summary), Player (/dashboard/rooms/player), Header (/@theme/components/header), Calendar (/extra-components/calendar), Form Layouts (/forms/form-layouts), Window Form (/modal-overlays/window/window-form), Room Selector (/dashboard/rooms/room-selector), Ui Features (/ui-features), Traffic Bar (/e-commerce/traffic-reveal-card/front-side/traffic-bar), Spinner In Tabs (/extra-components/spinner/spinner-in-tabs), Interactive Progress Bar (/extra-components/progress-bar/interactive-progress-bar), Datepicker (/forms/datepicker), Form Inputs (/forms/form-inputs), Charts (/e-commerce/charts-panel/charts), Bubble (/maps/bubble), Profit Card (/e-commerce/profit-card), Slide Out (/e-commerce/slide-out), Back Side (/e-commerce/profit-card/back-side), Smart Table (/tables/smart-table), Typography (/ui-features/typography), Modal Overlays (/modal-overlays), Visitors Statistics (/e-commerce/visitors-analytics/visitors-statistics), Ckeditor (/editors/ckeditor), Grid (/ui-features/grid), Echarts (/charts/echarts), Not Found (/miscellaneous/not-found), Visitors Analytics Chart (/e-commerce/visitors-analytics/visitors-analytics-chart), Tiny Mce (/editors/tiny-mce), Front Side (/e-commerce/earning-card/front-side), Electricity Chart (/dashboard/electricity/electricity-chart), Search Fields (/ui-features/search-fields), Dialog Name Prompt (/modal-overlays/dialog/dialog-name-prompt), Tooltip (/modal-overlays/tooltip), Search Map (/maps/search-map), Map (/maps/search-map/map), Window (/modal-overlays/window), Dialog (/modal-overlays/dialog), Miscellaneous (/miscellaneous), Gmaps (/maps/gmaps), Popovers (/modal-overlays/popovers), Search (/maps/search-map/search), Tree Grid (/tables/tree-grid), Showcase Dialog (/modal-overlays/dialog/showcase-dialog), Toastr (/modal-overlays/toastr), Search Input (/@theme/components/search-input), Tiny Mce (/@theme/components/tiny-mce), Status Card (/dashboard/status-card), Front Side (/e-commerce/traffic-reveal-card/front-side), Earning Card (/e-commerce/earning-card), Solar (/dashboard/solar), Security Cameras (/dashboard/security-cameras), Temperature Dragger (/dashboard/temperature/temperature-dragger), Charts (/charts), Chartjs (/charts/chartjs), Temperature (/dashboard/temperature), D3 (/charts/d3), Back Side (/e-commerce/earning-card/back-side), Charts Panel (/e-commerce/charts-panel), Forms (/forms), Weather (/dashboard/weather), Buttons (/forms/buttons), Spinner Sizes (/extra-components/spinner/spinner-sizes). Components, patterns, and features not included in the assessed documentation are outside the scope of this document. This document reflects a partial view of the application.

Document disclosure: Generated by DocAgent — automated codebase documentation analysis. Subject matter expert review is recommended before distribution.

What this application does: ngx-admin is an Angular-based admin dashboard application built on the Nebular UI framework. It serves as both a feature-rich dashboard template and a component showcase, targeting developers and administrators who need to monitor IoT/smart-home data, e-commerce analytics, and system metrics. The application demonstrates a wide range of UI patterns including interactive charts (ECharts, Chart.js, D3), data tables, map visualizations, form components, modal overlays, and real-time data widgets.

Tech stack at a glance:

How this guide is organized: Sections marked (Tutorial) walk you through an experience step by step. Sections marked (How-to) give you procedures for specific tasks. Sections marked (Reference) are lookup tables and factual descriptions. Sections marked (Explanation) provide context and rationale for architectural decisions.

Who to ask:

[Not documented — WHO: Team Lead; WHAT: Names and contact details for each code owner area listed above; WHERE: Replace the placeholder entries in the "Who to ask" list above.]


2. Environment Setup

(Tutorial)

This section walks you from a fresh machine to a running local development environment. Follow every step in order.

2.1 Prerequisites

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

Verify your setup:

node --version   # Should print v18.x.x or v20.x.x
npm --version    # Should print 9.x.x or 10.x.x
ng version       # Should print Angular CLI version

[Not documented — WHO: Team Lead; WHAT: The exact Node.js and Angular CLI versions pinned for this project (check .nvmrc, package.json engines field, or CI configuration); WHERE: Replace the version ranges in the Prerequisites table above with exact pinned versions.]

2.2 Clone and Install

git clone [repository-url]
cd ngx-admin
npm install

Expected output: npm prints a dependency tree and completes without errors. You may see deprecation warnings — these are expected and non-blocking.

[Not documented — WHO: Team Lead; WHAT: The repository URL; WHERE: Replace [repository-url] in the clone command above.]

2.3 Environment Configuration

This application uses Angular's environment.ts pattern for environment-specific configuration. (inferred from Angular CLI conventions — verify against src/environments/ directory)

# Check what environment files exist
ls src/environments/

You should see at minimum environment.ts (development) and environment.prod.ts (production).

Variables you may need to configure:

Variable Purpose Where to Get It
Google Maps API key Required for /maps/gmaps and /maps/search-map screens Ask your team lead
TinyMCE CDN base path Defaults to //cdn.ckeditor.com/4.6.2/full-all/ — may need updating Check src/app/@theme/components/tiny-mce/

[Not documented — WHO: Team Lead; WHAT: The complete list of environment variables required for local development, including any API keys, feature flags, or backend URLs; WHERE: Insert a complete environment variable table in this section.]

For most screens in this application, no environment configuration is required — the majority of data is served from in-memory mock services.

2.4 Start the Development Server

ng serve

Expected output:

** Angular Live Development Server is listening on localhost:4200 **
✔ Compiled successfully.

The first build may take 30–60 seconds. Subsequent builds are faster due to incremental compilation.

2.5 Verify It Works

  1. Open your browser and navigate to http://localhost:4200.
  2. You should see the ngx-admin dashboard with a sidebar navigation menu on the left.
  3. Click E-commerce in the sidebar — you should see charts and KPI cards load.
  4. Click Dashboard — you should see IoT-style widgets including a solar gauge and temperature controls.

[Screenshot: The ngx-admin dashboard home page with the Nebular sidebar visible on the left and e-commerce KPI cards in the main content area.]

If the page loads but charts appear blank, wait 2–3 seconds — ECharts initializes asynchronously after the Nebular theme service emits. This is expected behavior.


3. Codebase Orientation

(Reference)

3.1 Directory Structure

src/
├── app/
│   ├── @core/                  — Core module: data services, utilities, mock providers
│   │   ├── data/               — Abstract data service classes (injection tokens)
│   │   ├── mock/               — Concrete mock implementations of data services
│   │   └── utils/              — Shared utilities (LayoutService, AnalyticsService)
│   ├── @theme/                 — Theme module: shared layout components
│   │   └── components/         — Header, Footer, SearchInput, TinyMCE wrapper
│   └── pages/                  — Feature pages organized by route segment
│       ├── dashboard/          — IoT/smart-home dashboard screens
│       ├── e-commerce/         — E-commerce analytics screens
│       ├── extra-components/   — Component showcase screens
│       ├── forms/              — Form demonstration screens
│       ├── maps/               — Map integration screens
│       ├── charts/             — Chart library showcase screens
│       ├── tables/             — Data table screens
│       ├── editors/            — Rich text editor screens
│       ├── modal-overlays/     — Dialog, window, popover, toastr screens
│       ├── ui-features/        — Typography, icons, grid screens
│       └── miscellaneous/      — 404 and other utility screens
├── assets/
│   ├── map/                    — GeoJSON world map data (world.json)
│   ├── leaflet-countries/      — GeoJSON country boundaries for Leaflet
│   └── skins/                  — TinyMCE editor skin assets
└── environments/
    ├── environment.ts          — Development environment config
    └── environment.prod.ts     — Production environment config

3.2 Key Files

File Purpose When You'll Touch It
src/app/pages/pages.component.ts Root shell for all authenticated pages; owns the sidebar MENU_ITEMS When adding a new top-level navigation section
src/app/pages/pages-menu.ts Static MENU_ITEMS array defining the sidebar navigation tree Every time you add a new page that needs a sidebar link
src/app/@core/data/ Abstract data service classes (e.g., users.ts, electricity.ts) When adding a new data domain or extending an existing one
src/app/@core/mock/ Concrete mock implementations of all data services When you need to change what data the app displays in development
src/app/@core/utils/layout.service.ts Broadcasts layout size change events; consumed by all chart components for resize When debugging chart resize issues or adding a new chart
src/app/@theme/components/header/header.component.ts Global header with theme switcher, user menu, and sidebar toggle When modifying global navigation or theme switching behavior
src/app/pages/e-commerce/charts-panel/charts-panel.component.ts Reference implementation of the full data-loading + chart pattern When building a new data-driven chart page
src/app/pages/dashboard/temperature/temperature-dragger/temperature-dragger.component.ts Complex custom SVG interactive widget When building custom SVG-based interactive components
src/app/pages/tables/smart-table/smart-table.component.ts Reference implementation of ng2-smart-table with inline CRUD When building a new data table screen
src/app/pages/modal-overlays/dialog/dialog.component.ts Reference implementation of NbDialogService patterns When adding dialogs, windows, or overlays
src/app/pages/maps/search-map/search/search.component.ts Google Places Autocomplete integration with NgZone When integrating third-party DOM-manipulating libraries
src/app/pages/e-commerce/country-orders/map/map.component.ts Leaflet map with GeoJSON overlay and theme integration When building Leaflet-based map screens
src/assets/map/world.json GeoJSON world map data used by the Bubble map screen When updating map data
src/environments/environment.ts Development environment variables When adding new API keys or feature flags

3.3 Naming Conventions

What Convention Example
Component files kebab-case.component.ts electricity-chart.component.ts
Component selectors ngx- prefix + kebab-case ngx-electricity-chart
Nebular component selectors nb- prefix nb-card, nb-icon
Abstract data service classes PascalCase + Data suffix ElectricityData, UserData
Mock service classes PascalCase + MockService suffix ElectricityMockService
Feature directories kebab-case matching route segment e-commerce/, extra-components/
Interface names PascalCase, no prefix Electricity, TrafficList, Camera
Enum names PascalCase NgxLegendItemColor
Constants SCREAMING_SNAKE_CASE MENU_ITEMS, VIEW_BOX_SIZE
Private component properties camelCase alive, echartsIntance
@Input() properties camelCase chartValue, frontCardData
@Output() emitters camelCase, event-noun pattern selectPie, positionChanged
SCSS files Same name as component electricity-chart.component.scss

4. Architecture & Key Decisions

(Explanation)

4.1 Application Framework and Routing

What it is: Angular with a hierarchical router using nested <router-outlet> elements.

Why it was chosen: ngx-admin is built on the Angular framework. The nested routing approach allows feature sections (e.g., /e-commerce, /dashboard) to share a common layout shell while independently loading their child screens.

How it affects your work: Every feature section has a "shell component" — a component whose only job is to render <router-outlet>. When you add a new screen, you add it as a child route under the appropriate shell. You never add UI to a shell component itself.

Where to see it: PagesComponent (/pages) is the top-level shell. ECommerceComponent (/e-commerce), DashboardComponent (/dashboard), MapsComponent (/maps), TablesComponent (/tables), ChartsComponent (/charts), FormsComponent (/forms), EditorsComponent (/editors), ExtraComponentsComponent (/extra-components), ModalOverlaysComponent (/modal-overlays), UiFeaturesComponent (/ui-features), and MiscellaneousComponent (/miscellaneous) are all section-level shells.

4.2 UI Component Strategy — Nebular

What it is: All UI chrome (cards, buttons, menus, dialogs, icons, tabs, inputs) comes from the Nebular component library (@nebular/theme), an open-source Angular UI kit built on the Eva Design System.

Why it was chosen: Nebular provides a cohesive, themeable component set that integrates tightly with Angular's DI system. It ships with four built-in themes (default, dark, cosmic, corporate) and a NbThemeService that allows components to react to theme changes at runtime.

How it affects your work: When you need a card, use <nb-card>. When you need a button, use <button nbButton>. When you need a dialog, use NbDialogService. Do not introduce a second UI library. All Nebular components use the nb- selector prefix.

Where to see it: Every screen in the application uses Nebular components. The PagesComponent template uses <nb-menu> for the sidebar. The HeaderComponent uses NbThemeService and NbSidebarService.

4.3 Abstract Data Service Pattern

What it is: Every data domain (electricity, users, traffic, cameras, etc.) is defined as an Angular abstract class in src/app/@core/data/. The abstract class acts as an Angular injection token. A concrete mock implementation is registered in the module and injected at runtime.

Why it was chosen: This pattern decouples components from their data sources. Swapping from mock data to a real HTTP API requires only changing the provider registration in the module — no component code changes. It also makes unit testing straightforward: inject a mock that returns controlled Observable values.

How it affects your work: When you need data in a component, inject the abstract class (e.g., ElectricityData), not the concrete implementation. When you need to change what data is returned, edit the mock in src/app/@core/mock/. When you need a new data domain, create a new abstract class in src/app/@core/data/ and a corresponding mock in src/app/@core/mock/.

Where to see it: src/app/@core/data/electricity.ts (abstract), src/app/@core/mock/electricity.service.ts (concrete mock). The pattern is used for every data domain: users.ts, traffic-chart.ts, security-cameras.ts, temperature-humidity.ts, etc.

4.4 State Management Strategy

What it is: Component-local state held as class properties. No global state management library (NgRx, Akita, NGXS) is used.

Why it was chosen: The application is a dashboard template with largely independent screens. Each screen manages its own data lifecycle. The complexity of a global store is not warranted for this use case.

How it affects your work: State lives on the component class as plain TypeScript properties. When a component is destroyed, its state is lost. If you need to share state between sibling components, use a shared service or pass data through a common parent via @Input()/@Output() bindings.

Where to see it: ElectricityComponent holds listData, chartData, type, and currentTheme as class properties. RoomsComponent holds expanded, selected, isDarkTheme, and breakpoint as class properties.

4.5 Data Fetching Pattern

What it is: RxJS Observable subscriptions, often using forkJoin for parallel fetches. Subscriptions are managed with the takeWhile(() => this.alive) operator and ngOnDestroy.

Why it was chosen: Angular's ecosystem is built around RxJS. The abstract data services return Observable<T>, making RxJS the natural choice. forkJoin is used when a screen needs multiple datasets before it can render (e.g., the Electricity screen needs both list data and chart data simultaneously).

How it affects your work: You must always clean up subscriptions. The codebase uses the alive boolean flag pattern with takeWhile. Modern Angular (16+) prefers takeUntilDestroyed() — either pattern is acceptable, but be consistent within a file. Never subscribe without a cleanup mechanism.

Where to see it: ContactsComponent uses forkJoin + takeWhile. ElectricityComponent uses forkJoin + takeWhile. SecurityCamerasComponent uses takeUntil(this.destroy$) — a slightly more modern variant.

4.6 Theming and Responsive Layout

What it is: Nebular's NbThemeService provides two reactive streams: getJsTheme() (emits the current theme's JavaScript variable map) and onMediaQueryChange() (emits when the viewport crosses a breakpoint). Components subscribe to these to adapt their appearance and layout.

Why it was chosen: The application supports four themes (default, dark, cosmic, corporate). Charts and custom SVG components cannot be styled with CSS alone — they need JavaScript-accessible color values. NbThemeService.getJsTheme() provides those values reactively.

How it affects your work: Any component that renders a chart or custom SVG must subscribe to getJsTheme() to get theme-appropriate colors. Any component that needs to respond to viewport changes (e.g., changing button sizes, adjusting card sizes) subscribes to onMediaQueryChange(). Both subscriptions must be cleaned up in ngOnDestroy.

Where to see it: ElectricityChartComponent, OrdersChartComponent, ProfitChartComponent, VisitorsAnalyticsChartComponent, and SolarComponent all subscribe to getJsTheme(). RoomsComponent, CountryOrdersComponent, and TypographyComponent subscribe to onMediaQueryChange().

4.7 Chart Integration Pattern

What it is: Apache ECharts is the primary charting library, integrated via the ngx-echarts Angular directive. Charts are configured by building an options object from theme variables and input data, then binding it to [options] on a <div echarts> element.

Why it was chosen: ECharts provides a rich set of chart types with good performance. The ngx-echarts wrapper provides Angular-idiomatic bindings. The LayoutService provides a debounced resize event that chart components subscribe to, ensuring charts reflow correctly when the sidebar is toggled.

How it affects your work: Every chart component follows the same lifecycle: (1) subscribe to getJsTheme() in ngAfterViewInit with a delay(1), (2) build the options object using theme variables and @Input() data, (3) store the ECharts instance via (chartInit), (4) subscribe to LayoutService.onSafeChangeLayoutSize() to call echartsInstance.resize(). See Section 5.6 for the full pattern.

Where to see it: OrdersChartComponent, ProfitChartComponent, ElectricityChartComponent, EarningLiveUpdateChartComponent, VisitorsAnalyticsChartComponent, TrafficBarChartComponent.

4.8 Shell Component Pattern

What it is: A component whose template contains only <router-outlet> and whose class body is empty. It exists solely to provide a named URL segment and a rendering target for child routes.

Why it was chosen: Angular's router requires a component at every route level. Shell components satisfy this requirement without adding unnecessary logic or UI to route segments that are purely structural.

How it affects your work: When you see a component with an empty class body and a <router-outlet> template, do not add logic to it. If you need shared UI across all screens in a section (e.g., a section header), add it to the shell component's template. If you need shared data, use a service.

Where to see it: EditorsComponent, ExtraComponentsComponent, MapsComponent, TablesComponent, ChartsComponent, FormsComponent, ModalOverlaysComponent, UiFeaturesComponent, MiscellaneousComponent.

4.9 Subscription Lifecycle Management

What it is: Two patterns are used to prevent memory leaks from RxJS subscriptions: the alive flag pattern (takeWhile(() => this.alive) + ngOnDestroy sets alive = false) and the destroy$ subject pattern (takeUntil(this.destroy$) + ngOnDestroy calls destroy$.next()).

Why it was chosen: Angular components are destroyed when the user navigates away. Any active RxJS subscription that is not cleaned up will continue to run and attempt to update the destroyed component's state, causing memory leaks and potential errors.

How it affects your work: Every subscription you create must be cleaned up. Prefer the destroy$ subject pattern for new code — it is more reliable than takeWhile because it completes the observable immediately rather than waiting for the next emission. See Section 5.2 for the pattern.

Where to see it: SecurityCamerasComponent uses destroy$. Most other components use alive + takeWhile.


5. Development Patterns & Conventions

(Reference + Explanation)

5.1 Shell / Routing Component

Type: Routing Pattern

When to use: When you need a URL segment that groups child routes but has no UI of its own.

The pattern:

import { Component } from '@angular/core';

@Component({
  selector: 'ngx-my-section',
  template: '<router-outlet></router-outlet>',
})
export class MySectionComponent {}

Key rules:

Example in the codebase: EditorsComponent, MapsComponent, ChartsComponent.


5.2 Data-Loading Page Component

Type: Component Pattern

When to use: When a page needs to fetch data from a service and display it.

The pattern:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { MyData } from '../../@core/data/my-data';

@Component({
  selector: 'ngx-my-page',
  templateUrl: './my-page.component.html',
})
export class MyPageComponent implements OnInit, OnDestroy {
  // Declare state properties
  items: MyItem[];

  // Subject used to signal subscription cleanup
  private destroy$ = new Subject<void>();

  constructor(private myService: MyData) {}

  ngOnInit() {
    // Fetch data here, not in the constructor
    this.myService.getItems()
      .pipe(takeUntil(this.destroy$))
      .subscribe(items => {
        this.items = items;
      });
  }

  ngOnDestroy() {
    // Signal all takeUntil operators to complete
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Key rules:

Example in the codebase: ContactsComponent, ElectricityComponent, SecurityCamerasComponent.


5.3 Presentational / Dumb Component

Type: Component Pattern

When to use: When a component only renders data passed to it via @Input() and emits events via @Output().

The pattern:

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'ngx-my-card',
  templateUrl: './my-card.component.html',
})
export class MyCardComponent {
  // All data comes in via @Input()
  @Input() title: string;
  @Input() value: number;

  // User actions go out via @Output()
  @Output() selected = new EventEmitter<string>();

  onSelect(id: string) {
    this.selected.emit(id);
  }
}

Key rules:

Example in the codebase: ChartPanelSummaryComponent, TrafficBarComponent, LegendChartComponent, StatusCardComponent.


5.4 Abstract Data Service

Type: Data Fetching Pattern

When to use: When you need to add a new data domain to the application.

The pattern:

// src/app/@core/data/my-domain.ts
import { Observable } from 'rxjs';

export interface MyItem {
  id: number;
  name: string;
  value: number;
}

export abstract class MyDomainData {
  abstract getItems(): Observable<MyItem[]>;
  abstract getItemById(id: number): Observable<MyItem>;
}
// src/app/@core/mock/my-domain.service.ts
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { MyDomainData, MyItem } from '../data/my-domain';

@Injectable()
export class MyDomainMockService extends MyDomainData {
  private items: MyItem[] = [
    { id: 1, name: 'Item A', value: 42 },
  ];

  getItems() {
    return of(this.items);
  }

  getItemById(id: number) {
    return of(this.items.find(i => i.id === id));
  }
}

Key rules:

Example in the codebase: ElectricityData / ElectricityMockService, UserData / UsersMockService.


5.5 Theme-Aware Component

Type: Component Pattern

When to use: When a component needs to adapt its colors or styles based on the active Nebular theme.

The pattern:

import { Component, OnDestroy } from '@angular/core';
import { NbThemeService } from '@nebular/theme';
import { takeWhile } from 'rxjs/operators';

@Component({ selector: 'ngx-my-widget', templateUrl: './my-widget.component.html' })
export class MyWidgetComponent implements OnDestroy {
  private alive = true;
  currentTheme: string;
  themeColors: any; // typed as any; use specific interface if available

  constructor(private themeService: NbThemeService) {
    this.themeService.getJsTheme()
      .pipe(takeWhile(() => this.alive))
      .subscribe(config => {
        this.currentTheme = config.name;
        // Access theme-specific variables, e.g. config.variables.myWidget
        this.themeColors = config.variables.myWidget;
      });
  }

  ngOnDestroy() {
    this.alive = false;
  }
}

Key rules:

Example in the codebase: KittenComponent, TrafficComponent, DashboardComponent, HeaderComponent.


5.6 ECharts Chart Component

Type: Data Fetching Pattern + Component Pattern

When to use: When you need to render an interactive chart using Apache ECharts.

The pattern:

import { Component, Input, OnChanges, AfterViewInit, OnDestroy } from '@angular/core';
import { NbThemeService } from '@nebular/theme';
import { LayoutService } from '../../../@core/utils/layout.service';
import { takeWhile, delay } from 'rxjs/operators';

@Component({
  selector: 'ngx-my-chart',
  template: `<div echarts [options]="option" class="echart"
               (chartInit)="onChartInit($event)"></div>`,
})
export class MyChartComponent implements AfterViewInit, OnChanges, OnDestroy {
  @Input() chartData: number[];

  option: any = {};
  private echartsInstance: any;
  private alive = true;

  constructor(
    private theme: NbThemeService,
    private layoutService: LayoutService,
  ) {
    // Subscribe to layout resize events
    this.layoutService.onSafeChangeLayoutSize()
      .pipe(takeWhile(() => this.alive))
      .subscribe(() => this.resizeChart());
  }

  ngAfterViewInit() {
    // Delay(1) avoids ExpressionChangedAfterItHasBeenCheckedError
    this.theme.getJsTheme()
      .pipe(takeWhile(() => this.alive), delay(1))
      .subscribe(config => {
        const eTheme: any = config.variables.myChart;
        this.setOptions(eTheme);
      });
  }

  ngOnChanges() {
    // Only update after initial options are set
    if (this.option.series) {
      this.updateChartOptions();
    }
  }

  onChartInit(ec: any) {
    this.echartsInstance = ec;
  }

  private setOptions(eTheme: any) {
    this.option = {
      // Full ECharts configuration using eTheme colors and this.chartData
    };
  }

  private updateChartOptions() {
    // Partial update — only change what changed
    if (this.echartsInstance) {
      this.echartsInstance.setOption({ series: [{ data: this.chartData }] });
    }
  }

  private resizeChart() {
    if (this.echartsInstance) {
      setTimeout(() => this.echartsInstance.resize(), 0);
    }
  }

  ngOnDestroy() {
    this.alive = false;
  }
}

Key rules:

Example in the codebase: OrdersChartComponent, ProfitChartComponent, ElectricityChartComponent, TrafficBarChartComponent.


5.7 Smart Table Page

Type: Component Pattern

When to use: When you need a data table with built-in sorting, filtering, and inline CRUD.

The pattern:

import { Component } from '@angular/core';
import { LocalDataSource } from 'ng2-smart-table';
import { MyData } from '../../@core/data/my-data';

@Component({
  selector: 'ngx-my-table',
  templateUrl: './my-table.component.html',
})
export class MyTableComponent {
  settings = {
    actions: {
      add: true,
      edit: true,
      delete: true,
      confirmDelete: true, // Enables (deleteConfirm) output event
    },
    columns: {
      id: { title: 'ID', type: 'number' },
      name: { title: 'Name', type: 'string' },
    },
  };

  source: LocalDataSource;

  constructor(private service: MyData) {
    this.source = new LocalDataSource(this.service.getData());
  }

  onDeleteConfirm(event): void {
    if (window.confirm('Are you sure?')) {
      event.confirm.resolve();
    } else {
      event.confirm.reject();
    }
  }
}

Key rules:

Example in the codebase: SmartTableComponent.


5.8 Dialog Component

Type: Component Pattern

When to use: When you need to open a modal dialog that can return a value to the caller.

The pattern:

// The dialog content component
import { Component, Input } from '@angular/core';
import { NbDialogRef } from '@nebular/theme';

@Component({
  selector: 'ngx-my-dialog',
  template: `
    <nb-card>
      <nb-card-header>{{ title }}</nb-card-header>
      <nb-card-body><!-- content --></nb-card-body>
      <nb-card-footer>
        <button nbButton (click)="cancel()">Cancel</button>
        <button nbButton status="primary" (click)="submit(result)">OK</button>
      </nb-card-footer>
    </nb-card>
  `,
})
export class MyDialogComponent {
  @Input() title: string;
  result: string;

  constructor(protected ref: NbDialogRef<MyDialogComponent>) {}

  cancel() { this.ref.close(); }
  submit(value: string) { this.ref.close(value); }
}

// The caller component
import { NbDialogService } from '@nebular/theme';

this.dialogService.open(MyDialogComponent, {
  context: { title: 'My Dialog' },
}).onClose.subscribe(result => {
  if (result) { /* handle result */ }
});

Key rules:

Example in the codebase: ShowcaseDialogComponent, DialogNamePromptComponent, DialogComponent.


6. Common Development Tasks

(How-to)

6.1 How to: Add a New Page/Screen

When: You need to add a new screen to an existing section (e.g., a new dashboard widget page).

Steps:

  1. Create a new directory under the appropriate section in src/app/pages/:

    mkdir src/app/pages/dashboard/my-new-screen
  2. Create the component files:

    ng generate component pages/dashboard/my-new-screen --module pages/dashboard/dashboard.module

    Expected result: Angular CLI creates my-new-screen.component.ts, .html, .scss, and registers it in DashboardModule.

  3. Add the route to the section's routing module (e.g., dashboard-routing.module.ts):

    { path: 'my-new-screen', component: MyNewScreenComponent }
  4. Add a sidebar navigation entry in src/app/pages/pages-menu.ts:

    {
      title: 'My New Screen',
      icon: 'some-icon-outline',
      link: '/pages/dashboard/my-new-screen',
    }
  5. Navigate to http://localhost:4200/pages/dashboard/my-new-screen and verify the page loads.

Watch out for: The route path in pages-menu.ts must include the full path from the root (e.g., /pages/dashboard/my-new-screen), not just the relative segment.


6.2 How to: Add a New API Endpoint (Data Service)

When: You need to expose a new type of data to components.

Steps:

  1. Create the abstract class in src/app/@core/data/:

    // src/app/@core/data/my-domain.ts
    export interface MyItem { id: number; name: string; }
    export abstract class MyDomainData {
      abstract getItems(): Observable<MyItem[]>;
    }
  2. Create the mock implementation in src/app/@core/mock/:

    // src/app/@core/mock/my-domain.service.ts
    @Injectable()
    export class MyDomainMockService extends MyDomainData {
      getItems() { return of([{ id: 1, name: 'Test' }]); }
    }
  3. Register the provider in the core module (find the providers array in src/app/@core/core.module.ts or equivalent):

    { provide: MyDomainData, useClass: MyDomainMockService }
  4. Inject MyDomainData in your component and call getItems().

Watch out for: Always inject the abstract class, not the mock. If you inject the mock directly, swapping to a real HTTP service later will require changing every component that uses it.


6.3 How to: Add or Modify a Data Query

When: You need to change what data a screen displays.

Steps:

  1. Locate the mock service for the data domain in src/app/@core/mock/.
  2. Modify the data array or the method that returns it.
  3. If you need to add a new method, add it to the abstract class in src/app/@core/data/ first, then implement it in the mock.
  4. Restart the dev server if the mock uses module-level constants (changes to those require a rebuild).

Watch out for: Some mock services return static arrays defined at the top of the file. Others compute data dynamically. Check whether the method uses of(staticArray) or generates data on each call.


6.4 How to: Add a New Form with Validation

When: You need a form that collects user input and validates it before submission.

Steps:

  1. Import ReactiveFormsModule in your feature module.

  2. Build the form in the component class:

    import { FormBuilder, FormGroup, Validators } from '@angular/forms';
    
    form: FormGroup;
    
    constructor(private fb: FormBuilder) {
      this.form = this.fb.group({
        name: ['', [Validators.required, Validators.minLength(3)]],
        email: ['', [Validators.required, Validators.email]],
      });
    }
    
    onSubmit() {
      if (this.form.valid) {
        // handle submission
      }
    }
  3. Bind the form in the template using Nebular inputs:

    <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 3 chars)</p>
      </ng-container>
      <button nbButton type="submit" [disabled]="form.invalid">Submit</button>
    </form>

Watch out for: The WindowFormComponent in the codebase is an example of what not to do — it renders form fields with no Angular form binding and no validation. Always use Reactive Forms for any form that submits data.


6.5 How to: Handle Authentication in a New Route

When: You need to protect a new route so only authenticated users can access it.

Steps:

  1. Locate the existing AuthGuard (or equivalent) in the application.

    [Not documented — WHO: Team Lead; WHAT: The name and file path of the authentication route guard used in this project; WHERE: Replace step 1 with the actual guard name and import path.]

  2. Add the guard to your route definition:

    {
      path: 'my-new-screen',
      component: MyNewScreenComponent,
      canActivate: [AuthGuard],
    }
  3. Verify that navigating to the route while unauthenticated redirects to the login page.

Watch out for: Do not add authentication logic inside the component itself. Route guards are the correct place for access control in Angular.


6.6 How to: Run Tests

When: You want to verify your changes haven't broken existing functionality.

Steps:

  1. Run the unit test suite:

    ng test

    Expected result: Karma opens a browser window and runs all .spec.ts files. Results appear in the terminal.

  2. Run tests for a specific file:

    ng test --include='**/electricity.component.spec.ts'
  3. Run end-to-end tests (if configured):

    ng e2e

Watch out for: The assessed documentation found no co-located .spec.ts test files for any of the 100 screens. This means the test suite may be empty or tests may live in a separate directory. Ask your team lead for the testing setup before assuming ng test will produce meaningful results.

[Not documented — WHO: Team Lead; WHAT: Whether a test suite exists, where test files are located, and which test runner (Karma, Jest, Cypress, Playwright) is configured; WHERE: Update the steps in this section with the actual test commands.]


6.7 How to: Debug a Failing Data Service Call

When: A screen is not displaying data and you suspect the data service is failing silently.

Steps:

  1. Open the component that fetches the data and locate the .subscribe() call.

  2. Add a temporary error handler:

    this.myService.getData()
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: data => { this.data = data; },
        error: err => { console.error('Data service failed:', err); },
      });
  3. Open the browser DevTools console and reload the page. Look for the error message.

  4. If no error appears, check whether the mock service is returning an empty array. Open src/app/@core/mock/ and inspect the relevant service.

  5. If the service returns data but the template shows nothing, check whether the template has a null guard (e.g., *ngIf="data" or data?.property).

Watch out for: The majority of data service subscriptions in this codebase have no error handler. Silent failures are the most common cause of blank screens. Always add an error handler when debugging.


6.8 How to: Add a New Sidebar Navigation Item

When: You've added a new page and need it to appear in the sidebar.

Steps:

  1. Open src/app/pages/pages-menu.ts.

  2. Find the appropriate section in the MENU_ITEMS array (e.g., under "Dashboard" or "E-commerce").

  3. Add a new NbMenuItem entry:

    {
      title: 'My New Screen',
      icon: 'activity-outline',  // Eva icon name
      link: '/pages/dashboard/my-new-screen',
    }
  4. Save the file. The dev server will hot-reload and the new item will appear in the sidebar.

Watch out for: The link property must be the full absolute path starting with /pages/. Using a relative path will cause the menu item to not highlight correctly when active.


7. Debugging & Troubleshooting

(How-to)

7.1 Common Error: Chart Renders Blank on First Load

Symptoms: A screen with an ECharts chart loads but the chart area is empty or shows a blank canvas. The chart may appear after switching themes or resizing the window.

Cause: The getJsTheme() subscription in ngAfterViewInit fires before the ECharts instance is ready, or the options object is set before the theme resolves. The delay(1) operator is missing or the ngOnChanges guard (if (this.option.series)) is absent.

Fix:

  1. Verify that getJsTheme() is called in ngAfterViewInit, not the constructor.
  2. Verify that delay(1) is piped before the subscribe:
    this.theme.getJsTheme()
      .pipe(takeWhile(() => this.alive), delay(1))
      .subscribe(config => { this.setOptions(config.variables); });
  3. Verify that ngOnChanges guards against premature updates:
    ngOnChanges() {
      if (this.option.series) { this.updateChartOptions(); }
    }

7.2 Common Error: SVG url() References Break on Non-Root Deployments

Symptoms: Custom SVG components (like TemperatureDraggerComponent) render incorrectly — clip paths or gradients are missing. The issue appears only when the app is deployed at a sub-path (e.g., /app/ instead of /).

Cause: SVG url(#id) references require absolute URLs in some browsers. When the app is deployed at a non-root base href, the relative url(#id) syntax breaks.

Fix: Use the getUrlPath(id) pattern from TemperatureDraggerComponent:

constructor(
  private location: Location,
  private locationStrategy: LocationStrategy,
) {}

getUrlPath(id: string): string {
  const baseHref = this.locationStrategy.getBaseHref().replace(/\/$/, '');
  const path = this.location.path().replace(/\/$/, '');
  return `url(${baseHref}${path}#${id})`;
}

Use getUrlPath('my-clip-path') in the template instead of url(#my-clip-path).


7.3 Common Error: TypeError: Cannot read properties of undefined on Data Binding

Symptoms: The browser console shows TypeError: Cannot read properties of undefined (reading 'someProperty'). The screen renders blank or partially.

Cause: A component's @Input() data is undefined when the template first renders. This happens because data is loaded asynchronously and the template tries to access a property before the data arrives.

Fix:

  1. Add null guards in the template using the safe navigation operator:

    <span>{{ item?.name }}</span>
  2. Or use *ngIf to defer rendering until data is available:

    <div *ngIf="items">
      <div *ngFor="let item of items">{{ item.name }}</div>
    </div>
  3. Or provide a default value in the component:

    items: MyItem[] = [];

7.4 Common Error: Memory Leak / Subscription Not Cleaned Up

Symptoms: The browser DevTools memory profiler shows growing heap usage. Console warnings about "setState on unmounted component" equivalents. Stale data appearing after navigation.

Cause: An RxJS subscription was created but never unsubscribed. This is the most common bug in this codebase — many components use takeWhile(() => this.alive) but forget to set this.alive = false in ngOnDestroy, or set it to true instead of false (a known bug in ECommerceProgressSectionComponent).

Fix:

  1. Verify ngOnDestroy is implemented and sets alive = false:

    ngOnDestroy() {
      this.alive = false; // NOT true
    }
  2. For new code, prefer the destroy$ pattern which is harder to get wrong:

    private destroy$ = new Subject<void>();
    
    ngOnDestroy() {
      this.destroy$.next();
      this.destroy$.complete();
    }

7.5 Common Error: ExpressionChangedAfterItHasBeenCheckedError in Chart Components

Symptoms: Angular throws ExpressionChangedAfterItHasBeenCheckedError in the browser console when a chart component initializes. The chart may still render, but the error indicates a change detection timing problem.

Cause: The getJsTheme() subscription fires synchronously during ngAfterViewInit and updates a binding that Angular has already checked in the current change detection cycle.

Fix: Add delay(1) to the theme subscription in ngAfterViewInit:

ngAfterViewInit() {
  this.theme.getJsTheme()
    .pipe(takeWhile(() => this.alive), delay(1)) // delay(1) is the fix
    .subscribe(config => {
      this.setOptions(config.variables);
    });
}

7.6 Common Error: ECharts Chart Does Not Resize After Sidebar Toggle

Symptoms: After collapsing or expanding the sidebar, an ECharts chart remains at its old size — it either overflows its container or leaves empty space.

Cause: ECharts calculates its canvas dimensions at render time. When the sidebar toggles, the container width changes, but ECharts is not notified.

Fix:

  1. Verify the component subscribes to LayoutService.onSafeChangeLayoutSize():

    this.layoutService.onSafeChangeLayoutSize()
      .pipe(takeWhile(() => this.alive))
      .subscribe(() => this.resizeChart());
  2. Verify resizeChart() wraps the resize call in setTimeout:

    private resizeChart() {
      if (this.echartsInstance) {
        setTimeout(() => this.echartsInstance.resize(), 0);
      }
    }
  3. Verify LayoutService is provided in the module. If it is not injected, the subscription will never fire.


8. Gotchas & Pitfalls

(Reference)

# Gotcha Why It Matters What To Do Instead
1 ngOnDestroy sets alive = true instead of false ECommerceProgressSectionComponent has this bug. The subscription is never cleaned up, causing a memory leak. Always set this.alive = false in ngOnDestroy. Double-check every component that uses the alive pattern.
2 Data fetching in the constructor Several components (e.g., TrafficComponent, ElectricityComponent) call service methods in the constructor. This makes unit testing harder and can cause issues with Angular's lifecycle. Move data fetching to ngOnInit.
3 No error handler on .subscribe() The vast majority of subscriptions in this codebase have no error callback. API failures are silently swallowed, leaving the UI blank with no user feedback. Always add { error: err => console.error(err) } at minimum. In production, report to an error tracking service.
4 forkJoin with deprecated multi-argument syntax forkJoin(obs1, obs2) (multiple arguments) was removed in RxJS 7. Several components use this pattern. Use forkJoin([obs1, obs2]) (array syntax) for all new code.
5 echartsIntance typo Multiple chart components spell the property echartsIntance (missing 's'). This is a consistent typo across the codebase — do not "fix" it in existing files without updating all references. Use echartsInstance (correct spelling) in new components.
6 takeWhile vs takeUntil takeWhile(() => this.alive) evaluates the predicate on every emission. If the component is destroyed between emissions, one extra emission may be processed. Prefer takeUntil(this.destroy$) for new code — it completes immediately on destroy.
7 Hardcoded maxValue="20" in CountryOrdersComponent The chart's y-axis maximum is hardcoded to 20. If any country's data exceeds 20, the chart clips the data silently. Derive maxValue dynamically from the actual data range.
8 Traffic dropdown has no effect on data The TrafficComponent period dropdown updates type but never passes it to the data service. The chart always shows the same data regardless of selection. Wire the type value to the service call: this.service.getTrafficChartData(this.type).
9 CKEditor 4 is end-of-life The CKEditor integration uses version 4.6.2 (2017), which reached end-of-life in June 2023. It is loaded from a CDN with a protocol-relative URL (//cdn.ckeditor.com/...) that will fail on HTTPS pages. Upgrade to CKEditor 5 or replace with TinyMCE. At minimum, change the URL to https://.
10 CountryOrdersMapComponent null reference on invalid countryId If countryId does not match any GeoJSON feature, selectFeature(null) is called, which throws TypeError: Cannot read properties of null when accessing featureLayer.feature.properties.name. Add a null guard before emitting: if (featureLayer) { this.selectEvent.emit(...); }.
11 ChatService.reply() mutates shared botReplies array The bot reply logic directly mutates the module-level botReplies constant. Repeated calls permanently alter the source data for the session. Deep-clone the reply object before mutating: const reply = JSON.parse(JSON.stringify(botReply.reply)).
12 window:mousemove HostListener fires globally TemperatureDraggerComponent listens to window:mousemove for the entire lifetime of the component, even when the user is not dragging. This fires on every mouse movement across the entire page. Add/remove the listener dynamically only while isMouseDown is true, using Renderer2.

9. First-Task Checklist

(Tutorial)

This tutorial walks you through adding a new read-only data display page to the dashboard section. It exercises the most common patterns in the codebase: the abstract data service, the data-loading component, and the sidebar navigation.

9.1 The Task

Add a new dashboard page called "Energy Summary" at /pages/dashboard/energy-summary. The page displays a list of energy readings fetched from a mock data service, rendered inside a Nebular card.

9.2 What You'll Learn

9.3 Step-by-Step Walkthrough

Step 1: Create the data service interface and abstract class.

Create src/app/@core/data/energy-summary.ts:

import { Observable } from 'rxjs';

export interface EnergyReading {
  source: string;
  value: number;
  unit: string;
}

export abstract class EnergySummaryData {
  abstract getReadings(): Observable<EnergyReading[]>;
}

Expected result: The file compiles without errors.

Step 2: Create the mock implementation.

Create src/app/@core/mock/energy-summary.service.ts:

import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { EnergySummaryData, EnergyReading } from '../data/energy-summary';

@Injectable()
export class EnergySummaryMockService extends EnergySummaryData {
  private readings: EnergyReading[] = [
    { source: 'Solar', value: 6.4, unit: 'kWh' },
    { source: 'Grid', value: 12.1, unit: 'kWh' },
    { source: 'Battery', value: 3.2, unit: 'kWh' },
  ];

  getReadings() {
    return of(this.readings);
  }
}

Step 3: Register the provider in the core module.

Open src/app/@core/core.module.ts (or the equivalent module that registers mock providers). Add to the providers array:

{ provide: EnergySummaryData, useClass: EnergySummaryMockService }

[Not documented — WHO: Team Lead; WHAT: The exact file path and array name where mock providers are registered; WHERE: Update step 3 with the actual file path.]

Step 4: Generate the page component.

ng generate component pages/dashboard/energy-summary

Expected result: Angular CLI creates the component files and registers the component in DashboardModule (or prompts you to specify a module).

Step 5: Implement the component.

Open src/app/pages/dashboard/energy-summary/energy-summary.component.ts and replace its contents:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { EnergySummaryData, EnergyReading } from '../../../@core/data/energy-summary';

@Component({
  selector: 'ngx-energy-summary',
  templateUrl: './energy-summary.component.html',
})
export class EnergySummaryComponent implements OnInit, OnDestroy {
  readings: EnergyReading[];
  private destroy$ = new Subject<void>();

  constructor(private energyService: EnergySummaryData) {}

  ngOnInit() {
    this.energyService.getReadings()
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: readings => { this.readings = readings; },
        error: err => { console.error('Failed to load energy readings:', err); },
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Step 6: Write the template.

Open src/app/pages/dashboard/energy-summary/energy-summary.component.html:

<nb-card>
  <nb-card-header>Energy Summary</nb-card-header>
  <nb-card-body>
    <div *ngIf="readings; else loading">
      <div *ngFor="let reading of readings" class="reading-row">
        <span class="source">{{ reading.source }}</span>
        <span class="value">{{ reading.value }} {{ reading.unit }}</span>
      </div>
    </div>
    <ng-template #loading>
      <p>Loading...</p>
    </ng-template>
  </nb-card-body>
</nb-card>

Step 7: Add the route.

Open the dashboard routing module (find the file that defines routes for /dashboard). Add:

{ path: 'energy-summary', component: EnergySummaryComponent }

[Not documented — WHO: Team Lead; WHAT: The file path of the dashboard routing module; WHERE: Update step 7 with the actual file path.]

Step 8: Add the sidebar entry.

Open src/app/pages/pages-menu.ts and add to the dashboard section:

{
  title: 'Energy Summary',
  icon: 'flash-outline',
  link: '/pages/dashboard/energy-summary',
}

9.4 Verification

  1. Run ng serve if it is not already running.
  2. Navigate to http://localhost:4200.
  3. Look for "Energy Summary" in the sidebar under the Dashboard section.
  4. Click it — you should see a card with three energy readings: Solar, Grid, and Battery.
  5. Open the browser DevTools Network tab and confirm no HTTP requests are made (all data comes from the mock service).
  6. Navigate away from the page and back — the data should reload correctly.
  7. Open the DevTools Memory tab and take a heap snapshot before and after navigating away. The component should not retain memory after destruction.

9.5 What to Do Next

Once this task is complete, try these progressively more complex tasks:


10. Glossary

10.1 Technologies

Angular — The TypeScript-based web application framework used to build this application. Provides the component model, dependency injection system, router, and reactive forms.

Nebular (@nebular/theme) — An open-source Angular UI component library built on the Eva Design System. Provides all UI chrome in this application: cards (nb-card), buttons (nbButton), menus (nb-menu), dialogs (NbDialogService), icons (nb-icon), tabs (nb-tabset), inputs (nbInput), and the theming system (NbThemeService).

Apache ECharts — An open-source JavaScript charting library that renders interactive charts on HTML5 canvas. Used as the primary charting engine in this application.

ngx-echarts — An Angular directive wrapper for Apache ECharts. Provides the echarts attribute directive, [options] binding, and (chartInit) event output.

Chart.js — A second charting library used in the /charts/chartjs screen. Typically integrated via the ng2-charts Angular wrapper.

D3.js — A low-level JavaScript library for producing SVG-based data visualizations. Used in the /charts/d3 screen.

Leaflet — An open-source JavaScript library for interactive maps. Integrated via @asymmetrik/ngx-leaflet.

@asymmetrik/ngx-leaflet — Angular directive wrapper for Leaflet. Provides the leaflet attribute directive, [leafletOptions], [leafletLayers], and (leafletMapReady) bindings.

ng2-smart-table — An Angular data grid library providing <ng2-smart-table> with built-in sorting, filtering, pagination, and inline CRUD. Uses LocalDataSource for in-memory data and ServerDataSource for server-side data.

LocalDataSource — A class from ng2-smart-table that stores table data in memory and handles sorting, filtering, and pagination client-side.

TinyMCE — A WYSIWYG rich text editor loaded from CDN. Integrated via a custom Angular wrapper component (ngx-tiny-mce).

CKEditor 4 — A second WYSIWYG rich text editor loaded from CDN. End-of-life as of June 2023.

RxJS — The reactive programming library bundled with Angular. Provides Observable, Subject, forkJoin, takeWhile, takeUntil, delay, debounceTime, and other operators used throughout the application.

NbThemeService — Nebular's theme management service. Provides getJsTheme() (returns an Observable of the current theme's JavaScript variable map) and onMediaQueryChange() (returns an Observable that emits when the viewport crosses a breakpoint).

NbDialogService — Nebular's service for opening modal dialog overlays programmatically. Returns an NbDialogRef with an onClose observable.

NbDialogRef<T> — A reference object returned by NbDialogService.open(). Provides close(result?) to dismiss the dialog and onClose to receive the result.

NbWindowService — Nebular's service for opening floating, draggable window overlays.

NbToastrService — Nebular's service for displaying non-blocking toast notification overlays.

NbMediaBreakpointsService — Nebular service that provides the application's configured responsive breakpoint map (name → pixel width).

NbTreeGridDataSourceBuilder — Nebular service that constructs an NbTreeGridDataSource from a TreeNode[] array for use with nb-tree-grid.

LayoutService — An internal application utility service (src/app/@core/utils/layout.service.ts) that broadcasts layout size change events via an RxJS Subject. Chart components subscribe to onSafeChangeLayoutSize() (debounced 350ms) to trigger echartsInstance.resize().

Eva Icons — An open-source SVG icon pack used by Nebular. Icons are referenced by name (e.g., arrow-ios-downward, file-text-outline) via the nb-icon component.

Ionicons — A CSS icon font library used for social media icons in the Footer component and for the search icon in SearchInputComponent.

10.2 Patterns & Conventions

Shell Component — A component whose class body is empty and whose template contains only <router-outlet>. Exists solely to provide a URL segment and a rendering target for child routes.

Abstract Data Service Pattern — An Angular DI pattern where a domain's data contract is defined as an abstract class in src/app/@core/data/. A concrete mock implementation is registered in the module. Components inject the abstract class, never the concrete implementation.

alive flag pattern — A subscription lifecycle management pattern using a private alive: boolean = true property with takeWhile(() => this.alive) on subscriptions and this.alive = false in ngOnDestroy. Superseded by the destroy$ subject pattern in modern Angular.

destroy$ subject pattern — A subscription lifecycle management pattern using private destroy$ = new Subject<void>() with takeUntil(this.destroy$) on subscriptions and this.destroy$.next(); this.destroy$.complete() in ngOnDestroy. Preferred over the alive flag pattern.

forkJoin — An RxJS operator that subscribes to multiple observables simultaneously and emits a single array of their last values only after all source observables have completed. Used to load multiple datasets in parallel before rendering.

takeWhile — An RxJS operator that completes a subscription when a predicate function returns false. Used with () => this.alive for subscription cleanup.

takeUntil — An RxJS operator that completes a subscription when a notifier observable emits. Used with this.destroy$ for subscription cleanup.

ngx- prefix — The component selector namespace convention used for all custom application components in this codebase (e.g., ngx-dashboard, ngx-electricity-chart). Distinguishes application components from Nebular (nb-) components.

nb- prefix — The component selector namespace for Nebular UI library components (e.g., nb-card, nb-button, nb-icon).

Partial ECharts update — Calling echartsInstance.setOption({ series: [...] }) with only the changed portions of the configuration, rather than replacing the entire options object. More performant than full re-initialization.

delay(1) pattern — Applying delay(1) to the getJsTheme() subscription in ngAfterViewInit to defer chart initialization by one event loop tick, preventing ExpressionChangedAfterItHasBeenCheckedError.

getUrlPath(id) pattern — A method pattern used in SVG-heavy components to construct absolute url(...) references for SVG clipPath and filter elements, ensuring they resolve correctly when the app is deployed at a non-root base href.

confirmDelete: true — A ng2-smart-table settings flag that causes the table to emit a (deleteConfirm) event instead of immediately deleting a row, allowing the application to intercept and confirm the action.

10.3 Domain Terms

NbMediaBreakpoint — A Nebular object with name: string and width: number properties representing a single responsive breakpoint (e.g., { name: 'md', width: 768 }).

NbComponentStatus — A Nebular type union for semantic color roles: 'primary', 'success', 'info', 'warning', 'danger', 'basic', 'control'.

NbComponentSize — A Nebular type union for component size scale: 'tiny', 'small', 'medium', 'large', 'giant'.

NbComponentShape — A Nebular type union for border-radius style: 'rectangle', 'semi-round', 'round'.

NbGlobalPosition — A union type encompassing NbGlobalPhysicalPosition (e.g., TOP_RIGHT) and NbGlobalLogicalPosition (e.g., TOP_END) for positioning toast notifications.

TreeNode<T> — A generic interface for hierarchical tree data used by nb-tree-grid. Each node has a data: T payload and an optional children: TreeNode<T>[] array.

FSEntry — The domain model for a file system entry in the Tree Grid screen: { name, size, kind, items? }. The kind field distinguishes directories ('dir') from files.

PositionModel — A data class representing a geographic coordinate: { lat: number, lng: number }. Defaults to Minsk, Belarus (53.9, 27.5667).

Camera — A domain interface representing a security camera: { title: string, source: string }. The source field is a video stream URL.

TrafficList — An interface representing a single-period traffic summary: { date, value, delta: { up: boolean, value: number }, comparison: { prevDate, prevValue, nextDate, nextValue } }.

TrafficBar — An interface representing traffic as a time-series bar chart: { data: number[], labels: string[], formatter: string }.

ProfitChart — An interface for profit breakdown chart data: { chartLabel: string[], data: number[][] }. The data array contains three sub-arrays for Canceled, Payment, and All Orders series.

OrdersChart — An interface for orders time-series chart data: { chartLabel: string[], linesData: number[][] }.

OrderProfitChartSummary — An interface for a KPI summary item: { title: string, value: number }.

Electricity — A data model for an electricity usage category with monthly breakdown: { title, active?, months: Month[] }.

Month — A sub-model within Electricity for one month's data: { month, delta, down: boolean, kWatts, cost }.

ElectricityChart — A simplified label/value pair for chart rendering: { label: string, value: number }.

Temperature (interface) — A sensor reading model: { value: number, min: number, max: number }. Used for both temperature and humidity data.

Contacts — An interface for a contact list entry: { user: User, type: string }.

RecentUsers — An interface extending Contacts with a time field (numeric timestamp).

User — A minimal user profile: { name: string, picture: string }.

NgxLegendItemColor — An enum defining valid legend icon colors: GREEN, PURPLE, LIGHT_PURPLE, BLUE, YELLOW.

ProgressInfo — An interface for a progress bar metric: { title, value, activeProgress, description }.

UserActive — An interface for a user activity record: { date, pagesVisitCount, deltaUp: boolean, newVisits }.

OutlineData — A labeled data point for line charts: { label: string, value: number }.

VIEW_BOX_SIZE — The constant 300 used as the SVG coordinate system reference width in TemperatureDraggerComponent. All geometry is calculated in these units and scaled to actual pixel dimensions.

countryOrders theme variables — A named group within Nebular's JS theme variables providing color tokens for the Country Orders map and chart (e.g., countryFillColor, chartGradientFrom).

earningPie theme variables — A named group within Nebular's JS theme variables providing color tokens for the Earning Card pie chart (e.g., firstPieGradientLeft, center, radius).

visitorsPie theme variables — A named group within Nebular's JS theme variables providing color tokens for the Visitors Statistics pie chart.

solar theme variables — A named group within Nebular's JS theme variables providing color tokens for the Solar energy donut chart.

electricity theme variables — A named group within Nebular's JS theme variables providing color tokens for the Electricity line chart.

orders theme variables — A named group within Nebular's JS theme variables providing color tokens for the Orders area chart.

profit theme variables — A named group within Nebular's JS theme variables providing color tokens for the Profit bar chart.

trafficBarEchart theme variables — A named group within Nebular's JS theme variables providing color tokens for the Traffic Bar chart.