Configurable user dashboard — interactive + / remove / customize widgets #46

Open
opened 2026-05-20 08:38:42 +00:00 by mAi · 3 comments
Collaborator

Trigger

m 2026-05-20 10:42:

We should instruct an inventor to make the user dashboard configurable for the users, preferably interactively with a "+" and then they can add elements or remove them, customize etc. Bigger feature.

Current state

The logged-in landing (/ or /dashboard) currently renders a fixed layout: deadline traffic lights, upcoming appointments, recent activity, all scoped to visible projects (per project CLAUDE.md > Purpose > Dashboard). Every user sees the same widgets in the same order; there's no personalisation.

What m wants

  • Per-user customisable dashboard. Each user picks which widgets appear, in what order, possibly with per-widget settings (size, filter, time range).
  • Interactive editing. A + button to add widgets, an x (or drag-to-trash) to remove, a drag-to-reorder gesture. "Edit mode" toggle if necessary to avoid accidental rearrangement during normal use.
  • Customise per widget. Each widget exposes its own settings (e.g. deadline widget: which time horizon, which projects, which approval status filter; activity widget: how many entries, which event types).
  • Persistence. Layout + per-widget config saved per user, server-side. Reloads survive across sessions and devices.

Design considerations (not prescriptive — for the inventor's design pass)

  • Widget catalog. What's the v1 widget set? Start with what's already on the dashboard (deadlines, appointments, activity) + the obvious adjacencies (pinned projects, recent files, my open approvals). Define a Widget shape (id, title, default size, schema for per-widget config).
  • Storage model. A paliad.user_dashboard_layouts table? A JSON blob on users.metadata? A normalised paliad.user_dashboard_widgets (user_id, widget_id, position, settings jsonb)? The schema choice has implications for migration (adding a new widget type) and for reordering UX (drag updates 1 row vs. n rows).
  • Grid system. Fixed N-column grid (e.g. 12-col bootstrap-style) vs. masonry vs. drag-anywhere. Mobile fallback (single column, dragged order respected).
  • Edit mode UX. Always-on edit handles vs. a toggle button. Persistence on every drag vs. on save-button click vs. autosave-with-debounce. Undo / reset-to-default option.
  • Defaults. What does a brand-new user see (factory layout)? Can a global_admin set a firm-wide default that propagates to users who haven't customised?
  • Empty-state widgets. What does a deadline widget show when the user has no deadlines? An add-deadline CTA? Empty illustration? Just hide the widget?
  • Widget contract. Server-side hydration vs. client-fetched? Each widget owns its own loader function + render function? Per-widget loading skeleton?
  • Versioning. As we add widget types over time, how do we handle a user's saved layout referencing a widget that no longer exists (deprecated)?
  • Accessibility. Drag-and-drop alternatives for keyboard users (arrow-key reorder?).
  • i18n. Widget titles + settings labels in DE + EN per app convention.

Out of scope

  • Custom widgets defined by the user (formula-style, SQL-style, scriptable). v1 is a curated catalog the firm ships.
  • Widget-to-widget connections / cross-widget filters (a la BI dashboards). Each widget is independent.
  • Sharing a layout between users. Each user customises their own.
  • Admin-level configurable layouts beyond a single firm-wide default.

Acceptance

  • A user can enter edit mode on /dashboard, click + to add a widget from the catalog, click x to remove, drag to reorder, configure each widget's per-widget settings, and persist all of it.
  • The layout reloads correctly on a fresh login from a different device.
  • A brand-new user sees the factory-default layout.
  • A widget whose backing data is unavailable (e.g. removed from the catalog) doesn't crash the dashboard — it gracefully hides itself.
  • At least 3 widget types ship in v1 (deadlines, appointments, recent activity — the existing dashboard set, now as discrete widgets).

Role recommendation

inventor — bigger feature, design pass justified. After m's go-ahead on the design, slice plan likely 3+ slices:

  • Slice A: schema + Go service + API + factory-default rendering on /dashboard (server-side, no edit mode yet).
  • Slice B: edit mode + drag/drop + per-widget config UI + persistence.
  • Slice C: catalog expansion (additional widget types beyond the v1 three) + admin firm-wide default.

Branch: mai/<inventor>/dashboard-configurable.

## Trigger m 2026-05-20 10:42: > We should instruct an inventor to make the user dashboard configurable for the users, preferably interactively with a "+" and then they can add elements or remove them, customize etc. Bigger feature. ## Current state The logged-in landing (`/` or `/dashboard`) currently renders a fixed layout: deadline traffic lights, upcoming appointments, recent activity, all scoped to visible projects (per project CLAUDE.md > Purpose > Dashboard). Every user sees the same widgets in the same order; there's no personalisation. ## What m wants - **Per-user customisable dashboard.** Each user picks which widgets appear, in what order, possibly with per-widget settings (size, filter, time range). - **Interactive editing.** A `+` button to add widgets, an `x` (or drag-to-trash) to remove, a drag-to-reorder gesture. "Edit mode" toggle if necessary to avoid accidental rearrangement during normal use. - **Customise per widget.** Each widget exposes its own settings (e.g. deadline widget: which time horizon, which projects, which approval status filter; activity widget: how many entries, which event types). - **Persistence.** Layout + per-widget config saved per user, server-side. Reloads survive across sessions and devices. ## Design considerations (not prescriptive — for the inventor's design pass) - **Widget catalog.** What's the v1 widget set? Start with what's already on the dashboard (deadlines, appointments, activity) + the obvious adjacencies (pinned projects, recent files, my open approvals). Define a `Widget` shape (id, title, default size, schema for per-widget config). - **Storage model.** A `paliad.user_dashboard_layouts` table? A JSON blob on `users.metadata`? A normalised `paliad.user_dashboard_widgets` (user_id, widget_id, position, settings jsonb)? The schema choice has implications for migration (adding a new widget type) and for reordering UX (drag updates 1 row vs. n rows). - **Grid system.** Fixed N-column grid (e.g. 12-col bootstrap-style) vs. masonry vs. drag-anywhere. Mobile fallback (single column, dragged order respected). - **Edit mode UX.** Always-on edit handles vs. a toggle button. Persistence on every drag vs. on save-button click vs. autosave-with-debounce. Undo / reset-to-default option. - **Defaults.** What does a brand-new user see (factory layout)? Can a global_admin set a firm-wide default that propagates to users who haven't customised? - **Empty-state widgets.** What does a deadline widget show when the user has no deadlines? An add-deadline CTA? Empty illustration? Just hide the widget? - **Widget contract.** Server-side hydration vs. client-fetched? Each widget owns its own loader function + render function? Per-widget loading skeleton? - **Versioning.** As we add widget types over time, how do we handle a user's saved layout referencing a widget that no longer exists (deprecated)? - **Accessibility.** Drag-and-drop alternatives for keyboard users (arrow-key reorder?). - **i18n.** Widget titles + settings labels in DE + EN per app convention. ## Out of scope - Custom widgets defined by the user (formula-style, SQL-style, scriptable). v1 is a curated catalog the firm ships. - Widget-to-widget connections / cross-widget filters (a la BI dashboards). Each widget is independent. - Sharing a layout between users. Each user customises their own. - Admin-level configurable layouts beyond a single firm-wide default. ## Acceptance - A user can enter edit mode on /dashboard, click `+` to add a widget from the catalog, click `x` to remove, drag to reorder, configure each widget's per-widget settings, and persist all of it. - The layout reloads correctly on a fresh login from a different device. - A brand-new user sees the factory-default layout. - A widget whose backing data is unavailable (e.g. removed from the catalog) doesn't crash the dashboard — it gracefully hides itself. - At least 3 widget types ship in v1 (deadlines, appointments, recent activity — the existing dashboard set, now as discrete widgets). ## Role recommendation **inventor** — bigger feature, design pass justified. After m's go-ahead on the design, slice plan likely 3+ slices: - Slice A: schema + Go service + API + factory-default rendering on /dashboard (server-side, no edit mode yet). - Slice B: edit mode + drag/drop + per-widget config UI + persistence. - Slice C: catalog expansion (additional widget types beyond the v1 three) + admin firm-wide default. Branch: `mai/<inventor>/dashboard-configurable`.
mAi self-assigned this 2026-05-20 08:38:42 +00:00
Author
Collaborator

t-paliad-219 Slice A complete — backend storage + service + handlers + frontend widget dispatch

Branch: mai/newton/inventor-configurable (pushed)
Commits since shift-1 design:

  • bfd6c9a Slice A1 — migration 109 + DashboardLayoutSpec + DashboardLayoutService + WidgetCatalog (7 entries) + 18 pure-function tests + 4 live-DB tests
  • 607b58f Slice A2 — 4 HTTP endpoints (GET/PUT /api/me/dashboard-layout, POST .../reset, GET /api/dashboard-widget-catalog) + main.go wiring
  • fdc4b14 Slice A3 — DashboardService widened to 60d/LIMIT 40 for upcoming-deadlines+appointments + new InboxSummary field powered by ApprovalService.PendingCountForUser/ListPendingForApprover
  • 297d20b Slice A4 — frontend: triple hydration placeholders, dashboard.tsx data-widget-key attributes + inbox-approvals section markup, dashboard.ts widget dispatcher (applyLayout + settingsFor + filterByHorizonDays + renderInbox), 5 new i18n keys

Verification:

  • go build ./... clean
  • go vet ./... clean
  • go test ./internal/... -short -count=1 all green (25 new tests pass)
  • bun run build clean — 2528 → 2533 i18n keys, scan clean
  • Migration 109 dry-run live (BEGIN..ROLLBACK on supabase) — clean
  • All three __PALIAD_DASHBOARD_*__ placeholders present in dist/dashboard.html

Pixel-identical factory render budget: held. Every previously-visible widget keeps its DOM markup, classes, IDs, and parent. New widget (inbox-approvals) lands between agenda and activity per WidgetCatalog order — expected visible regression per m's Q3 pick.

Design doc deviation note:

  • Migration slot bumped from 107 (design doc) → 109 (live) because leibniz claimed 107 (caldav_sync_log.binding_id) and 108 (caldav_mkcalendar_capability) on main after the design was filed. Boltzmann's gap-tolerant runner (c85c382) covers the race.
  • Slice C0 from the design doc is OBSOLETE. paliad.user_pinned_projects + PinService already exist in the codebase (pre-dates this task). Slice C reduces to one PR adding the pinned-projects widget module + catalog entry.

What's next:

  • Slice B: edit mode (toggle, drag/↑↓/x/+ chrome, picker modal, gear popover, autosave debounce, reset-to-default).
  • Slice C: pinned-projects widget reading from existing PinService.

Awaiting maria's review and merge to main before continuing to Slice B.

### t-paliad-219 Slice A complete — backend storage + service + handlers + frontend widget dispatch **Branch:** `mai/newton/inventor-configurable` (pushed) **Commits since shift-1 design:** - `bfd6c9a` Slice A1 — migration 109 + `DashboardLayoutSpec` + `DashboardLayoutService` + `WidgetCatalog` (7 entries) + 18 pure-function tests + 4 live-DB tests - `607b58f` Slice A2 — 4 HTTP endpoints (`GET/PUT /api/me/dashboard-layout`, `POST .../reset`, `GET /api/dashboard-widget-catalog`) + main.go wiring - `fdc4b14` Slice A3 — `DashboardService` widened to 60d/LIMIT 40 for upcoming-deadlines+appointments + new `InboxSummary` field powered by `ApprovalService.PendingCountForUser`/`ListPendingForApprover` - `297d20b` Slice A4 — frontend: triple hydration placeholders, dashboard.tsx data-widget-key attributes + inbox-approvals section markup, dashboard.ts widget dispatcher (`applyLayout` + `settingsFor` + `filterByHorizonDays` + `renderInbox`), 5 new i18n keys **Verification:** - `go build ./...` clean - `go vet ./...` clean - `go test ./internal/... -short -count=1` all green (25 new tests pass) - `bun run build` clean — 2528 → 2533 i18n keys, scan clean - Migration 109 dry-run live (BEGIN..ROLLBACK on supabase) — clean - All three `__PALIAD_DASHBOARD_*__` placeholders present in `dist/dashboard.html` **Pixel-identical factory render budget:** held. Every previously-visible widget keeps its DOM markup, classes, IDs, and parent. New widget (inbox-approvals) lands between agenda and activity per `WidgetCatalog` order — expected visible regression per m's Q3 pick. **Design doc deviation note:** - Migration slot bumped from 107 (design doc) → 109 (live) because leibniz claimed 107 (`caldav_sync_log.binding_id`) and 108 (`caldav_mkcalendar_capability`) on `main` after the design was filed. Boltzmann's gap-tolerant runner (`c85c382`) covers the race. - **Slice C0 from the design doc is OBSOLETE.** `paliad.user_pinned_projects` + `PinService` already exist in the codebase (pre-dates this task). Slice C reduces to one PR adding the pinned-projects widget module + catalog entry. **What's next:** - Slice B: edit mode (toggle, drag/↑↓/x/+ chrome, picker modal, gear popover, autosave debounce, reset-to-default). - Slice C: pinned-projects widget reading from existing PinService. Awaiting maria's review and merge to main before continuing to Slice B.
Author
Collaborator

Slice B shipped via newton; merged at https://mgit.msbls.de/m/paliad/commit/0857c1c. Edit mode + drag/drop + autosave live after Dokploy deploy. Slice C (catalog expansion + admin firm-wide default) remaining.

Slice B shipped via newton; merged at https://mgit.msbls.de/m/paliad/commit/0857c1c. Edit mode + drag/drop + autosave live after Dokploy deploy. Slice C (catalog expansion + admin firm-wide default) remaining.
Author
Collaborator

Slice C shipped via newton; merged at https://mgit.msbls.de/m/paliad/commit/ca71162. m/paliad#46 complete (Slices A + B + C). Live after Dokploy deploy.

Slice C shipped via newton; merged at https://mgit.msbls.de/m/paliad/commit/ca71162. m/paliad#46 complete (Slices A + B + C). Live after Dokploy deploy.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#46
No description provided.