# Trackofy CAN Module — UI/UX Specification for Angular Rebuild

> **Audience:** Angular front-end team.
> **Goal:** Rebuild the CAN module UI/UX **pixel-perfect and behaviorally identical** to the current implementation, talking to the **same PHP API** (`API/CAN_api_module.php`).
> **Scope:** This document specifies design tokens, layout, every shared component, every screen, all interactions, states, validations, responsive rules, accessibility, and business rules. It does **not** require changing the backend — the API contract below is the integration boundary.

---

## 1. Module Overview & Purpose

The CAN module visualizes telematics/CAN-bus data (battery temps, voltages, pump/compressor/HVAC parameters, etc.) coming from fleet vehicles. It is a **read + light-configuration** module:

- **Monitor** the fleet (Dashboard), per-vehicle live values (Unit Insight), and device inventory (Units).
- **Analyze** historical data as charts (Trends Live) and tabular reports (Report).
- **React** to threshold breaches (Alerts) and configure alert rules (Settings).
- **Explain** data in plain language via deterministic **AI Summary** panels.

**Key principles to preserve:**
1. **API-first / thin client.** Every screen is a thin client over JSON endpoints. No business logic in the UI beyond presentation, selection state, and validation.
2. **Per-user data isolation.** The API resolves the logged-in user from the PHP session; the UI never sends a user id. The UI only renders what the API returns.
3. **Vehicle-centric language.** Always show **vehicle registration number (`veh_reg`)** and friendly parameter labels — never raw DB ids, metric codes, or array indexes (except where explicitly noted).
4. **Time is IST.** All displayed timestamps are India Standard Time, labeled **“Last Contact.”**

---

## 2. Integration / API Contract

### 2.1 Transport
- **Single endpoint**, HTTP `POST`, `Content-Type: multipart/form-data` (FormData).
- Every request includes a `method` field naming the operation, plus operation params.
- **Arrays are sent comma-joined** (e.g. `params = "code1,code2,code3"`).
- Send credentials/cookies (`withCredentials: true`) — auth is the PHP session cookie.
- Response is JSON: `{ status: 1|0, msg: string, ...payload }`.
  - `status === 1` (or `true`/`'1'`) = success. Anything else = failure; show `msg`.

**Reference wrapper (current vanilla JS — replicate as an Angular service):**
```
api(method, payload={}) -> POST FormData{ method, ...payload(arrays joined by ',') }
ok(res) -> res.status === 1 || res.status === true || res.status === '1'
```

### 2.2 Endpoint summary (method → key response fields)
| Method | Params | Returns |
|---|---|---|
| `get_can_protocols` | — | `data[]`: `{protocol_code, protocol_name, ...}` |
| `get_can_global_dashboard` | — | `summary{}`, `protocols[]`, `units[]`, `alerts[]` |
| `get_can_global_units` | `protocol_code?`, `status?`, `search?` | `data[]` device rows |
| `get_can_devices` | `protocol_code` | `data[]` devices that have data on that protocol |
| `get_can_params` | `protocol_code` | `data[]` parameter catalog (`is_chart/is_report/is_alert/is_core/is_kpi`, `code/label/unit/display_group/is_array/array_max_idx`) |
| `get_can_latest` | `protocol_code`, `sys_service_id` | `data[]` latest values, `veh_reg`, `total_params` |
| `get_can_report` | `protocol_code`, `sys_service_ids`, `from`, `to`, `params[]`, `page`, `page_size`, `sort_by`, `sort_dir` | `columns[]`, `data[]`, `total_rows`, `page`, `page_size`, `sort_by`, `sort_dir` |
| `get_can_report_ai_summary` | `protocol_code`, `sys_service_ids`, `from`, `to`, `params[]` | `summary{}` |
| `get_can_ai_summary` | — | `summary{}` (fleet) |
| `get_can_unit_ai_summary` | `protocol_code`, `sys_service_id` | `summary{}` (one vehicle) |
| `get_can_alerts` | `protocol_code?`, `alert_level?`, `sys_service_id?` | `data[]` alert rows |
| `recheck_can_alerts` | — | `open_alerts` |
| `get_can_alert_settings` | — | `data[]` configured rules |
| `save_can_alert_setting` | (see Settings form) | `status/msg` |
| `toggle_can_alert_setting` | `sys_service_id`, `protocol_metric_id`, `alert_type_id` | `is_active` |
| `delete_can_alert_setting` | `sys_service_id`, `protocol_metric_id`, `alert_type_id` | `status/msg` |
| `get_can_multi_unit_trend` | `protocol_code`, `sys_service_ids`, `metrics`(`code:idx,...`), `from`, `to` | `series[]`: `{sys_service_id, code, label, unit, data:[{gps_time, value_num, value_text}]}` |

> **Note:** In trend/latest responses the time field key is `gps_time`, but its **value is already IST** (the API maps `rowcreated`). Display it as **“Last Contact.”** Report rows also use key `gps_time` carrying the IST value; the report **column label is “Last Contact.”**

---

## 3. Global Design System (Design Tokens)

Reproduce these exactly (current values are CSS custom properties). Recommend an Angular theme file / SCSS variables.

### 3.1 Color palette
| Token | Value | Usage |
|---|---|---|
| `--brand` | `#0c84c2` | Primary brand blue (buttons, accents, active nav) |
| `--brand-2` | `#16a9cf` | Lighter brand (gradients) |
| `--brand-dark` | `#076aa0` | Darker brand (button gradient end, headings on light) |
| `--navy` | `#0f2440` | Headings / strong text |
| `--bg` | `#eef2f8` | Page background (plus a soft radial glow, see 3.5) |
| `--panel` | `#ffffff` | Card/surface background |
| `--panel-2` | `#f7faff` | Subtle inset surfaces (stat cards, table header) |
| `--text` | `#0f1b2d` | Body text |
| `--muted` | `#6b7a90` | Secondary text, labels |
| `--line` | `#e6ecf4` | Hairline borders |
| `--line-2` | `#dbe4f0` | Input/secondary borders |
| `--green` / `--green-bg` | `#16a34a` / `#e7f8ee` | Online / success |
| `--red` / `--red-bg` | `#e11d48` / `#fdeef0` | Offline-critical / danger |
| `--yellow` / `--yellow-bg` | `#d97706` / `#fdf3e3` | Warnings |
| `--blue` / `--blue-bg` | `#2563eb` / `#e8f0fe` | Info |

