From b766389df09445d8ead7f6a1ef72b9d609fdaf50 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 01:36:00 +0000 Subject: [PATCH] Enhance empty states with actionable guidance * Updated empty states across the application (domain panel, history charts, bulk scans, and API keys) to provide actionable next steps. * Replaced generic "No data yet" messages with helpful calls-to-action (e.g., "Generate your first one above"). * Improved overall user experience by guiding users rather than leaving them at dead ends. Co-authored-by: schmug <38227427+schmug@users.noreply.github.com> --- .Jules/palette.md | 3 +++ src/views/dashboard.ts | 12 ++++++------ test/mcp.test.ts | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index 3265d33..1a6c0b8 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,6 @@ ## 2026-05-16 - Add ARIA live region for dynamic form validation **Learning:** For multi-step wizard forms or dynamic client-side validation, screen readers need explicit context when errors appear. Using a hidden error div without `role="alert"` and `aria-live="polite"` prevents the error from being announced. **Action:** Always link the input field to its error container using `aria-describedby` and dynamically toggle `aria-invalid="true"` on the input when it fails validation. +## 2024-05-22 - Actionable Empty States +**Learning:** Empty states that simply state a lack of data (e.g., "No scan history yet.") are unhelpful. Providing actionable guidance or a direct call-to-action (e.g., "Trigger a scan from the domain overview.") significantly improves the user experience by guiding them on what to do next. +**Action:** Always include actionable guidance or clear calls-to-action in empty states across the application. diff --git a/src/views/dashboard.ts b/src/views/dashboard.ts index 69fa91a..7791149 100644 --- a/src/views/dashboard.ts +++ b/src/views/dashboard.ts @@ -1670,7 +1670,7 @@ export function renderDomainPanel({ // add-domain CTA out of the way when the user is just narrowing a list. tableBody = isFiltered ? `
-

No domains match these filters.

+

No domains match these filters. Try adjusting your search or clearing filters.

Clear filters
` : `
@@ -2290,7 +2290,7 @@ export function renderDomainDetailPage({ const historySection = scanHistory.length > 0 ? `` - : `

No scan history yet.

`; + : `

No scan history yet. Click "Scan Now" above to trigger your first scan.

`; const body = `
${esc(grade)} @@ -2358,7 +2358,7 @@ const GRADE_RANK_FOR_SPARKLINE: Record = { function renderSparkline(entries: HistoryScanEntry[]): string { if (entries.length === 0) { - return `

No scans yet to chart.

`; + return `

No scans yet to chart. Trigger a scan from the domain overview.

`; } const width = 600; const height = 80; @@ -2408,7 +2408,7 @@ function renderDriftCell( function renderDriftTable(entries: HistoryScanEntry[]): string { if (entries.length === 0) { - return `

No scans yet.

`; + return `

No scans yet. Trigger a scan from the domain overview.

`; } // Rows are newest-first. To highlight "changed vs the prior (older) scan", // we compare each row to the NEXT row in the list (which is chronologically @@ -2626,7 +2626,7 @@ ${error ? `
${esc(error)}
` : ""}`;

${ results.results.length === 0 - ? `

No domains parsed from the submission.

` + ? `

No domains parsed from the submission. Please check your input format and try again.

` : ` @@ -2940,7 +2940,7 @@ export function renderApiKeysPage({ let table: string; if (keys.length === 0) { - table = `

No API keys yet.

`; + table = `

No API keys yet. Generate your first one above.

`; } else { const rows = keys .map((k) => { diff --git a/test/mcp.test.ts b/test/mcp.test.ts index 25f6ad7..7f21296 100644 --- a/test/mcp.test.ts +++ b/test/mcp.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { handleMcpRequest, MCP_PROTOCOL_VERSION,