Skip to content

Dashboard and Widgets

Melvin PETIT edited this page Jun 24, 2026 · 2 revisions

Dashboard and Widgets

The dashboard is a drag-and-drop grid of widgets backed by a registry. Layout and visibility are persisted per user, and presets let users save and switch between arrangements.

Widget registry

Every widget is declared in src/lib/widgetRegistry.ts as a WidgetDef:

type WidgetDef = {
  type: string
  defaultTitle: string
  description: string
  defaultSize: { w: number; h: number }
  minSize: { w: number; h: number }
  category: "overview" | "alerts" | "breaches" | "employees" | "trends"
  defaultVisible?: boolean   // false widgets ship in the library, hidden by default
}

Available widgets

Type Title Category Default
stats-row Key Metrics overview visible
trend-chart Incident Timeline trends visible
data-type-breakdown Exposed Data Types breaches visible
breach-sources Breach Sources breaches visible
severity-donut Alert Severity alerts visible
department-risk Department Exposure employees visible
alerts-feed Recent Alerts alerts visible
top-risky-employees Top Employees at Risk employees visible
risk-gauge Risk Score overview hidden
alert-status-breakdown Alert Status alerts hidden
employee-exposure Employee Exposure employees hidden
alerts-by-month Alerts by Month trends hidden
alert-velocity Alert Velocity trends hidden
critical-alerts Urgent Alerts alerts hidden
alerts-by-department Alerts by Department alerts hidden
breach-source-donut Breach Origin breaches hidden
breach-timeline Breach Timeline breaches hidden
top-breaches Top Breaches by Impact breaches hidden
data-type-radar Data Type Radar breaches hidden

Hidden widgets are available from the Widget Library and can be added to any layout.

Layout and persistence

  • The grid is rendered by DashboardCanvas using react-grid-layout, with drag-and-drop powered by dnd-kit.
  • Live state flows through two React contexts: DashboardConfigContext (the saved config) and DashboardEditContext (edit-mode buffer).
  • The active layout and widget set are stored per user in DashboardConfig (layout and widgets JSON columns), saved via PUT /api/dashboard/config.

Customize interactions

In Customize mode widgets can be dragged, resized and renamed. Two custom behaviours keep the grid coherent instead of letting the engine shove everything down:

  • Drag swap: dropping a widget onto another exchanges their slots and sizes (the target is the widget the dragged footprint overlaps most), rather than displacing a whole column.
  • Resize squeeze: growing a widget shrinks the colliding row neighbour to reclaim the freed columns instead of pushing it below. A neighbour that can't shrink past its minW is evicted to the next row, where it reflows that row in turn so the zone below makes room. Growing a widget's height leaves the widgets stacked directly below untouched; vertical compaction pushes them straight down.

Both run inside a custom react-grid-layout compactor in DashboardCanvas; the pure layout maths are unit-tested in dashboardLayoutUtils.ts (findSwapTarget, squeezeResize).

Presets

Presets capture a named layout + widget set, scoped either PERSONAL (one user) or COMPANY (shared). The active preset is tracked on User.activePresetId.

Endpoint Action
GET/POST /api/dashboard/presets List / create presets
PATCH/DELETE /api/dashboard/presets/[id] Rename / delete a preset
POST /api/dashboard/presets/[id]/activate Make a preset the active layout

Risk score

The risk-gauge and Key Metrics surface a 0-100 company risk score computed by calculateRiskScore in src/lib/risk.ts:

exposureScore = (compromisedEmployees / totalEmployees) * 40
alertScore    = min(critical*12 + high*6 + medium*2, 40)
recencyScore  = min(recentBreaches * 4, 20)
score         = min(round(exposure + alert + recency), 100)

getRiskLevel(score) buckets it: 76+ Critical, 51-75 High, 26-50 Medium, 0-25 Low.

Clone this wiki locally