**Chart series palette (ordered):** `#0c84c2, #e11d48, #16a34a, #d97706, #7c3aed, #0891b2, #db2777, #65a30d, #2563eb, #ea580c, #0d9488, #9333ea`.

**AI accent gradient:** `linear-gradient(135deg,#7c3aed,#0c84c2)` (violet→blue). The Report page’s primary AI button is an animated **RGB** gradient (see 5.10).

### 3.2 Typography
- **Font family:** `Inter`, fallback `Segoe UI, Arial, sans-serif` (load Inter weights 400–900 from Google Fonts).
- **Base body:** `14px`, color `--text`, `-webkit-font-smoothing: antialiased`, normal letter-spacing.
- **Weights in use:** 400 (body), 500 (nav), 600 (labels, secondary), 700 (headings, values), 800 (page/section emphasis, AI). Avoid 900.
- **Heading sizes:**
  - Page title `h1`: `19px / 700`, color `--navy`, with a 4×15px gradient bar before it.
  - Section head `h2`: `15px / 700`, `--navy`, with a 4×14px gradient bar before it (`padding-left:12px`).
  - Card sub-paragraph: `13px`, `--muted`, line-height 1.55.
- **Uppercase micro-labels** (form labels, stat labels, table headers): `11px`, `600–800`, `letter-spacing .4–.6px`, `--muted`, `text-transform:uppercase`.

### 3.3 Spacing & sizing
- **Radii:** card `--radius:14px`; inputs/buttons `--radius-sm:10px`; pills/badges `999px`.
- **Card:** padding `20px`, margin-bottom `18px`, border `1px solid --line`, shadow `--shadow`.
- **Main content:** padding `26px 30px 52px`, `max-width:1560px`, centered.
- **Grid gap:** `16px`.
- **Inputs/selects:** height `43px`, padding `0 13px`, border `1px solid --line-2`, radius `10px`, font 14px.
- **Buttons:** padding `10px 16px`, 13px/600; `mini` variant `8px 12px`/12px.

### 3.4 Shadows
- `--shadow: 0 12px 32px rgba(15,40,80,.08)` (cards).
- `--shadow-sm: 0 4px 16px rgba(15,40,80,.06)` (stat/metric cards).

### 3.5 Background treatment
Body background = `--bg` plus a fixed radial glow top-right:
`radial-gradient(1100px 360px at 82% -140px, rgba(22,169,207,.10), transparent 60%)`.

---

## 4. Global Layout & Navigation

### 4.1 Shell structure (every page)
```
[ 6px gradient top strip ]
[ Sticky header: Logo · Nav · GPS button ]   height 78px
[ <main> max-width 1560px, padded ]
   [ Page title row ]
   [ Cards … ]
[ Toast (fixed, top-right) ]
```

### 4.2 Header (`.can-header`)
- Height **78px**, sticky top, `z-index:50`, white 96% + blur, bottom hairline, subtle shadow, padding `0 30px`.
- **Left:** Trackofy logo image (116×35), links to Dashboard.
- **Center/Right nav (`.can-nav`):** horizontal items, 13.5px/500, radius 10px, gap 6px:
  - `⌂ Dashboard` → dashboard
  - `▣ Units` → units
  - `≋ Trends Live` → trends (live)
  - `▮ Report` → report
  - `▲ Alerts` → alerts
  - `⚙ Settings` → alert settings
  - **Active item:** background `#e6f4fb`, color `--brand-dark`, plus a 3px gradient underline (`::after`, inset 13px, bottom -1px).
  - **Hover:** background `#eef6fb`, color `--brand`.
- **Right action (`.can-gps`):** “↩ GPS” pill button, gradient `135deg, --brand → --brand-dark`, white, radius 10px, padding `10px 20px`. Links back to the main GPS app (`../user/home_dashboard.php`). Hover lifts 1px.

### 4.3 Top strip
6px tall bar: `linear-gradient(90deg, --brand-2, --brand, --brand-dark)`.

### 4.4 Page title block (`.page-title`)
Flex row, space-between, margin-bottom 20px:
- Left: `h1` + optional `p` subtitle (muted).
- Right (optional): action buttons (e.g. AI Summary, Refresh, Back).
- On mobile (<560px) it stacks vertically.

### 4.5 Navigation flow
- Top nav is the only global navigation; all pages are siblings (no nested routes today).
- **Cross-page deep link:** Dashboard “Latest Units” and Units list link to **Unit Insight** via query params: `unit_insight?service_id=<sys_service_id>&protocol_code=<protocol_code>`.
- Recommended Angular routes:
  `/can/dashboard`, `/can/units`, `/can/unit-insight`, `/can/trends`, `/can/report`, `/can/alerts`, `/can/settings`.

---

## 5. Shared Components (build once, reuse)

### 5.1 Card (`.card`)
White surface, radius 14, border `--line`, shadow `--shadow`, padding 20, margin-bottom 18. Most content sits in cards.

### 5.2 Section head (`.section-head`)
Flex space-between, margin-bottom 14. Left = `h2` (15/700 navy, gradient bar). Right = actions/meta. Used as the header inside cards.

### 5.3 Stat card (`.stat`) — KPI tile
- Surface with a 4px left accent bar (gradient by value color).
- Structure: `.label` (uppercase 11/600 muted) → `.value` (24/700) → `.hint` (11.5 muted).
- **Color variants** by adding class to `.value`: `green`, `red`, `yellow`, `blue` (default = brand-dark). The left accent bar auto-matches via `:has()`.
- Hover: translateY(-2px) + larger shadow.
- Used in grids `grid-5` (Dashboard summary), `grid-4` (Unit Insight summary).

