# Phase 5: Frontend — Activity tab + smart filtering + live updates **Status:** ⬜ Not Started **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** frontend · uses the `frontend-design` skill ## Objective Build the dedicated top-level **Activity** tab: a read-only, smart-filterable, keyset-paginated log viewer with an entry detail view, live-append of new events, and export. This is a viewer (Dashboard-style), NOT a CRUD card section. ## Tasks - [ ] `core/ui.ts`: add `formatTimestamp(isoOrMs)` (Today/Yesterday/Date · HH:MM, i18n-aware) and `formatRelativeTime(isoOrMs)` ("2m ago"), with `tabular-nums` styling guidance for the list. Reuse existing `time.*` i18n key conventions. - [ ] `features/activity-log.ts`: - `export async function loadActivityLog()` — fetch first page from `GET /activity-log` (via `fetchWithAuth`), render the toolbar + list into the panel. - **Smart filter toolbar:** category (multi, IconSelect/chips), severity (chips), actor (EntitySelect/text), entity type, date range, free-text search (debounced). Quick presets: Today / Errors / Auth / Devices. Filters drive server-side query params (no client-side filtering of a partial page). Re-query on change; reset cursor. - **List:** one row per entry — severity icon, category badge, relative time (title=absolute), actor, message, entity crosslink (use `navigateToCard(...)` when the referenced entity is resolvable). Keyset "load more" (or infinite scroll) using `next_before_seq`. - **Detail:** expandable row / drawer showing full metadata JSON, exact timestamp, ids. - **Live append:** `document.addEventListener('server:activity_logged', e => …)` — prepend the new entry if it passes the active filters; show a subtle "new" affordance. (Depends on the Phase 2 allowlist entry — already shipped.) - **Export button:** triggers `GET /activity-log/export?format=…` with current filters via an authed blob download (use `fetchWithAuth` → blob URL, per frontend.md auth rules). - Empty / loading / error states; re-render on `languageChanged`. - [ ] Tab wiring: - `core/tab-registry.ts`: add `activity_log: { loadFnName: 'loadActivityLog', autoRefresh: false }`. - `templates/index.html`: sidebar tab button (`data-tab`, `switchTab('activity_log')`, history/clock SVG icon, `data-i18n`) + `
`. - `app.ts`: import + `Object.assign(window, { loadActivityLog, … })`; `global.d.ts` decls. - [ ] Icons: add a history/audit icon to `core/icon-paths.ts` + `core/icons.ts`; severity icons (info/warning/error) reuse existing constants where possible. - [ ] i18n: add `activity_log.*` keys to `static/locales/{en,ru,zh}.json` (title, filter labels, category/severity names, column labels, presets, empty/error, export, "N entries"). - [ ] Tutorials: add an Activity-tab step to the getting-started tour in `features/tutorials.ts` + `tour.*` keys in all 3 locales. ## Files to Modify/Create - `server/src/ledgrab/static/js/core/ui.ts` — modify: timestamp/relative-time formatters - `server/src/ledgrab/static/js/features/activity-log.ts` — new: the viewer - `server/src/ledgrab/static/js/core/tab-registry.ts` — modify: register tab - `server/src/ledgrab/templates/index.html` — modify: tab button + panel - `server/src/ledgrab/static/js/app.ts` — modify: import + window globals - `server/src/ledgrab/static/js/global.d.ts` — modify: window decls - `server/src/ledgrab/static/js/core/icon-paths.ts` / `core/icons.ts` — modify: icons - `server/src/ledgrab/static/locales/{en,ru,zh}.json` — modify: i18n keys - `server/src/ledgrab/static/js/features/tutorials.ts` — modify: tour step - `server/src/ledgrab/static/css/*` — modify/new: list + toolbar styling (follow base.css vars) ## Acceptance Criteria - New **Activity** tab loads, lists entries, and paginates via keyset "load more". - Filters hit server-side query params; quick presets work; free-text is debounced. - New events append live via `server:activity_logged` and respect active filters. - Export downloads CSV/JSON with auth, honoring current filters. - Fully localized (en/ru/zh); empty/loading/error states; re-renders on language change. - No plain `