docs(plans): dashboard overhaul Phase 5h design
Phase A inventor deliverable: 3 candidate shapes (Tiles + view switcher, Project rows table, 3-pane workspace), recommendation = Tiles, and m's chip-picker decisions captured in §7. Scope locked for coder gate: Tiles default, Pinned ∪ active ∪ open-work as 'current', 3 tabs (Tiles/Tasks/Events) — Activity deferred, Stale folded into Quiet under Tiles. No code touched. Coder shift only after head's go/no-go.
This commit is contained in:
325
docs/plans/dashboard-overhaul.md
Normal file
325
docs/plans/dashboard-overhaul.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# Dashboard overhaul — Phase 5h design
|
||||
|
||||
**Status:** Phase A (design). No code touched. Awaiting m's go/no-go via head.
|
||||
**Branch:** `mai/fuller/phase-5h-phase-a-design`.
|
||||
**Author:** fuller (inventor), 2026-05-26.
|
||||
**Source request, verbatim:** *"The Dashboard could really use an overhaul as well. Of course we should continue to use unified filters but the dashboard should allow all kinds of views and easily show a helpful overview over my current projects."*
|
||||
|
||||
---
|
||||
|
||||
## §1 — Current state diagnosis
|
||||
|
||||
`/dashboard` today is a 5-card stream renderer. The shape, with row counts derived from the live data:
|
||||
|
||||
| Card | What it shows | Sort | Cap |
|
||||
|---|---|---|---|
|
||||
| Open tasks | Every open VTODO across all linked CalDAV lists, bucketed Overdue/Today/Tomorrow/Week/No-due | overdue→due asc→prio→summary | 30 |
|
||||
| Events | Every VEVENT next 7d, grouped by day | start asc | 50 |
|
||||
| Open issues | Every open Gitea issue across all linked repos | updated_at desc | 30 |
|
||||
| Recent documents | Every dated `item_link` in the last 30d | event_date desc | 30 |
|
||||
| Stale projects | mai-managed items with quiet repo + 0 open tasks + 0 open issues | stale_days desc | 20 |
|
||||
|
||||
Filter strip: `tag`, `mgmt`, `has` chips reusing `TreeFilter`. 60s cache keyed by encoded filter. `?refresh=1` busts. Empty cards collapse to one-liners when no filter is active. Inline VTODO ✓/✎/× on the Tasks card.
|
||||
|
||||
### What works (don't throw away)
|
||||
- **Aggregation pipeline** (`internal/aggregate.Aggregator`) — solid, cached, fan-out workers. Any new shape should ride on the same Todos/Events/Issues/Docs primitives.
|
||||
- **Filter parity** with `/`, `/timeline`, `/calendar`, `/graph`. The chip vocabulary is a real asset; m said keep it.
|
||||
- **Cache shape** — 60s TTL keyed by filter is fine. Tasks-card writeback already invalidates correctly.
|
||||
- **Inline VTODO writeback** is genuinely useful — one ✓ click clears a row without leaving the page.
|
||||
- **Stale card** as a "consider archiving" candidate list. Tiny, surfaces a question that's otherwise hidden.
|
||||
|
||||
### What doesn't fit m's request
|
||||
1. **Task-centric, not project-centric.** m sees "every open task" / "every open issue" as a giant flat stream. He cannot scan the list and answer *"how is project X doing?"* without ctrl-F. He explicitly asked for a project overview.
|
||||
2. **No notion of "current".** Every active project contributes equally. Of 47 active items, m's likely day-to-day focus is ~5-10 (the ones with recent activity + open work). The dashboard buries those signals.
|
||||
3. **One view, no switcher.** m said *"all kinds of views"*. Today it's the 5-card stream and that's it.
|
||||
4. **Pinning is underused.** Only 2 items are pinned (`dev`, `projax`) — pinning today has no visible payoff anywhere, so m doesn't bother. A project-centric dashboard would give pins a job.
|
||||
5. **Stale card surfaces non-actionable noise.** Once m sees a project once and decides "not now, not archive", the row stays forever. No snooze, no dismiss. Becomes wallpaper.
|
||||
6. **Per-project signals are scattered.** To see paliad's status m has to scan five separate cards for paliad rows. Should be one tile.
|
||||
|
||||
### What's already settled (don't redesign)
|
||||
- Unified filter chips (`?q`, `?tag`, `?mgmt`, `?has`, `?public`) — preserved verbatim.
|
||||
- Server-rendered Go `html/template` + HTMX. No JS framework.
|
||||
- Mobile + desktop both via the new sidebar (≥768px) / bottom-nav (≤767px) layout.
|
||||
- No file uploads, no new schema columns without head sign-off, no CLI surface.
|
||||
|
||||
---
|
||||
|
||||
## §2 — m's mental model: what "current projects" means
|
||||
|
||||
Reading the live data (`mcp__projax__list_items`):
|
||||
|
||||
- **47 active items**, of which 7 are root areas (`admin`, `dev`, `finances`, `health`, `home`, `social`, `work`, plus the synthetic `public` parent).
|
||||
- **14 items are public** — `dev.fdbck`, `dev.flexsiebels`, `dev.goldi`, `dev.imagen`, `dev.mai`, `dev.mclone`, `dev.mvoice`, `dev.otto`, `dev.mcompete`, `dev.mlex`, `dev.upc-kommentar`, `dev.youpcorg`, `health.sports.manjin`, `work.paliad`. These are clearly the projects m considers *portfolio-worthy* — code projects he wants the world to see.
|
||||
- **Only 2 items are pinned**: `dev` (root area) and `projax` (this very project). Pinning is structurally there but visually invisible, so it's not used. That's an opportunity, not a constraint — if pins surface clearly, m starts using them.
|
||||
- **Activity signal** is reachable cheaply: every mai-managed item has a `gitea-repo` link → repo `updated_at` (already cached for the Stale card). Every linked CalDAV list has VTODO/VEVENT counts (already aggregated). A project is "alive" if any of: open VTODO, open issue, repo touched in last 14d, dated link in last 14d.
|
||||
|
||||
So "**current projects**" isn't a fixed predicate — it's a small ordered subset. A workable definition for v1 is the union of:
|
||||
- **Pinned** (m's explicit "this matters now" signal — promote pins to UI-visible),
|
||||
- **Active recently** (repo activity OR open VTODO with due ≤ 14d OR dated link in last 14d),
|
||||
- **At risk** (any overdue VTODO, or open issues with stale `updated_at`).
|
||||
|
||||
Everything else — the 30+ projects that are technically `active` but not on m's plate this week — should be reachable but **not on the primary surface**. A "show all active" toggle handles them.
|
||||
|
||||
Crucially: the *definition* of "current" is configurable. The chip strip already lets m narrow by tag / mgmt / has-links. The new dashboard adds an implicit "current" prefilter on top of that, with a chip to lift it.
|
||||
|
||||
---
|
||||
|
||||
## §3 — Candidate shapes
|
||||
|
||||
Three viable directions. For each: sketch, signals, view switcher behaviour, filter integration, tradeoffs, cost.
|
||||
|
||||
### Candidate A — Project Tiles + view switcher
|
||||
|
||||
**Sketch (desktop, ~1200px main column, 3-column tile grid):**
|
||||
|
||||
```
|
||||
┌─ Dashboard ──────────────────────────────────────────────────────────┐
|
||||
│ [Tiles] [Tasks] [Events] [Activity] · tag▾ mgmt▾ has▾ · ↻ updated 2m │
|
||||
│ ◇ pinned + active ○ show all active │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ ★ paliad │ │ ★ projax │ │ ★ youpcorg │ │
|
||||
│ │ work · live │ │ dev · this │ │ dev · live │ │
|
||||
│ │ 4 open · 2! │ │ 7 open │ │ 1 open │ │
|
||||
│ │ • next call │ │ • 5h dash │ │ • 1600+ judg │ │
|
||||
│ │ 2d ago │ │ now │ │ 4d ago │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ flexsiebels │ │ otto │ │ mai │ │
|
||||
│ │ dev · live │ │ dev · live │ │ dev · live │ │
|
||||
│ │ 2 open │ │ 9 open · 3! │ │ 5 open │ │
|
||||
│ │ • home page │ │ • i64 deploy │ │ • #218 mAi │ │
|
||||
│ │ 1d ago │ │ 3h ago │ │ 6h ago │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
│ Quiet (12) ▾ Archived (3) ▾ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Per-tile signals** (top→bottom):
|
||||
- Title + pin star (click to toggle). Primary path under title (`work.paliad`). Live URL pill if `public_live_url` is set.
|
||||
- **Counts row**: `N open` (open VTODOs across linked calendars), `M!` (overdue), optional `K issues` (open Gitea issues). Color: overdue red, otherwise default. Click navigates to `/i/<path>/`.
|
||||
- **Top signal line**: the single most-relevant next thing — either the soonest-due open VTODO, or the most-recently-updated open issue, or last commit summary. One line, truncated.
|
||||
- **Last-activity stamp**: `2d ago` / `3h ago` / `now`. Derived from max(repo.updated_at, latest_vtodo_modified, latest_dated_link).
|
||||
- **Optional micro-bar** at the bottom — colored stripe showing the ratio overdue/today/week tasks. Skip for v1 if cluttered.
|
||||
|
||||
**View switcher** (top tab strip, 4 views, same filter + scope chips apply across all):
|
||||
- **Tiles** (default) — the grid above. Project-centric.
|
||||
- **Tasks** — the existing Open-tasks card + Events card, full-width. Task-centric, today's view. Same inline ✓/✎/× writeback.
|
||||
- **Events** — the existing Events card alone, full-width with bigger day headers. The "what's on my calendar" view.
|
||||
- **Activity** — a chronological feed merging recent commits (repo `updated_at` per linked repo), closed issues, completed VTODOs, dated docs. "What just happened across my projects." This is genuinely new; deferable to v2 if scope-tight.
|
||||
|
||||
**Scope chip** (next to filter chips, top-row): `◇ pinned + active` (default — the "current projects" prefilter) ↔ `○ show all active`. Single radio. Lifts the implicit "current" filter so m can browse the long tail.
|
||||
|
||||
**Filter integration**: existing `tag` / `mgmt` / `has` / `public` chips narrow the project set across all four views. URL stays a single `/dashboard?view=tiles&tag=work&scope=current` so links bookmark cleanly. Default view (`tiles`) + default scope (`current`) elided from URL.
|
||||
|
||||
**Tradeoffs**:
|
||||
- ✅ Best match for m's explicit ask. "Helpful overview over my current projects" maps almost literally to the tile grid.
|
||||
- ✅ Pinning gets a visible job. Stars on tiles make the feature legible.
|
||||
- ✅ View switcher gives m the "all kinds of views" he asked for, without losing today's task-centric value (it's the `Tasks` tab).
|
||||
- ✅ Per-tile signals tell a story at a glance. Scrolling 8 tiles is faster than scrolling 30 unrelated task rows.
|
||||
- ❌ Tile signals require a per-project rollup the current `dashboardPayload` doesn't compute. Moderate aggregation work — group existing TodoRow/IssueRow/repo-updated by item.ID.
|
||||
- ❌ The Activity view is the most novel and the highest risk; can be cut from v1 if needed (3 tabs is fine).
|
||||
- ❌ Tiles trade information density for legibility. A power user wanting "everything everywhere" has to flip to Tasks view.
|
||||
|
||||
**Implementation cost**: moderate.
|
||||
- `dashboard.go` grows a per-project rollup (group existing rows by `Item.ID`, compute `LastActivity = max(repoUpdated, dueSoonest, eventSoonest, docLatest)`).
|
||||
- New `dashboardProject` struct + tiles template partial.
|
||||
- View-switcher = a URL `?view=` param routing into one of 4 template partials. Each partial re-uses the existing `*_section` templates wherever possible.
|
||||
- Scope chip = one boolean URL param + a `IsCurrent(item, rollup, now)` predicate.
|
||||
- Pin toggle = a POST handler that flips `Pinned` and re-renders. The detail page already has the field; surface it on the tile.
|
||||
|
||||
### Candidate B — Project Rows + signal columns
|
||||
|
||||
**Sketch (desktop, full-width table):**
|
||||
|
||||
```
|
||||
┌─ Dashboard ──────────────────────────────────────────────────────────────┐
|
||||
│ [Compact] [Detailed] [Kanban] · tag▾ mgmt▾ has▾ · ◇ pinned + active │
|
||||
├──────────────────────────────────────────────────────────────────────────┤
|
||||
│ Project Path Open Over Issues Last Status │
|
||||
│ ─────────────────────────────────────────────────────────────────────────│
|
||||
│ ★ paliad work.paliad 4 2 3 2d ago live │
|
||||
│ ★ projax dev.projax 7 0 1 now dev │
|
||||
│ ★ youpcorg dev.youpcorg 1 0 2 4d ago live │
|
||||
│ ★ otto dev.otto 9 3 5 3h ago dev │
|
||||
│ mai dev.mai 5 1 8 6h ago dev │
|
||||
│ flexsiebels dev.flex… 2 0 1 1d ago live │
|
||||
│ fdbck dev.fdbck 0 0 0 12d ago live │
|
||||
│ ─────────────────────────────────────────────────────────────────────────│
|
||||
│ Quiet (12) ▾ Archived (3) ▾ │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Per-row signals**: pin star, title, primary path, open-task count, overdue count, open-issue count, last-activity relative, status pill. Each column sortable (column-header click flips sort, persisted in URL).
|
||||
|
||||
**View switcher**:
|
||||
- **Compact** (default) — the table above.
|
||||
- **Detailed** — the current 5-card layout, preserved verbatim. "Take me back to the old dashboard."
|
||||
- **Kanban-by-status** — three columns: `Active`, `Stale`, `At risk` (overdue or open-issue-heavy). Cards in each column = same per-row signals condensed.
|
||||
|
||||
**Filter integration**: identical to Candidate A — chips narrow the project set across all three views.
|
||||
|
||||
**Tradeoffs**:
|
||||
- ✅ Highest information density. m sees ~25 projects without scrolling on a wide screen.
|
||||
- ✅ Sortable columns are a "spreadsheet" superpower — *"show me everything ordered by overdue count desc"* is one click.
|
||||
- ✅ Includes a "back to old layout" via the Detailed view — zero regret for muscle memory.
|
||||
- ✅ Same per-project rollup work as Candidate A, so impl-cost largely overlaps.
|
||||
- ❌ Tables feel less like a "dashboard" and more like a report. The visual texture is monotone — no big numbers, no color, no story per row. Less daily-driver, more weekly review.
|
||||
- ❌ Mobile is uglier — a 6-column table degrades into a tall card list, losing the table's compactness advantage.
|
||||
- ❌ Kanban view as proposed is shallow — three status columns is just a filter dimension dressed up. Not a real flow.
|
||||
|
||||
**Implementation cost**: moderate, mostly overlapping Candidate A. Sortable headers add ~50 lines of JS (or server-side via URL `?sort=col,dir`).
|
||||
|
||||
### Candidate C — Workspace-style 3-pane
|
||||
|
||||
**Sketch (desktop, fixed 3-column layout):**
|
||||
|
||||
```
|
||||
┌─ Dashboard · tag▾ mgmt▾ has▾ · ↻ updated 2m ago ────────────────────────┐
|
||||
│ ┌─ FOCUS ──────────┐ ┌─ TODAY ────────────┐ ┌─ ACTIVITY ───────────────┐│
|
||||
│ │ ★ paliad │ │ Overdue (2) │ │ otto · i64 deployed 3h ││
|
||||
│ │ 4 · 2! 2d │ │ • paliad/call 1d │ │ projax · 5h commit 4h ││
|
||||
│ │ ★ projax │ │ • otto/i65 2d │ │ mai · #218 closed 6h ││
|
||||
│ │ 7 · 0 now │ │ Today (5) │ │ youpcorg · scrape 1d ││
|
||||
│ │ ★ youpcorg │ │ • paliad/spec │ │ flex · #45 merged 1d ││
|
||||
│ │ 1 · 0 4d │ │ • projax/dash │ │ paliad · push 2d ││
|
||||
│ │ ★ otto │ │ ⋮ │ │ ⋮ ││
|
||||
│ │ 9 · 3! 3h │ │ Events (3) │ │ ││
|
||||
│ │ ───────── │ │ • 14:00 paliad-call│ │ ││
|
||||
│ │ Pinned (4) │ │ • 16:30 sport │ │ ││
|
||||
│ │ + Pin a project │ │ • Tue 09:00 standup│ │ ││
|
||||
│ └──────────────────┘ └────────────────────┘ └─────────────────────────┘│
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Three fixed panes** (each independently scrollable):
|
||||
- **FOCUS** (left, ~25%) — pinned-only project tiles. Vertical strip. Star-toggle to pin/unpin. The "what matters this week" surface. Empty state has a "+ Pin a project" affordance.
|
||||
- **TODAY** (middle, ~40%) — the existing Open-tasks + Events cards condensed, no per-project cards. Overdue → Today → Tomorrow → Events for today/tomorrow.
|
||||
- **ACTIVITY** (right, ~35%) — chronological feed: recent commits + issue updates + completed VTODOs + dated docs, newest first. The "what happened recently" view.
|
||||
|
||||
**View switcher**: none. The layout is the layout. Mobile collapses to a vertical stack (FOCUS → TODAY → ACTIVITY).
|
||||
|
||||
**Filter integration**: chips narrow all three panes simultaneously (filter to `tag=work` and FOCUS shows only pinned work-projects, TODAY shows only work-tagged tasks, ACTIVITY shows only work-tagged events).
|
||||
|
||||
**Tradeoffs**:
|
||||
- ✅ Strongest "workspace" feel — closer to Linear / Notion than to a stream reader. Most "dashboard-y".
|
||||
- ✅ FOCUS pane is the most direct expression of "show me my current projects" and forces pinning to do real work.
|
||||
- ✅ No view-switcher means no decision fatigue — open it, see everything that matters in three panes.
|
||||
- ❌ No view-switcher also means **no answer to "I want a different view"** — directly contradicts m's "all kinds of views" ask.
|
||||
- ❌ Three-pane on desktop only. Mobile becomes a vertical scroll that's not meaningfully different from today's stack. The whole shape is desktop-first.
|
||||
- ❌ Requires pinning to be useful, but pinning is currently unused. Bootstrap problem — m has to invest before he sees value.
|
||||
- ❌ Highest implementation cost: three new layouts (pane shells + responsive collapse + per-pane templates) and the brand-new Activity feed.
|
||||
|
||||
**Implementation cost**: large. Three new template surfaces, the Activity aggregator is new code, and the responsive mobile collapse needs care.
|
||||
|
||||
---
|
||||
|
||||
## §4 — Recommendation: **Candidate A (Project Tiles + view switcher)**
|
||||
|
||||
A best matches m's three signals in order:
|
||||
|
||||
1. *"Helpful overview over my current projects"* → the Tiles grid is literally that. One tile per project, scannable, the most-relevant next thing surfaced per tile.
|
||||
2. *"All kinds of views"* → the view switcher gives 3-4 different shapes (Tiles / Tasks / Events / Activity), all sharing the same filter + scope, all bookmarkable. C has no switcher and B's "Detailed" view is just nostalgia for today's layout.
|
||||
3. *"Continue to use unified filters"* → same chip strip as today, extended with a `scope=current|all` chip that's the only addition. No filter vocabulary changes.
|
||||
|
||||
B is the runner-up, and a strong runner-up — its sortable table is genuinely useful for weekly-review, and its "Detailed" tab is a comfortable escape hatch. But the *first impression* of a table-of-projects on the daily-driver surface is "report," not "dashboard," and m opened the request with the word *helpful*. Tiles feel more helpful at a glance.
|
||||
|
||||
C has the best architectural shape (no mode-switching, everything visible) but flunks the "all kinds of views" ask outright, and depends on pinning that m hasn't adopted. The FOCUS pane idea is good enough that it shows up inside Candidate A's Tiles view as the "pinned-first sort" — we keep the strongest signal from C without paying its cost.
|
||||
|
||||
**Recommended scope-cut for v1** (if cost is a worry): ship Tiles + Tasks + Events tabs only. Defer Activity to a follow-up phase. That's 3 tabs, the first of which is new and the other two are partial reuses of today's templates.
|
||||
|
||||
---
|
||||
|
||||
## §5 — Implementation plan (if greenlit)
|
||||
|
||||
### Files touched
|
||||
|
||||
- `web/dashboard.go`
|
||||
- Add `dashboardProject` rollup struct: `{Item, OpenTasks, Overdue, OpenIssues, LastActivity, NextSignal, IsLive}`.
|
||||
- Add `collectProjectRollups(items, todos, events, issues, repos)` — groups the existing rows by `Item.ID`, computes `LastActivity = max(repo.updated_at, last_modified_vtodo, latest_dated_link, last_event)`, picks `NextSignal` (soonest-due VTODO summary, else latest issue title, else latest commit summary).
|
||||
- Add `IsCurrent(rollup, now)` predicate: pinned OR (open tasks > 0) OR (open issues > 0) OR (LastActivity within 14d) OR (any overdue VTODO).
|
||||
- Extend `handleDashboard` to read `?view=` (tiles | tasks | events | activity, default tiles) and `?scope=` (current | all, default current). Both elide from URL when default.
|
||||
- Cache key extends to include `view` + `scope` so `/dashboard?view=tasks` is a separate cache entry — keeps the 60s TTL meaningful per shape.
|
||||
- `web/templates/dashboard.tmpl` — top tab strip + scope chip + per-view partial dispatch.
|
||||
- `web/templates/dashboard_tiles.tmpl` (new) — tile grid + "Quiet (N) ▾" collapsible footer.
|
||||
- `web/templates/dashboard_section.tmpl` — retained as the `tasks` view's partial (the existing 5-card layout, minus Stale which moves under Quiet).
|
||||
- `web/templates/dashboard_events.tmpl` (new, lightweight) — the existing Events card surface promoted to a full-tab view with bigger day headers.
|
||||
- `web/static/style.css` — `.dash-tiles` grid (CSS grid, auto-fill, `minmax(280px, 1fr)`), `.tile` shell, `.tile-counts`, `.tile-signal`, `.tile-stamp`. Mobile breakpoint stacks tiles 1-column ≤ 600px, 2-col 600–900px, 3-col ≥ 900px.
|
||||
- `web/dashboard_pin.go` (new) — `handleDashboardPin` POSTs `?id=<uuid>&pin=true|false`, calls `Store.SetPin([id], bool)`, invalidates dashboard cache, re-renders.
|
||||
- `store/store.go` — add `SetPin(ctx, ids []string, pinned bool) error` if not already there (mirrors `SetPublic`).
|
||||
|
||||
Activity view (if v1): `web/dashboard_activity.go` — merges recent gitea commits (new: lift commit lister out of detail page), recent closed issues, recent completed VTODOs, recent dated docs. Sort by event_time desc. Cap 50. Cache piggybacks on dashboard's 60s.
|
||||
|
||||
### Commit slicing
|
||||
|
||||
1. **Rollup + tile data model**, no UI change. Adds `dashboardProject` + `collectProjectRollups` + tests against a fixture set of items+todos+issues. `view=tiles` not wired yet.
|
||||
2. **Tiles tab + view switcher chrome**. `?view=tiles` (default), `?view=tasks` falls through to today's layout. Tab strip in `dashboard.tmpl`. CSS for tiles.
|
||||
3. **Scope chip** (`?scope=current|all`) + `IsCurrent` predicate + "Quiet (N) ▾" expandable footer that lists collapsed projects.
|
||||
4. **Pin toggle** on tile + `handleDashboardPin` + cache invalidation.
|
||||
5. **Events tab** as its own URL — `?view=events` renders the events partial standalone.
|
||||
6. (Optional, v2-mergeable) **Activity tab**: aggregator extension + template + tab entry.
|
||||
7. **Mobile polish**: media-query breakpoints for tile grid, touch-friendly pin star, drawer fits.
|
||||
8. **Design.md addendum**: a new "Dashboard overhaul (Phase 5h)" section documenting the final shape, the URL contract, and the rollup definition.
|
||||
|
||||
Each commit on this branch is independently revertable. The Tasks tab (today's layout) survives the whole sequence — if m hates Tiles, switching to Tasks gets him back his familiar dashboard with zero data loss.
|
||||
|
||||
### Test coverage
|
||||
|
||||
- `dashboard_rollup_test.go` — fixture-driven: feed in items + synthetic todos/issues, assert `dashboardProject` fields. Cover edge cases: project with 0 links, project with multiple linked repos, overdue counting.
|
||||
- `dashboard_view_test.go` — request `/dashboard?view=tiles`, `/dashboard?view=tasks`, `/dashboard?view=events`; assert the right template selected + the expected sections present.
|
||||
- `dashboard_scope_test.go` — assert `scope=current` filters out the right items; `scope=all` includes them.
|
||||
- `dashboard_pin_test.go` — POST flip → SetPin called → cache invalidated → re-render shows star state.
|
||||
- Existing `dashboard_test.go`, `dashboard_events_test.go`, `dashboard_edit_test.go` keep passing unchanged — the Tasks tab is the same surface.
|
||||
|
||||
### Deploy strategy
|
||||
|
||||
Standard projax flow: push to `mai/fuller/phase-5h-*` worktree-branch as commits land, head merges to `main` per slice, Gitea webhook → Dockerfile → `https://projax.msbls.de/healthz` SHA check. The view-switcher default of `tiles` means the first deploy that lands flips m's daily surface — but the Tasks tab is one click away, so m has a fallback the moment he wants it.
|
||||
|
||||
### Rollback if it doesn't feel right
|
||||
|
||||
Three safety levers, in order of cheapness:
|
||||
1. **Change the default tab**: flip `view` default from `tiles` to `tasks` in one line of `handleDashboard`. m sees today's dashboard, the Tiles view still exists for opt-in.
|
||||
2. **Hide the tab strip**: comment out the tab-strip template block. View URL params still work but no UI to switch.
|
||||
3. **Revert the merge commits**: each phase is a clean merge, so `git revert -m 1 <sha>` per slice cleanly unwinds.
|
||||
|
||||
A week of dogfood after Phase 5h ships is the natural soak. If m still likes today's dashboard better after 7 days, lever 1.
|
||||
|
||||
---
|
||||
|
||||
## §6 — Open questions (the 4 worth chip-asking)
|
||||
|
||||
These are decisions with 2-4 mutually-exclusive options. The remaining design choices have sensible defaults and can be settled in implementation chat.
|
||||
|
||||
1. **Default tab on `/dashboard`** — Tiles (new) or Tasks (today's layout preserved)?
|
||||
2. **"Current" definition** — pinned ∪ recently-active (recommended), or activity-only, or pinned-only, or pinned ∪ open-work?
|
||||
3. **Activity tab in v1** — ship it now (4th tab), or defer to a follow-up phase (3 tabs at launch)?
|
||||
4. **Stale card fate** — fold into Quiet section under Tiles, keep as its own card on the Tasks tab, or retire entirely (replaced by the LastActivity stamp on each tile)?
|
||||
|
||||
---
|
||||
|
||||
## §7 — m's decisions (2026-05-26)
|
||||
|
||||
Picks via `AskUserQuestion`, all four matching the inventor's recommendation:
|
||||
|
||||
- **Q1 (Default tab): Tiles.** New project-tile grid is the landing surface. Tasks tab one click away.
|
||||
- **Q2 (Current = ?): Pinned ∪ recently-active ∪ open-work.** Generous union — catches everything plausibly relevant without forcing m to pin first. Quiet long-tail collapses into a "Quiet (N) ▾" fold under the grid.
|
||||
- **Q3 (Activity tab): Defer to v2.** Phase 5h ships 3 tabs (Tiles / Tasks / Events). Activity feed is the most novel piece and can land as a follow-up after Tiles has dogfood.
|
||||
- **Q4 (Stale card): Fold into Quiet under Tiles.** Per-tile `LastActivity` stamp carries the staleness signal; the "consider archiving?" nudge migrates to the Quiet fold's framing. The standalone Stale card retires.
|
||||
|
||||
No inventor↔m disagreements; no reasoning notes needed. All four picks tighten the recommended scope to "Tiles-first, 3 tabs, no separate Stale card" — a smaller phase than the doc's optional-Activity v1 sketch.
|
||||
|
||||
### Scope-locked summary for the coder gate
|
||||
|
||||
If head greenlights the coder shift, the brief is:
|
||||
|
||||
1. Rollup data model + `IsCurrent(rollup, now)` = pinned OR (open tasks > 0) OR (open issues > 0) OR (LastActivity within 14d).
|
||||
2. Tab strip on `/dashboard` with three tabs: **Tiles** (default), **Tasks** (today's 5-card layout minus Stale), **Events** (events card promoted to its own surface).
|
||||
3. Tile grid with per-project signals + pin star + "Quiet (N) ▾" fold containing both `IsCurrent=false` projects AND the Stale candidates.
|
||||
4. URL contract: `?view=tiles|tasks|events` (default `tiles`, elided), `?scope=current|all` (default `current`, elided). Existing chip vocabulary unchanged.
|
||||
5. Cache key extends to `(filter, view, scope)`. Pin toggle invalidates dashboard cache.
|
||||
6. Tests cover rollup math + view routing + scope predicate + pin flip.
|
||||
7. Mobile: 1-col ≤ 600px, 2-col 600–900px, 3-col ≥ 900px tile grid; tab strip stays at top.
|
||||
8. Design.md addendum documenting the final shape lands in the same branch.
|
||||
|
||||
Activity tab + sortable-table view + multi-pane workspace are explicit non-goals for Phase 5h. Revisit after dogfood if Tiles proves the model.
|
||||
Reference in New Issue
Block a user