### 5.4 Buttons
| Variant | Look |
|---|---|
| `.btn` (primary) | gradient `135deg --brand→--brand-dark`, white, shadow; hover lift |
| `.btn.light` | white, `--brand-dark` text, `--line-2` border |
| `.btn.gray` | `#eef2f8` bg, `#3a475c` text, border |
| `.btn.mini` | smaller padding/12px (used in toolbars/pagers) |
| `.btn.danger` | `--red-bg` bg, `--red` text, red border (Delete) |
| `.btn.ai-btn` | violet→blue gradient (AI Summary on Dashboard/Unit Insight) |
| `.ai-btn-rgb` | **animated RGB** gradient + glow (Report AI button) — see 5.10 |

Disabled buttons: opacity .45–.5, `pointer-events:none`.

### 5.5 Badges (`.badge`)
Pill, 11.5/600, leading status dot (`::before`):
- `online` (green-bg/green-text) — “Online”
- `offline` (slate) — “Offline”
- `nodata` (muted slate) — “No Data”
- `warn` (yellow) — “Warning”
- `crit` (red) — “Critical”
- `info` (blue)

Helpers: `statusBadge(s)` maps `ONLINE→Online`, `NODATA→No Data`, else `Offline`. `alertBadge(level)` maps `CRITICAL→Critical (crit)`, else `Warning (warn)`.

### 5.6 Forms
- `.form-grid` = 4 equal columns, gap 14, `align-items:end`. `.form-grid.three` = 3 columns.
- Each field: `<label>` (uppercase micro) above an `input`/`select`.
- Inputs 43px, focus ring `0 0 0 3px rgba(12,132,194,.14)` + brand border.
- Selects use a custom chevron SVG, right-aligned.

### 5.7 Chips (selection)
Two chip styles:
- **Report chips (`.rep-chip`)**: pill with a square “tick” box; `active` = light-blue bg + filled brand tick. Used for multi-select Units and Parameters.
- **Trends chips (`.chip`)**: pill; `active` = light-blue bg; shows a small colored square (`.cdot`) when selected (vehicle color). Used for vehicles and metrics.
- Chip rows scroll vertically if tall (`.chip-row`, max-height ~230px on Report).

### 5.8 DataTable (client-side) — **the standard table component**
A reusable table used by Dashboard “Latest Units”, Units, Alerts, and Settings “Configured Rules”. **Build as one Angular component** `<can-data-table [columns] [rows] [options]>`.

**Column model:** `{ key, label, render?(row)->html, sortable?=true, noExport? }`.

**Features (all client-side):**
- **Toolbar above table** (`.dt-toolbar`, space-between):
  - Left (`.dt-tools-left`): **⬇ Excel** (`.btn.light.mini`) + **⬇ CSV** (`.btn.gray.mini`).
  - Right: **search input** (`.dt-search`, 38px, max 260–280px) “Search table…”.
- **Sortable headers:** clickable `th` (cursor pointer, `:hover` brand). Click toggles asc/desc; active column shows ` ▲`/` ▼`. Numeric-aware sort (numbers compared numerically, else `localeCompare`). Columns with `sortable:false` or `key:'action'` are not sortable.
- **Pagination (`.dt-pager`, below):** “Showing X–Y of N rows” + `Rows` size select **[10, 25, 50, 100]** + `« First ‹ Prev Page p/q Next › Last »`. Disabled buttons greyed. Default page size per page (Dashboard 10, others 25).
- **Search** filters across all keyed columns (current dataset), resets to page 1.
- **Export** (Excel `.xls` via HTML table blob; CSV via quoted blob) exports the **filtered** dataset, excluding `action`/`noExport` columns; filename from `options.exportName`.
- **Empty state:** if no rows → a single full-width `.empty` cell (“No data found” / “No rows match your search.”).

> Report uses a **server-side** variant (see 8.2) — same visual chrome but paging/sorting/search semantics differ.

### 5.9 Charts (hand-drawn SVG)
Two simple charts on Dashboard + the Trends time-series chart.
- **Bar chart** (`drawBars`): up to 10 bars, gradient fills, value above, label below, baseline.
- **Donut** (`drawDonut`): SVG ring segments + center total + legend with %.
- **Trends time-series** (see Screen 4) — line/area/bars, axes, crosshair tooltip.
- Chart container `.chart-box` height 290 (dashboard) / `.tn-chart` 400 (trends).
- Angular teams may swap in a chart lib (e.g. ngx-charts/ECharts) **as long as visual + interaction parity is met** (axis labels, IST x-axis, multi-series crosshair, per-series colors, normalize toggle).

### 5.10 AI Summary side panel (slide-in drawer)
Used on Dashboard (fleet) and Unit Insight (vehicle).
- **Overlay** (`.ai-overlay`): fixed, full-screen, `rgba(15,40,80,.35)` + blur, fades in.
- **Panel** (`.ai-panel`): fixed right, full height, width **430px** (max 93vw), slides in from right (`translateX` transition .3s), `z-index:201`, column flex.
- **Header** (`.ai-head`): pastel gradient bg, title (e.g. “✨ AI Fleet Summary”), close `×` button.
- **Body** sections (`.ai-body`, scroll): `.ai-headline` (16/800), `.ai-narr` (14 narrative), `.ai-stats` (2-col stat tiles), labeled sections (`.ai-sec` uppercase) with list items (`.ai-li` dot + text), “Needs Attention” callouts (`.ai-att`, level variants info/critical/yellow), footer (“Generated … ”).
- **Open/close:** open on button; close on overlay click, `×`, or `Esc`.

The **Report** AI uses an **inline** panel instead of a drawer (see Screen 5).

### 5.11 Toast (`.toast`)
Fixed top-right (`right:22, top:84`), success = green, error variant `.err` = red. Auto-hides after ~3.2s. `toast(msg, isError)`.

