Files
DiunDashboard/.planning/phases/04-ux-improvements/04-CONTEXT.md

6.7 KiB

Phase 4: UX Improvements - Context

Gathered: 2026-03-24 Status: Ready for planning

## 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.

## 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)

<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>

## Specific Ideas

No specific requirements -- open to standard approaches. The existing shadcn/ui + Tailwind dark mode setup provides the foundation for theme toggling.

## Deferred Ideas

None -- discussion stayed within phase scope.


Phase: 04-ux-improvements Context gathered: 2026-03-24 via auto mode