docs(04): capture phase context
This commit is contained in:
118
.planning/phases/04-ux-improvements/04-CONTEXT.md
Normal file
118
.planning/phases/04-ux-improvements/04-CONTEXT.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Phase 4: UX Improvements - Context
|
||||
|
||||
**Gathered:** 2026-03-24
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Deliver UX features that make the dashboard genuinely usable at scale: bulk dismiss (all + per-group), search and filter across updates, new-update indicators (badge, tab title, toast, highlight), and accessibility fixes (theme toggle, always-visible drag handle). No new database tables — bulk dismiss adds Store methods; search/filter is client-side; indicators use localStorage.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Bulk dismiss (BULK-01, BULK-02)
|
||||
- **D-01:** Add two new Store methods: `AcknowledgeAll() (count int, err error)` and `AcknowledgeByTag(tagID int) (count int, err error)` — consistent with existing `AcknowledgeUpdate(image)` pattern
|
||||
- **D-02:** Two new API endpoints: `POST /api/updates/acknowledge-all` and `POST /api/updates/acknowledge-by-tag` (with `tag_id` in body) — returning the count of dismissed items
|
||||
- **D-03:** UI placement: "Dismiss All" button in the header/stats area; "Dismiss Group" button in each TagSection header next to the existing delete button
|
||||
- **D-04:** Confirmation: modal/dialog confirmation for dismiss-all (high-impact action); inline confirm pattern (matching existing tag delete) for per-group dismiss
|
||||
|
||||
### Search and filter (SRCH-01 through SRCH-04)
|
||||
- **D-05:** Client-side filtering only — all data is already in memory from polling, no new API endpoints needed
|
||||
- **D-06:** Filter bar placed above the sections list, below the stats row
|
||||
- **D-07:** Controls: text search input (filters by image name), status dropdown (all/pending/acknowledged), tag dropdown (all/specific tag/untagged), sort dropdown (date/name/registry)
|
||||
- **D-08:** Filters do not persist across page reloads — reset on each visit (dashboard is a quick-glance tool)
|
||||
|
||||
### New-update indicators (INDIC-01 through INDIC-04)
|
||||
- **D-09:** Pending update badge/counter displayed in the Header component next to the "Diun Dashboard" title — always visible
|
||||
- **D-10:** Browser tab title reflects pending count: `"DiunDash (N)"` when N > 0, `"DiunDash"` when zero
|
||||
- **D-11:** Toast notification when new updates arrive during polling — auto-dismiss after 5 seconds with manual dismiss button; non-stacking (latest update replaces previous toast)
|
||||
- **D-12:** "New since last visit" detection via localStorage timestamp — store `lastVisitTimestamp` on page unload; updates with `received_at` after that timestamp get a visual highlight
|
||||
- **D-13:** Highlight style: subtle left border accent (e.g., `border-l-4 border-amber-500`) on ServiceCard for new-since-last-visit items
|
||||
|
||||
### Accessibility and theme (A11Y-01, A11Y-02)
|
||||
- **D-14:** Light/dark theme toggle placed in the Header bar next to the refresh button — icon button (sun/moon)
|
||||
- **D-15:** Theme preference persisted in localStorage; on first visit, respects `prefers-color-scheme` media query; removes the hardcoded `classList.add('dark')` from `main.tsx`
|
||||
- **D-16:** Drag handle on ServiceCard always visible at reduced opacity (`opacity-40`), full opacity on hover — removes the current `opacity-0 group-hover:opacity-100` pattern
|
||||
|
||||
### Claude's Discretion
|
||||
- Toast component implementation (custom or shadcn/ui Sonner)
|
||||
- Exact filter bar layout and responsive breakpoints
|
||||
- Animation/transition details for theme switching
|
||||
- Whether to show a count in the per-group dismiss button (e.g., "Dismiss 3")
|
||||
- Sort order default (most recent first vs alphabetical)
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Store interface and handler patterns
|
||||
- `pkg/diunwebhook/store.go` -- Store interface (9 methods; new bulk methods extend this)
|
||||
- `pkg/diunwebhook/sqlite_store.go` -- SQLiteStore implementation (pattern for new methods)
|
||||
- `pkg/diunwebhook/postgres_store.go` -- PostgresStore implementation (must also get new methods)
|
||||
- `pkg/diunwebhook/server.go` -- Server struct and handler registration (new endpoints go here)
|
||||
|
||||
### Frontend components affected
|
||||
- `frontend/src/App.tsx` -- Root component (filter state, bulk dismiss wiring, layout changes)
|
||||
- `frontend/src/hooks/useUpdates.ts` -- Polling hook (toast detection, bulk dismiss callbacks, tab title)
|
||||
- `frontend/src/components/Header.tsx` -- Header (badge counter, theme toggle, dismiss-all button)
|
||||
- `frontend/src/components/TagSection.tsx` -- Tag sections (per-group dismiss button)
|
||||
- `frontend/src/components/ServiceCard.tsx` -- Service cards (new-update highlight, drag handle fix)
|
||||
- `frontend/src/main.tsx` -- Entry point (theme initialization logic change)
|
||||
|
||||
### Requirements
|
||||
- `.planning/REQUIREMENTS.md` -- BULK-01, BULK-02, SRCH-01-04, INDIC-01-04, A11Y-01, A11Y-02
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `Button` component (`frontend/src/components/ui/button.tsx`): use for dismiss-all and per-group dismiss buttons
|
||||
- `Badge` component (`frontend/src/components/ui/badge.tsx`): use for pending count badge in header
|
||||
- `cn()` utility (`frontend/src/lib/utils.ts`): conditional class composition for highlight styles
|
||||
- `timeAgo()` utility (`frontend/src/lib/time.ts`): already used in ServiceCard, relevant for toast messages
|
||||
- `AcknowledgeButton` component: existing per-item dismiss pattern to follow for bulk buttons
|
||||
|
||||
### Established Patterns
|
||||
- `useUpdates` hook: centralized data fetching + state management -- extend with bulk dismiss, toast detection, and tab title side effects
|
||||
- Optimistic updates: used for tag assignment -- apply same pattern for bulk dismiss (update UI immediately, fire API call)
|
||||
- Polling at 5s intervals: toast detection can diff previous vs current poll results
|
||||
- Dark mode via Tailwind `class` strategy: theme toggle adds/removes `dark` class on `document.documentElement`
|
||||
- No global state library: filter state lives in `App.tsx` via `useState`, passed as props
|
||||
|
||||
### Integration Points
|
||||
- `cmd/diunwebhook/main.go`: register 2 new routes on the mux
|
||||
- `store.go`: add `AcknowledgeAll` and `AcknowledgeByTag` to Store interface
|
||||
- `sqlite_store.go` + `postgres_store.go`: implement new Store methods in both dialects
|
||||
- `server.go`: add handler methods for bulk acknowledge endpoints
|
||||
- `App.tsx`: add filter state, wire filter bar component, pass bulk dismiss callbacks
|
||||
- `Header.tsx`: add pending count badge, theme toggle button, dismiss-all button
|
||||
- `main.tsx`: replace hardcoded dark mode with localStorage + prefers-color-scheme logic
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
No specific requirements -- open to standard approaches. The existing shadcn/ui + Tailwind dark mode setup provides the foundation for theme toggling.
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None -- discussion stayed within phase scope.
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 04-ux-improvements*
|
||||
*Context gathered: 2026-03-24 via auto mode*
|
||||
Reference in New Issue
Block a user