### 5.12 Loading / empty / loader
- **Spinner** (`.loader`): 16px ring, brand top border, spin .8s. Inline with text.
- **`loading(containerId, msg)`**: replaces a container’s content with `[spinner] msg…`.
- **Empty state** (`.empty`): dashed border, centered, muted, padding 26 — used for “no data”, invalid request, etc.

### 5.13 Metric card (`.metric-card`) — Unit Insight
Small card: `.metric-label` (12 muted) → `.metric-value` (20/700 navy, with small unit) → `.metric-meta` (11 muted, Last Contact time). `oor` modifier = red border/bg (out-of-range). Laid out in `.metric-grid` (auto-fill, min 210px).

---

## 6. Screen-by-Screen Specifications

### Screen 1 — Dashboard (`/can/dashboard`)

**Purpose:** Executive fleet overview. No selection required; loads on enter.

**Page title:** `h1 “CAN Dashboard”`, subtitle “Overall CAN fleet summary. No protocol/device/date selection required.” Right actions: **✨ AI Summary** (`.btn.ai-btn`) + **Refresh** (`.btn.light`).

**Layout (top→bottom):**
1. **Summary cards** — `grid-5` of `.stat`:
   | Card | Color | Value field | Hint |
   |---|---|---|---|
   | Total CAN Assets | blue | `summary.total_can_units` | All CAN devices you own |
   | Online / Reporting | green | `summary.reporting_units` | Reported in last 24h |
   | Offline / Stale | red | `summary.offline_units` | No recent CAN data |
   | Protocols | default | `summary.total_protocols` | Active protocols with data |
   | Active Alerts | yellow | `summary.total_alerts` | Open warning + critical |
2. **`grid-3` row:**
   - **Protocol Wise Assets** — bar chart (`protocols[]`: x=`protocol_code`, y=`total_units`).
   - **Online vs Offline** — donut (`reporting_units` green vs `offline_units` red).
   - **Recent Alerts** — list (`alerts[]`): each = `alertBadge(level)` + bold label + line “`veh_reg` • `protocol_code` • `rowcreated`” + message. Header has “View All” → Alerts.
3. **Latest CAN Units** — `<can-data-table>` (pageSize **10**), columns:
   `Vehicle (veh_reg)`, `IMEI`, `Protocol (protocol_name)`, `Last Contact (last_gps_time)`, `Params (Live / Total)`, `Status`, `Warnings (warning_count)`, `Critical (critical_count)`, `Action`.
   - **Params (Live / Total)** renders `metric_count / total_params` (total in muted). For `status_text==='NODATA'` → render “—”.
   - **Status** → `statusBadge(status_text)` (Online/Offline/No Data).
   - **Action** → `View` link to Unit Insight **only if `protocol_code` present**; otherwise muted text “No data yet”.

**Data source:** `get_can_global_dashboard` (one call returns summary, protocols, units, alerts). The fleet view is **global** (shows all owned CAN devices including No-Data ones).

**AI Summary panel (drawer):** opens via button; calls `get_can_ai_summary`; renders headline/narrative, stats (Total Vehicles, Reporting 24h, Offline/Stale, Open Alerts), Fleet Vitals, Highlights, Needs Attention.

---

### Screen 2 — Units (`/can/units`)

**Purpose:** Inventory of all CAN devices with status.

**Page title:** `h1 “CAN Units”`, subtitle “All CAN enabled services/devices with their protocol, last data time, and current status.”

**Filters card** (`.form-grid`, 4 cols):
- **Protocol** select (`All Protocols` + protocol list).
- **Status** select: `All / Online / Offline`.
- **Search** input “Vehicle / IMEI / protocol”.
- **Search** button (primary) → `loadUnits()`.

**Unit List card:** section-head “Unit List” + **Refresh** (light). `<can-data-table>` `unitTable` (pageSize **25**), columns:
`Vehicle`, `IMEI`, `Protocol`, `Type (protocol_type)`, `Last Contact`, `Params (Live / Total)`, `Values (value_count)`, `Status`, `Action (View Insight)`.
- Same NODATA handling as Dashboard (Params “—”, Action “No data yet”).

**Data source:** `get_can_global_units({protocol_code, status, search})`.
**Business rule:** **No protocol** → all owned devices (incl. No-Data). **Protocol selected** → only that protocol’s devices (data-driven). Status filter: `OFFLINE` includes `NODATA`.

---

### Screen 3 — Unit Insight (`/can/unit-insight?service_id=&protocol_code=`)

**Purpose:** Deep-dive on one vehicle’s current values.

**Entry:** via query params `service_id` + `protocol_code` (from Units/Dashboard “View”). If either is missing/empty → show `.empty` “Invalid request. Open this page from CAN Units list.” and render nothing else.

**Page title:** `h1 “Unit Insight”`, subtitle “Vehicle: **<veh_reg>** • Protocol: **<protocol_code>**” (veh_reg filled after load). Right: **✨ AI Summary** + **Back to Units** (light).

**Layout:**
1. **Summary** — `grid-4` `.stat`: `Vehicle (veh_reg, blue)`, `IMEI`, `Protocol (code)`, `Parameters Reporting` = `allRows.length / total_params` (green) with hint “Live values received / defined in protocol”.
2. **Core Snapshot** card — section-head + **Refresh**. `metric-grid` of core metrics (`is_core===1`, first 12; fallback first 12 of all).
3. **All Latest Parameters** card — header `h2 “All Latest Parameters <count note>”` where count note = “— X of Y parameters reporting”; plus a **parameter search** input (`.param-search`, max 340) “Search parameter, value or group…”. Body = parameters **grouped by `display_group`**, each group titled `Group (n)`, then a `metric-grid` of metric cards.

**Metric card content:** label, value (`value_text` if present else `value_num`) + unit, meta = `rowcreated` (Last Contact).

