openvidu/openvidu-dashboard/dashboard-ce/frontend/AGENTS.md

4.8 KiB

Frontend (@openvidu-dashboard/frontend and @openvidu-dashboard/ui)

Angular 22 standalone, zoneless and signals: OnPush is the default (do not set it), inject() instead of constructor injection, @Service() for root services, Signal Forms for forms. All UI lives in the library projects/dashboard-ui (src/lib/: core/ services by concern (auth/, http/, state/, refresh/, preferences/, ui/), shell/, components/ building blocks, charts/, format/, routing/, features/<area>/ pages with their data services and helpers, routes.ts); its README describes composition and the extension points. Behaviour is specified in openspec/specs/ui-* and the *-ui specs.

A component with a template or style file has a folder of its own, named after it; small components that belong together (badges, feedback, locks, search, tiles, zone) share a folder and its spec.

Routes and pages

  • Routes are DashboardRoute configs in src/lib/routes.ts (DASHBOARD_ROUTES) whose data carries navigation, page (liveCapable, rangeScoped, timed, feature), breadcrumb and entity; applications pass them to composeRoutes(), where a later config replaces a route by path. Pages are lazy and never exported from public-api.ts, or the library build folds them into the initial chunk.
  • The library holds what CE renders, locks included. What only an extending edition renders lives in its own application: its pages replace the locked CE routes and its pieces of CE pages fill the extension points of the library README. features/analytics/ holds the Analytics page with the locks of its PRO strip.

State and data

  • The URL is the single source of a page's state through QueryStateService (drops cursor and page on any other change; keepCursor for panel parameters). Relative times are resolved to ISO in tz before calling the API; the zone is one per session (SessionZoneService), written into the URL of timed pages by ListStateService.
  • HTTP only through HttpService (paths relative to api/v1/, errors as ApiRequestError); browser storage only through StorageService.
  • Pages load through injectListLoader() or injectRefreshScheduler() (manual for on-demand pages, paused with a reason); injectLiveCounters() polls the overview counters outside the header control. The shell polls GET /api/v1/status through injectServerStatusPoll() into ServerStatusService, the one source of the status banners, the live chip and the Data pipeline card; InfoService holds edition, features, retention and limits.
  • Freshness is judged at the last response (generatedAt) with the typings freshnessWindows(), never extrapolated by the ticker; running durations render with ovd-duration, never Date.now() in templates.

Building blocks

  • Cards, sections, charts and empty states take a heading input, never title (Angular would render it as a native tooltip over the whole host).
  • A term a newcomer would not understand gets an info tip through the info input of tiles, cards, charts, TableColumn and EntityFact, with its text in INFO_TEXTS (format/info-texts.ts); labels carry no hover text of their own.
  • Charts: bucket and zone in the note (seriesNote()), options merged into the kept instance (bindChartOptions()), colours from chartSlots(), no zoom on wheel.
  • Identifiers appear once, with ovd-copy-button beside the visible value; navigation uses links, never row clicks.
  • Lists use ovd-list-search inside .ovd-filter-bar, ovd-room-chip for the room filter, ranked sorts from rankedSortReasons() and ovd-data-table.
  • Messages use ovd-notice (ovd-error-banner for errors with Retry) and enum labels enumLabel() (format/labels.ts).
  • Icons are Material Symbols Rounded listed in projects/dashboard-ui/icons.json and compiled by scripts/icons.mjs build, which also takes the manifests of an extending application; lint fails on an icon used but unlisted, listed but unused, or unknown. Theme tokens are light-dark() pairs in projects/dashboard-ui/styles/_tokens.scss.

Builds and tests

  • Gzip budgets checked after ng build (scripts/check-bundle-size.mjs): 400 kB initial, 150 kB per lazy chunk, 250 kB for the ECharts chunk.
  • Unit tests: pnpm exec ng test dashboard-ui --watch=false (Vitest, jsdom; --include=<spec> for one file). vitest-base.config.mts adds the source condition; projects/dashboard-ui/src/testing/ holds setup, shared providers and fixtures.
  • vitest-base.config.mts turns off Vitest's file system module cache, which would serve modules compiled from older sources to the Angular builder. When the demo goes blank after a typings change ("does not provide an export named"), delete .angular/cache/*/frontend/vite and restart it.