diff --git a/workplans/IHUB-WP-0018-personal-dashboard-framework.md b/workplans/IHUB-WP-0018-personal-dashboard-framework.md new file mode 100644 index 0000000..aeaab7d --- /dev/null +++ b/workplans/IHUB-WP-0018-personal-dashboard-framework.md @@ -0,0 +1,292 @@ +--- +id: IHUB-WP-0018 +type: workplan +title: "Personal Dashboard Framework" +domain: inter-hub +status: todo +owner: tegwick +created: "2026-05-03" +updated: "2026-05-03" +phase: 13 +--- + +# Personal Dashboard Framework + +## Goal + +Design and implement a personal dashboard framework that allows individual users to +compose, configure, and persist a view of the inter-hub platform tailored to their +role and focus. The dashboard is the post-login landing page and the primary daily +driver surface for hub operators, governance reviewers, and AI orchestrators. + +## Motivation + +The current post-login experience drops the user on a raw Hubs list. As inter-hub +grows to 12+ phases of functionality, users need a curated, role-aware entry point +that surfaces the signals that matter to them without requiring manual navigation. +The dashboard is also the natural home for cross-cutting observability (recent +decisions, open candidates, outcome signals) that cuts across the current +controller-per-entity navigation. + +--- + +## Tasks + +### T01 — Research: Dashboard frameworks and patterns for inspiration + +```task +id: IHUB-WP-0018-T01 +status: todo +priority: high +``` + +Survey existing dashboard systems to extract patterns that are re-implementable in +Haskell / IHP / Tailwind under inter-hub's design constraints (server-rendered, +type-safe, governed). + +**Research targets:** + +| System | What to extract | +|---|---| +| **Grafana** | Panel/grid layout model; datasource abstraction; variable-driven filtering | +| **Kibana (dashboards)** | Saved-search panels; time-range awareness; role-based visibility | +| **Retool / Appsmith** | Widget catalogue approach; drag-grid layout; data binding model | +| **Linear (home view)** | Flat "My Work" aggregation across entities; priority surfacing | +| **Notion (linked databases)** | Filter/sort persistence per user; view types (table, board, calendar) | +| **Observable Framework** | Reactive cell model; markdown + code co-location | +| **Metabase** | Question-as-unit; dashboard as ordered collection of questions | +| **Streamlit** | Declarative layout (columns, expanders); pure functional rendering loop | + +**Questions to answer per system:** +1. How is a dashboard persisted? (JSON blob, relational rows, code-as-config?) +2. How is a widget/panel parameterised? (datasource, filter, display options) +3. How is layout described? (fixed grid, CSS grid, drag-and-drop, responsive breakpoints) +4. How is per-user state separated from shared/team state? +5. What is the update model? (full-page reload, WebSocket push, polling, partial HTMX swap) +6. How are access controls expressed at panel level? + +**IHP/Haskell-specific constraints to keep in mind:** +- Server-rendered by default; AutoRefresh (WebSocket) available for live data +- No client-side state management library; JS must be minimal +- Type safety from DB schema → view layer is a first-class constraint +- Tailwind CSS; no component library + +**Exit criteria:** Research notes written in `docs/research/dashboard-frameworks.md`. + +--- + +### T02 — Product Requirements Specification (PRS) + +```task +id: IHUB-WP-0018-T02 +status: todo +priority: high +depends_on: T01 +``` + +Produce a formal PRS for the Personal Dashboard Framework based on T01 findings and +inter-hub's design principles. + +**Required sections:** + +1. **Problem statement** — who uses the dashboard, what decisions they make with it, + what pain the current flat-nav approach causes + +2. **User personas** + - Hub Operator: monitors activity within their hub; wants recent events, open candidates + - Governance Reviewer: triages candidates, reviews decisions; needs queue and signal views + - AI Orchestrator: monitors agent proposals, outcome correlations; needs performance panels + - Platform Admin: watches system health, API usage, learning throughput + +3. **Core requirements (MoSCoW)** + - Must: per-user dashboard persisted in DB; selectable panels from a catalogue; + layout preserved across sessions; role-aware default layout on first login + - Should: panel-level filtering (by hub, by time range); live-update via AutoRefresh + for signal panels; keyboard-navigable + - Could: drag-and-drop layout editing; shared/team dashboards; dashboard templates + - Won't (Phase 13): mobile-native layout; client-side data fetching; external datasources + +4. **Non-functional requirements** + - First paint < 500 ms (server-rendered, no JS data fetching) + - Dashboard save/load < 100 ms + - Each panel query < 200 ms (indexed, bounded result sets) + - Zero JS frameworks; AutoRefresh WebSocket for live panels only + +5. **Governance fit** — dashboard widgets are themselves `Widget` records in the IHF + sense; `InteractionEvent`s recorded on dashboard interactions; annotations possible + on any panel + +**Exit criteria:** `docs/prs/dashboard-framework-prs.md` reviewed and accepted. + +--- + +### T03 — Functional Design Document (FDD) + +```task +id: IHUB-WP-0018-T03 +status: todo +priority: high +depends_on: T02 +``` + +Translate the PRS into a concrete functional design covering schema, component model, +rendering pipeline, and layout system. This is the authoritative reference for implementation. + +**Required sections:** + +#### 3.1 Data model + +```sql +-- A user's named dashboard +CREATE TABLE dashboards ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + user_id UUID NOT NULL REFERENCES users(id), + name TEXT NOT NULL, + is_default BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- An instance of a panel type on a dashboard, with position +CREATE TABLE dashboard_panels ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + dashboard_id UUID NOT NULL REFERENCES dashboards(id) ON DELETE CASCADE, + panel_type TEXT NOT NULL, -- FK to panel_type_registry + config JSONB NOT NULL DEFAULT '{}', + col INT NOT NULL DEFAULT 0, + row INT NOT NULL DEFAULT 0, + col_span INT NOT NULL DEFAULT 1, + row_span INT NOT NULL DEFAULT 1, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- Registry of available panel types +CREATE TABLE panel_type_registry ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, + name TEXT NOT NULL UNIQUE, + label TEXT NOT NULL, + description TEXT, + default_config JSONB NOT NULL DEFAULT '{}', + requires_hub BOOLEAN NOT NULL DEFAULT FALSE, + live_update BOOLEAN NOT NULL DEFAULT FALSE +); +``` + +#### 3.2 Panel catalogue (Phase 13 scope) + +| Panel type name | Label | Description | Live? | +|---|---|---|---| +| `recent-interactions` | Recent Activity | Latest interaction events across watched hubs | Yes | +| `open-candidates` | Open Candidates | Requirement candidates awaiting triage | No | +| `decision-queue` | Decision Queue | Decisions pending review | No | +| `outcome-signals` | Outcome Signals | Recent outcome signal summary | Yes | +| `hub-health` | Hub Health | Health snapshot per hub | No | +| `agent-proposals` | Agent Proposals | Open AI agent proposals | No | +| `learning-digest` | Learning Digest | Latest institutional knowledge entries | No | +| `my-annotations` | My Annotations | Annotations by the current user | No | + +#### 3.3 Layout system + +12-column CSS grid; panels occupy `col_span` columns and `row_span` rows. +Row height fixed at 240px. No drag-and-drop in Phase 13; layout edited via +form fields (col, row, span). Responsive: collapse to single column below 768px. + +``` +┌─────────────────────────────────────────────────────┐ +│ [Recent Activity ×6] [Decision Queue ×3] [Hub ×3]│ +│ │ +├─────────────────────────────────────────────────────┤ +│ [Open Candidates ×4] [Outcome Signals ×4] [My ×4] │ +└─────────────────────────────────────────────────────┘ +``` + +#### 3.4 Rendering pipeline + +``` +GET /Dashboard + → DashboardController#show + → fetch dashboard + panels (ordered by row, col) + → for each panel: dispatch to panel renderer (panelHtml panelType config) + → embed in DashboardView with CSS grid layout + → AutoRefresh wraps live panels only +``` + +Each panel renderer is a Haskell function: +```haskell +type PanelRenderer = PanelConfig -> ModelContext -> IO Html +renderPanel :: Text -> PanelConfig -> ModelContext -> IO Html +renderPanel "recent-interactions" = renderRecentInteractions +renderPanel "open-candidates" = renderOpenCandidates +... +``` + +#### 3.5 Edit flow + +`GET /Dashboard/Edit` → grid with inline forms per panel (col/row/span inputs) + +"Add Panel" dropdown from `panel_type_registry`. `POST /Dashboard` saves layout. +No JavaScript needed for basic edit; optional HTMX for panel preview. + +#### 3.6 Default dashboard on first login + +`afterLoginRedirectPath` (in `SessionsControllerConfig`) redirects to `/Dashboard`. +On first visit, `DashboardController` checks for an existing dashboard; if absent, +creates a default one seeded from the user's role (determined by a `role` field +to be added to `users`, or a simple heuristic based on existing data). + +**Exit criteria:** FDD reviewed; schema migrations drafted; panel catalogue agreed. + +--- + +### T04 — Implementation workplan + +```task +id: IHUB-WP-0018-T04 +status: todo +priority: medium +depends_on: T03 +``` + +Break T03's FDD into a detailed, sequenced task list suitable for execution as a new +workplan (IHUB-WP-0019). Each task must have a clear entry/exit criterion and fit within +the 8k token soft budget. + +**Expected task structure of IHUB-WP-0019:** + +``` +T01 Schema migration: dashboards, dashboard_panels, panel_type_registry +T02 Seed: default panel types in panel_type_registry +T03 DashboardController — show action (fetch + render) +T04 Panel renderers — first 3 panels (recent-interactions, open-candidates, decision-queue) +T05 DashboardView — CSS grid layout +T06 Panel renderers — remaining 5 panels +T07 Dashboard edit flow (layout form, add/remove panels) +T08 Default dashboard seeding on first login +T09 afterLoginRedirectPath → /Dashboard +T10 AutoRefresh for live panels (recent-interactions, outcome-signals) +T11 Role-aware default layout +T12 Smoke tests +``` + +**Exit criteria:** IHUB-WP-0019 workplan file committed; T01–T12 each have +entry/exit criteria; ready for execution. + +--- + +## Exit Criteria Summary + +| Task | Deliverable | Status | +|---|---|---| +| T01 | `docs/research/dashboard-frameworks.md` | todo | +| T02 | `docs/prs/dashboard-framework-prs.md` | todo | +| T03 | `docs/fdd/dashboard-framework-fdd.md` | todo | +| T04 | `workplans/IHUB-WP-0019-personal-dashboard-impl.md` | todo | + +## Design Principles (binding throughout) + +- **Server-first**: every panel renders in a single round-trip. No client-side data fetching. +- **Type-safe config**: `PanelConfig` is a Haskell ADT, not an opaque JSON blob at runtime. +- **IHF governed**: each rendered panel is a `Widget` with a `widgetEnvelope`; interactions + are recorded; annotations can be attached. +- **Tailwind only**: no external component library. Layout via CSS Grid with inline style for + structural properties (lessons learned from sidebar nav). +- **Minimal JS**: AutoRefresh WebSocket for live panels; vanilla JS for any UX enhancement. + No framework, no bundler beyond the existing IHP asset pipeline.