**Data source:** `get_can_latest({protocol_code, sys_service_id})` → `data[]`, `veh_reg`, `total_params`.

**Parameter search:** client-side filter over loaded rows by label/code/group/value/unit. Empty result → `.empty` “No parameters match your search.”

**AI Vehicle Summary panel (drawer):** `get_can_unit_ai_summary`; stats = Parameters, Open Alerts, Out of Range, Last Contact; sections Key Readings, Highlights, Needs Attention (out-of-range items flagged).

---

### Screen 4 — Trends Live (`/can/trends`)

**Purpose:** Compare metric trends over time across up to 5 vehicles of the **same protocol**.

**Page title:** `h1 “CAN Trends New” + green “LIVE” pill`, subtitle “Compare live metric trends across up to 5 vehicles of the same protocol, straight from the CAN history tables.”

**Cards:**
1. **Data & Chart Settings** (`.cfg-grid`, 4 cols, wraps to 2 on ≤920px):
   - **Protocol** select, **From** input, **To** input (datetime strings `YYYY-MM-DD HH:mm:ss`), **Chart Type** select `Line / Area / Bars`.
   - **Show markers** toggle (checkbox switch, default on).
   - **Normalize (mixed units)** toggle (default off).
   - **Apply** button (full-width).
2. **Vehicles / Services** — section-head + count “N / 5 selected”. `chip-row` of vehicle chips (`.chip`), **max 5 selectable**; selected chip shows its assigned series color dot. Clicking toggles; exceeding 5 → toast “Maximum 5 vehicles”.
3. **Metric Trend (multi-vehicle)** — h2 + subtitle “One metric plotted for every selected vehicle. Each vehicle gets its own colour.” Right: **Array Index** select (for array metrics). A **search box** “🔍 Search metric by name or unit…” filters chips. `trendChips` = **single-select** chartable metrics. Legend row. **Chart** (`tn-chart`).
4. **Multi Parameter Comparison (multi-vehicle)** — h2 + subtitle “Every selected metric × every selected vehicle becomes its own coloured series. Mixed units auto-normalize.” Right buttons: **Smart Default**, **Clear**, **Draw Comparison**. A **search box** “🔍 Search metrics by name or unit…” + live **“N selected”** counter. `compareChips` = **multi-select**. Legend. **Chart**.

**Chart spec (critical for parity):**
- SVG, width responsive (min 820), height 376; padding L60 R18 T28 B44.
- **X-axis:** time, 6 tick labels formatted IST; `HH:mm` when span ≤36h else `MM-DD HH:mm`. 4–5 vertical gridlines.
- **Y-axis:** 5 gridlines with labels. **Default = actual values** (`n2`, 2-dp). **Normalized mode** shows `100% / 75% / 50% / 25% / 0%` and a caption “Normalized scale (0–100%) — hover to read actual values”. If mixed units and NOT normalized, caption “Actual values · mixed units (…) — tick ‘Normalize’ to compare on a 0–100% scale”.
- **Series:** line (polyline 2.5px), area (filled 12% opacity), bars (3px). Markers optional (3.5px dots). One color per series from palette.
- **Crosshair interaction:** a transparent capture rect over the plot drives a **vertical dashed guide line** + a highlight dot per series at the hovered timestamp + a **single tooltip** (`#canTip`, dark) listing the timestamp (to the second) and **every series’ actual value** with colored swatch. Nearest point per series via binary search. Tooltip flips to stay on-screen near edges. On mouse leave, hide line/dots/tooltip.
- **Normalization rule:** comparison chart shows actual values by default; normalize **only** when the user ticks the toggle. (Trend chart is single-metric, same unit → always actual values.)

**Data source:** `get_can_devices(protocol)` for vehicle chips (protocol-scoped); `get_can_params(protocol)` for metric chips (filter `is_chart===1`); `get_can_multi_unit_trend({protocol_code, sys_service_ids, metrics:"code:idx,...", from, to})` returns `series[]`.

**Defaults on protocol change:** auto-select up to 5 vehicles; trend metric = first chartable; comparison metrics = first 3 non-array chartable; date range auto-set to the latest data day.

**Edge:** selecting only No-Data vehicles → API returns `status:1` empty `series` → chart shows “No data found …” (not an error).

---

### Screen 5 — Report (`/can/report`)

**Purpose:** Tabular, exportable, paginated report for any vehicles/parameters/date range.

**Page title:** `h1 “CAN Report”`, subtitle about picking parameters.

**Cards:**
1. **Setup** (`.form-grid.three`): **Protocol** select (includes **“All Protocols”** value=""), **From**, **To** (default today 00:00:00 → 23:59:59).
2. **Units / Devices** — section-head + count “N / M selected”. `rep-toolbar`: search “Search vehicle / IMEI…”, **Select All**, **Clear**. `chip-row` of unit chips (`.rep-chip` w/ tick). Default: **all selected**.
3. **Report Parameters** — count “N / M selected” (or “ALL” in All-Protocols mode). `rep-toolbar`: search, **Select All**, **Recommended** (core/kpi), **Clear**. `chip-row` of param chips. Below: **Generate Report** button (only this button here).
   - **All Protocols mode:** individual params are not listed; show message “All reportable parameters across all protocols are included. Pick a specific protocol to choose individual parameters.” and count “ALL”.
4. **AI Insights (inline panel, hidden by default)** — `.ai-ins` card with violet→blue gradient header “✨ AI Report Insights” + close `×`. Body = narrative tiles (Data Points, Vehicles, Parameters, Time Span), per-metric cards (avg, min↔max bar with dot, trend ▲/▼/— Steady, point count), and insight chips. Appears above the Result card when opened.
5. **Report Result** — section-head “Report Result” + `resultMeta`. A **tools row (`#reportTools`, hidden until a report is generated)**: left group = **✨ AI** (RGB glowing button) + **⬇ Excel** + **⬇ CSV**; right = **“Search this page…”** input. Then the **table** + **server-side pager**.

