fix(products-ui): review pass — show-more race, keyboard history access, a11y glyphs/aria-live, label consistency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:46:43 -07:00
parent 87a56a66c4
commit d18a84e135
6 changed files with 125 additions and 34 deletions
+27 -7
View File
@@ -1,7 +1,13 @@
// Products page (SD-0002 §5.2) — the catalog's home: where imports start and history
// lives. SLICE-5: export is disabled (SLICE-6 ships it); the browsable list is #14's.
import { useEffect, useState } from "react";
import { getProductsSummary, listRuns, type ProductsSummary, type RunSummary } from "../../productsApi";
import {
dialectLabel,
getProductsSummary,
listRuns,
type ProductsSummary,
type RunSummary,
} from "../../productsApi";
import { Banner } from "../../ui/kit";
const STATUS_LABELS: Record<string, string> = {
@@ -40,7 +46,13 @@ export default function ProductsPage() {
</Banner>
);
}
if (!summary || !runs) return <p className="note">Loading</p>;
if (!summary || !runs) {
return (
<p className="note" role="status">
Loading
</p>
);
}
const empty = summary.product_count === 0;
return (
@@ -51,9 +63,12 @@ export default function ProductsPage() {
{!empty && <span className="products__count"> · {summary.product_count.toLocaleString()}</span>}
</h1>
<div className="products__actions">
<button type="button" className="btn-secondary" disabled title="Export arrives in a coming release">
Export
</button>
<div className="products__export">
<button type="button" className="btn-secondary" disabled title="Export arrives in a coming release">
Export
</button>
<span className="note">Export arrives in a coming release</span>
</div>
<a className="btn-primary" href="#/products/import">
Import products
</a>
@@ -95,8 +110,13 @@ export default function ProductsPage() {
}}
>
<td>{new Date(r.created_at).toLocaleString()}</td>
<td>{r.file_name}</td>
<td>{r.dialect}</td>
<td>
{/* Anchor = the keyboard/SR path (§6.6); the row onClick stays as a
mouse convenience. Both set the same hash, so the double fire on
an anchor click is idempotent. */}
<a href={`#/products/imports/runs/${r.id}`}>{r.file_name}</a>
</td>
<td>{dialectLabel(r.dialect)}</td>
<td>{r.products_added}</td>
<td>{r.products_updated}</td>
<td>{r.rows_errored}</td>