6.7 KiB
Phase 4: UX Improvements - Context
Gathered: 2026-03-24 Status: Ready for planning
## Phase BoundaryDeliver 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.
## Implementation DecisionsBulk dismiss (BULK-01, BULK-02)
- D-01: Add two new Store methods:
AcknowledgeAll() (count int, err error)andAcknowledgeByTag(tagID int) (count int, err error)— consistent with existingAcknowledgeUpdate(image)pattern - D-02: Two new API endpoints:
POST /api/updates/acknowledge-allandPOST /api/updates/acknowledge-by-tag(withtag_idin 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
lastVisitTimestampon page unload; updates withreceived_atafter 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-schememedia query; removes the hardcodedclassList.add('dark')frommain.tsx - D-16: Drag handle on ServiceCard always visible at reduced opacity (
opacity-40), full opacity on hover — removes the currentopacity-0 group-hover:opacity-100pattern
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)
<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
Buttoncomponent (frontend/src/components/ui/button.tsx): use for dismiss-all and per-group dismiss buttonsBadgecomponent (frontend/src/components/ui/badge.tsx): use for pending count badge in headercn()utility (frontend/src/lib/utils.ts): conditional class composition for highlight stylestimeAgo()utility (frontend/src/lib/time.ts): already used in ServiceCard, relevant for toast messagesAcknowledgeButtoncomponent: existing per-item dismiss pattern to follow for bulk buttons
Established Patterns
useUpdateshook: 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
classstrategy: theme toggle adds/removesdarkclass ondocument.documentElement - No global state library: filter state lives in
App.tsxviauseState, passed as props
Integration Points
cmd/diunwebhook/main.go: register 2 new routes on the muxstore.go: addAcknowledgeAllandAcknowledgeByTagto Store interfacesqlite_store.go+postgres_store.go: implement new Store methods in both dialectsserver.go: add handler methods for bulk acknowledge endpointsApp.tsx: add filter state, wire filter bar component, pass bulk dismiss callbacksHeader.tsx: add pending count badge, theme toggle button, dismiss-all buttonmain.tsx: replace hardcoded dark mode with localStorage + prefers-color-scheme logic
</code_context>
## Specific IdeasNo specific requirements -- open to standard approaches. The existing shadcn/ui + Tailwind dark mode setup provides the foundation for theme toggling.
## Deferred IdeasNone -- discussion stayed within phase scope.
Phase: 04-ux-improvements Context gathered: 2026-03-24 via auto mode