**Report table behavior (server-side):**
- Columns come from API `columns[]` (`{key, label}`); first two are `gps_time`→**“Last Contact”** and `veh_reg`→**“Vehicle”**, then one per selected metric (label includes unit; array idx appended as `#n`).
- **Sortable headers:** clicking sends `sort_by`+`sort_dir` to the API and reloads page 1; the API sorts the **entire dataset** (NULLs last). Active column shows ▲/▼. Default sort `gps_time desc`.
- **Pagination:** server-side `OFFSET/FETCH`. Page sizes **[20, 50, 100, 200]**, default **20**. Pager: “Showing X–Y of N” + size select + First/Prev/Next/Last.
- **Search this page:** client-side filter of the **current page only** (placeholder makes that explicit).
- **Export Excel/CSV:** fetches the **full dataset** (`page_size: 50000`) then builds the file.
- **AI button** appears only after a successful generate; opens the inline AI panel analyzing the **current report selection**; hidden + closed on protocol change.

**Data source:** units via `get_can_devices(protocol)` (specific) or `get_can_global_units()` (All Protocols); params via `get_can_params(protocol)`; report via `get_can_report(...)`; AI via `get_can_report_ai_summary(...)`.

**Empty/edge:** owned device with no data in range → `status:1`, empty `columns`/`data`, friendly msg; table shows “No data found for the selected date range.” Generate validations: require ≥1 unit; in specific-protocol mode require ≥1 parameter (toast otherwise).

---

### Screen 6 — Alerts (`/can/alerts`)

**Purpose:** Log of warning/critical threshold breaches.

**Page title:** `h1 “CAN Alerts”`, subtitle “CAN warning and critical alert log.”

**Filters card** (`.form-grid`): **Protocol** select (`All` + list), **Level** select (`All / Warning / Critical`), **Vehicle** select (**protocol-scoped** — repopulates when protocol changes), **Search** button.

**Alert Log card:** section-head + buttons **Re-check Alerts** (primary) + **Refresh** (light). `<can-data-table>` `alertTable` (pageSize **25**), columns:
`Level (alertBadge)`, `Vehicle`, `IMEI`, `Protocol (protocol_name)`, `Metric (label)`, `Actual (actual_value)`, `Limit (limit_value)`, `Message`, `Last Contact (rowcreated)`.

**Interactions:**
- Changing **Protocol** → reloads the Vehicle dropdown (scoped) → reloads alerts.
- **Re-check Alerts** → `recheck_can_alerts` → toast “Alerts re-checked — N open” → reload.
- **Search** / **Refresh** → `get_can_alerts({protocol_code, alert_level, sys_service_id})`.

---

### Screen 7 — Settings / Alert Configuration (`/can/settings`)

**Purpose:** Define thresholds per vehicle+metric and choose notification channels; manage existing rules.

**Page title:** `h1 “CAN Alert Setting”`, subtitle about configuring limits and recipients.

**Cards:**
1. **Alert Configuration** (`.form-grid`, 6 fields wrap): **Protocol** select, **Vehicle** select, **Metric** select (only `is_alert===1` metrics), **Mode** select `High / Low`, **Warning Limit** input (e.g. 80), **Critical Limit** input (e.g. 100).
2. **Notifications** card:
   - **Notify via** row (`.notify-bar`): checkboxes **Application** (default checked), **Email**, **SMS** (`.check` style, 18px brand accent).
   - **Email block** (`#emailBlock`, hidden unless Email checked): label “Email Recipients”, hint “Up to 3 email addresses”, **+ Email** button; dynamic **email rows** (`.email-row`) each = email input + red trash icon button (`.icon-del`). Always keep ≥1 row while enabled; **max 3** (disable + toast at limit).
   - **SMS block** (`#smsBlock`, hidden unless SMS checked): single **SMS Number** input (max 320px) + hint “Only one mobile number…”.
3. **Action row:** **Save Alert Setting** (primary) + **Reset** (gray).
4. **Configured Alert Rules** card: section-head + **Refresh**; hint line; `<can-data-table>` `rulesTable` (pageSize **25**), columns:
   `Vehicle`, `Metric (label + unit)`, `Mode`, `Warning (or —)`, `Critical (or —)`, `Notify` (channel chips App/Email/SMS, `sortable:false noExport:true`), `Status` (Active=green badge / Disabled=offline badge), `Actions` (**Enable/Disable** toggle + **Delete** danger).

**Save flow & validation (client-side before POST):**
- At least one channel must be selected → else toast “Select at least one notification channel”.
- If Email on: ≥1 email, ≤3, each must match email regex → else toast specific error.
- If SMS on: number required → else toast.
- POST `save_can_alert_setting({protocol_code, sys_service_id, metric_code, mode, alert_code:'GENERIC_CAN_ALERT', alert_name:'Generic CAN Alert', warning_limit, critical_limit, is_notification, is_email, email_id:CSV, is_mobile, mobile_no})`.
- On success: toast “Alert setting saved”, reload rules, fire `recheck_can_alerts` (fire-and-forget).

**Rule management:**
- **Toggle:** `toggle_can_alert_setting({sys_service_id, protocol_metric_id, alert_type_id})` (composite key) → reload.
- **Delete:** native `confirm('Delete this alert rule?')` → `delete_can_alert_setting(...)` → toast → reload.

**Reset:** clears limits, resets channels to App only, hides Email/SMS blocks, clears email rows + SMS number.

---

## 7. State Management Requirements

Recommended per-screen state (Angular: component state or a feature store/NgRx if preferred):

