From 5621140a72731ad1e9c228bb05944700365af372 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Fri, 20 Mar 2026 10:16:24 -0700 Subject: [PATCH] fix: add error feedback and keyboard accessibility to DigestView --- web/src/views/DigestView.vue | 43 +++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/web/src/views/DigestView.vue b/web/src/views/DigestView.vue index 65f3f1f..0c87513 100644 --- a/web/src/views/DigestView.vue +++ b/web/src/views/DigestView.vue @@ -12,6 +12,7 @@ const selectedUrls = ref>>({}) const queueResult = ref>({}) const extracting = ref>({}) const queuing = ref>({}) +const entryError = ref>({}) onMounted(() => store.fetchAll()) @@ -41,11 +42,16 @@ function otherLinks(id: number): DigestLink[] { async function extractLinks(entry: DigestEntry) { extracting.value = { ...extracting.value, [entry.id]: true } - const { data } = await useApiFetch<{ links: DigestLink[] }>( + const { data, error: err } = await useApiFetch<{ links: DigestLink[] }>( `/api/digest-queue/${entry.id}/extract-links`, { method: 'POST' }, ) extracting.value = { ...extracting.value, [entry.id]: false } + if (err) { + entryError.value = { ...entryError.value, [entry.id]: 'Could not extract links — try again' } + return + } + entryError.value = { ...entryError.value, [entry.id]: null } if (!data) return linkResults.value = { ...linkResults.value, [entry.id]: data.links } expandedIds.value = { ...expandedIds.value, [entry.id]: true } @@ -59,7 +65,7 @@ async function queueJobs(entry: DigestEntry) { const urls = [...(selectedUrls.value[entry.id] ?? [])] if (!urls.length) return queuing.value = { ...queuing.value, [entry.id]: true } - const { data } = await useApiFetch<{ queued: number; skipped: number }>( + const { data, error: err } = await useApiFetch<{ queued: number; skipped: number }>( `/api/digest-queue/${entry.id}/queue-jobs`, { method: 'POST', @@ -68,6 +74,11 @@ async function queueJobs(entry: DigestEntry) { }, ) queuing.value = { ...queuing.value, [entry.id]: false } + if (err) { + entryError.value = { ...entryError.value, [entry.id]: 'Could not queue jobs — try again' } + return + } + entryError.value = { ...entryError.value, [entry.id]: null } if (!data) return queueResult.value = { ...queueResult.value, [entry.id]: data } linkResults.value = { ...linkResults.value, [entry.id]: [] } @@ -93,7 +104,16 @@ function formatDate(iso: string) {
-
+
+ +
{{ entryError[entry.id] }}
+
✅ {{ queueResult[entry.id]!.queued }} @@ -236,6 +259,10 @@ function formatDate(iso: string) { cursor: pointer; user-select: none; } +.entry-header:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: -2px; +} .entry-toggle { color: var(--color-text-muted); font-size: 0.9rem; flex-shrink: 0; padding-top: 2px; } .entry-meta { flex: 1; min-width: 0; } @@ -287,6 +314,16 @@ function formatDate(iso: string) { padding: var(--space-2) var(--space-3); } +/* Error message */ +.entry-error { + padding: var(--space-2) var(--space-4); + font-size: 0.8rem; + color: var(--color-error); + background: color-mix(in srgb, var(--color-error) 10%, var(--color-surface-raised)); + border-radius: 6px; + margin: 0 var(--space-4) var(--space-2); +} + /* Status messages */ .entry-status { padding: var(--space-3) var(--space-4) var(--space-4);