--- id: IHUB-WP-0020 type: workplan title: "Personal Dashboard Framework" domain: inter_hub repo: inter-hub status: backlog owner: tegwick topic_slug: inter_hub created: "2026-05-03" updated: "2026-06-07" phase: 13 state_hub_workstream_id: "72fc022b-0196-492a-aaba-3475f8768f06" --- # 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-0020-T01 status: todo priority: high state_hub_task_id: "6074f195-636b-4517-b6d1-eb3c57394a82" ``` 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-0020-T02 status: todo priority: high depends_on: T01 state_hub_task_id: "698304bc-b91a-42e2-a617-b3ddbf749174" ``` 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-0020-T03 status: todo priority: high depends_on: T02 state_hub_task_id: "438e5771-a043-4f26-a1ce-994ed478a760" ``` 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-0020-T04 status: todo priority: medium depends_on: T03 state_hub_task_id: "970aa221-7e17-4500-8b37-9c98676280b1" ``` Break T03's FDD into a detailed, sequenced task list suitable for execution as a new workplan (IHUB-WP-0021). Each task must have a clear entry/exit criterion and fit within the 8k token soft budget. **Expected task structure of IHUB-WP-0021:** ``` 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-0021 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-0021-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.