- **Global/shared:** none persisted; the API resolves user from session. Cache `get_can_protocols` per app load.
- **Dashboard:** `summary, protocols, units, alerts`; AI panel open + AI summary payload.
- **Units:** filter state `{protocolFilter, statusFilter, searchText}`; `rows`.
- **Unit Insight:** route params `{service_id, protocol_code}`; `allRows`, `total_params`, `veh_reg`; param search term; AI panel state.
- **Trends:** `protocol, from, to, chartType, markers, normalize`; `units[]` + `selectedUnits[]` (max 5) + per-unit color map; `trendMetric` + `trendIdx`; `compareMetrics[]`; two metric search terms; chart render metadata (for crosshair).
- **Report:** `protocol, from, to`; `reportUnits[]/selectedUnits[]`; `reportParams[]/selectedCodes[]`; `reportCols, reportRows, totalRows, currentPage, pageSize, sortBy, sortDir, lastReq`; `tableFilter`; AI inline open + payload.
- **Alerts:** filters `{protocol, level, vehicle}`; vehicle options (protocol-scoped); rows.
- **Settings:** form model (protocol/vehicle/metric/mode/limits/channels/emails[]/mobile); rules list.

**Selection-state rule (important):** chip/multi-select state lives in the component model, **not** in the DOM. Searching/filtering chips must not lose selections (hidden selected items remain selected). Counters reflect the true selected total.

---

## 8. Tables, Grids, Filters, Sorting, Pagination

### 8.1 Client-side tables (Dashboard, Units, Alerts, Settings)
- Search across keyed columns, numeric-aware sort, page sizes [10/25/50/100], Excel/CSV export of filtered set. See 5.8.

### 8.2 Server-side table (Report only)
- Paging via API `page`/`page_size`; sorting via `sort_by`/`sort_dir` (whole-dataset); page-only client search; export fetches full set. See Screen 5.
- **Why two modes:** report data can be huge; client tables are for bounded lists.

### 8.3 Filters
- Selects + search inputs; “Search” button triggers reload on filter cards (Units, Alerts). Trends/Report react to chip selection + explicit Apply/Generate/Draw.

---

## 9. Modals / Dialogs / Panels

- **AI side drawer** (Dashboard, Unit Insight): right slide-in, overlay, closes on overlay/×/Esc. See 5.10.
- **AI inline panel** (Report): expands in-page above results; close `×`.
- **Native confirm** for destructive delete (Settings rule delete). Angular teams may replace with a styled confirm dialog but must keep the confirm step.
- **No other modals.** Toasts handle transient feedback.

---

## 10. Responsive Behavior

Breakpoints (current CSS):
- **≤1180px:** `grid-5` → 3 columns.
- **≤1024px:** header stacks vertically (logo/nav/actions centered, height auto).
- **≤920px:** `grid-5/grid-4` → 2 cols; `grid-3` → 1; `form-grid` (and `.three`, `.cfg-grid`) → 2 cols.
- **≤560px (mobile):** main padding `16px 14px 40px`; all grids/forms → 1 column; nav items slightly smaller; page-title stacks.
- **Tables:** always wrapped in `.table-wrap` (`overflow:auto`), `min-width:900px` → horizontal scroll on small screens. Toolbars/pagers wrap.
- **Chart:** width responsive to container (min 820 internal viewBox); on narrow screens it scrolls/scales within the card.
- **AI panel:** `width:430px; max-width:93vw` so it fits mobile.

Targets: **Desktop** (full multi-column), **Tablet** (2-col grids, stacked header), **Mobile** (single column, scrollable tables/charts).

---

## 11. Loading, Empty, Success, Error States

| State | Pattern |
|---|---|
| **Loading** | Replace target with `[spinner] <message>…` (e.g. “Loading units…”, “Generating report…”, “Analysing your fleet…”). Charts/tables show inline loader. |
| **Empty (no data)** | `.empty` block with context message (“No data found for the selected date range.”, “No alerts”, “No parameters match your search.”, “No CAN devices on this protocol for your account.”). |
| **No-data device** | Status badge **“No Data”**, Params **“—”**, action **“No data yet”** (no broken link). |
| **Success** | Green **toast** (“Alert setting saved”, “Alerts re-checked — N open”, “Alert rule deleted”). |
| **Error** | Red **toast** with `res.msg` (or thrown error). Validation errors are toasts. Session-expired → API returns status 0 with a session message; surface it. |
| **Invalid route params** (Unit Insight) | `.empty` “Invalid request. Open this page from CAN Units list.” |

---

## 12. Icons, Colors, Typography, Spacing — Quick Reference

- **Icons:** currently Unicode glyphs in nav (`⌂ ▣ ≋ ▮ ▲ ⚙`), `✨` for AI, `⬇` for export, `🔍` in some search placeholders, `↩` GPS, `▲▼ —` sort/trend, `×` close, `« ‹ › »` pager, trash **SVG** for email delete. Angular team may substitute a consistent icon set (e.g. Lucide/Material) **matching meaning**; keep AI = sparkle, delete = trash, etc.
- **Colors/Typography/Spacing:** see Section 3 (tokens). Reuse tokens everywhere — do not hardcode hex.
- **Status color semantics:** green=online/active/success, slate=offline, muted=no-data, yellow/amber=warning, red=critical/danger, blue=info/primary, violet=AI.

---

## 13. Accessibility Considerations

Maintain/improve on current behavior:
- **Keyboard:** AI drawer closes on `Esc`; all actionable elements are real `button`/`a`/`select`/`input` (focusable). Ensure chips are keyboard-operable (role `button`, Enter/Space) in Angular.
- **Labels:** every form control has a visible `<label>`; add `aria-label` to icon-only buttons (close `×`, trash, AI icon button — current code uses `aria-label`/`title`).
- **Live regions:** announce toast messages (`aria-live="polite"`), and table “Showing X–Y of N”.
- **Focus management:** when the AI drawer opens, move focus into it; restore focus to the trigger on close. Trap focus within the open drawer.
- **Color contrast:** muted text on white meets AA for body sizes; keep status conveyed by **text + color** (badges include words, not color alone).
- **Charts:** provide the tooltip values as the accessible data path; consider an off-screen data table or `aria` summary for screen readers (current SVG is mouse-only — an Angular improvement).
- **Sortable headers:** add `aria-sort` and make them buttons.
- **`aria-hidden`** toggling on the drawer panel (current code sets `aria-hidden` true/false).

---

## 14. Business Rules Affecting the UI

1. **Device universe = ownership.** A user’s CAN devices = `tbl_services` rows with `is_bms = 1`. Global views (Dashboard, Units default, Report “All Protocols”) show **all** of them, including ones not sending data (**No Data**). The UI must render No-Data devices, never hide them.
2. **Protocol-scoped lists.** When a protocol is selected (Report, Trends, Units filter, Alerts vehicle dropdown), the device/vehicle list shows **only devices that have data on that protocol** (a device’s protocol is defined by its data). Example: selecting EEka shows just its 1 device.
3. **Params (Live / Total).** `metric_count` = parameters this vehicle is actually reporting; `total_params` = reportable parameters defined for the protocol. Display as “live / total”. Matches the Report’s parameter count.
4. **Last Contact = IST.** All times shown are IST (API maps `rowcreated`). Label “Last Contact”, never “GPS Time”.
5. **Online/Offline:** reporting within last 24h = Online; older = Offline; never = No Data.
6. **Trends:** max **5 vehicles**, **same protocol**; comparison auto-uses palette colors; normalize is opt-in.
7. **Report:** “All Protocols” includes all reportable params (no per-param selection). Specific protocol requires ≥1 parameter.
8. **Alerts config:** at least one notification channel; Email ≤3 valid addresses; SMS one number. Rules keyed by composite `(sys_service_id, protocol_metric_id, alert_type_id)` — toggle/delete use this triple (the surrogate id may be 0 in data; **do not** rely on it).
9. **Owned-but-no-data is not an error.** Report/Trends for an owned device with no data return `status:1` + empty payload + friendly message — the UI shows an empty state, not an error toast. Only genuinely un-owned ids are access-denied.
10. **Privacy:** never render internal ids, metric codes, or array indexes in user-facing text (except the array index selector on Trends, which is a functional control).

---

## 15. Edge Cases & Special Scenarios

- **Vehicle stored under device id vs service id:** the API canonicalizes; the UI always uses the `sys_service_id` the list endpoint returns and passes it straight back. Don’t transform ids client-side.
- **Mixed-unit comparison (Trends):** default shows actual values (axis = numbers) with a “mixed units” caption; only the Normalize toggle switches to 0–100%.
- **Duplicate metric code across protocols (All-Protocols report):** columns may collide on shared codes; acceptable today. (Single-protocol reports are exact.)
- **Empty date range / no history table:** report returns columns (so headers still show) with empty data and “No history found…”.
- **Large reports:** never load all rows client-side; rely on server paging; export path fetches up to 50,000 rows.
- **Protocol change resets:** Report resets sort to `gps_time desc`, hides tools, closes AI, reloads units+params (in parallel). Trends re-derives default vehicle/metric selections and date range. Alerts reloads vehicle dropdown.
- **Session expiry:** API returns status 0 with a session message + `session_keys`; show the message and (ideally) redirect to login.
- **No protocols / no devices:** show empty states; selects show “No units”/“No protocols”.
- **Search with no matches:** chip lists and tables show a contextual “No … match your search.” message; selections persist behind the filter.
- **AI panels are deterministic:** the `summary{}` payload is structured (headline/narrative/stats/highlights/attention/vitals/metrics/insights). Render generically so a future LLM-backed payload needs no UI change.

---

## 16. Appendix — Response Shapes (for binding)

**Device row (`get_can_global_units` / dashboard `units[]`):**
```
{ sys_service_id, veh_reg, imei, protocol_id, protocol_code, protocol_name, protocol_type,
  metric_count, total_params, value_count, last_gps_time, status_text('ONLINE'|'OFFLINE'|'NODATA'),
  warning_count, critical_count }
```
**Report response:**
```
{ status, msg, columns:[{key,label}], data:[{ gps_time, veh_reg, <metricCode>:value, ... }],
  total_rows, page, page_size, sort_by, sort_dir }
```
**Trend series:**
```
series:[{ sys_service_id, code, label, unit, data:[{ gps_time(IST), value_num, value_text }] }]
```
**Alert row:**
```
{ alert_level('WARNING'|'CRITICAL'), veh_reg, imei, protocol_name, code, label, unit,
  actual_value, limit_value, message, rowcreated, gps_time, is_resolved }
```
**Alert rule row:**
```
{ veh_reg, metric_label, code, unit, mode('HIGH'|'LOW'), warning_limit, critical_limit,
  is_notification, is_email, is_mobile, is_active, sys_service_id, protocol_metric_id, alert_type_id }
```
**Parameter catalog row:**
```
{ code, label, unit, display_group, display_order, is_core, is_kpi, is_chart, is_report, is_alert,
  is_array, array_max_idx }
```
**AI summary (generic):**
```
{ headline, narrative, stats{...}, vitals[], readings[], highlights[], attention[{level,text}],
  metrics[{label,unit,avg,min,max,count,trend('up'|'down'|'flat')}], insights[], generated_at }
```

---

### Build checklist for parity
- [ ] Design tokens (colors/typography/spacing/shadows/radii) implemented as theme variables.
- [ ] Shell (top strip, sticky header, nav with active underline, GPS button, main, toast).
- [ ] Reusable: Card, SectionHead, Stat, Button variants, Badge (+No Data), Form fields, Chips (rep + trends), **DataTable** (client), **ServerTable** (report), AI Drawer, AI Inline panel, Charts (bars/donut/time-series+crosshair), Loader/Empty/Toast, Metric card.
- [ ] 7 screens with exact layouts, API calls, and states above.
- [ ] Selection state in model (not DOM); counters; protocol-scoping rules.
- [ ] IST “Last Contact” everywhere; Params (Live/Total); No-Data handling.
- [ ] Responsive breakpoints (1180/1024/920/560).
- [ ] Accessibility (focus trap, aria-sort, aria-live, labels, Esc-close).

*End of specification.*
