UX overhaul: routing, accessibility, feedback, and polish
Build and push image / build (push) Successful in 50s
Build and push image / build (push) Successful in 50s
Add react-router-dom for URL-based navigation with browser back/forward, deep links, and bookmarks. Replace window.confirm() with styled ConfirmDialog. Add toast notifications and success feedback on consume/audit/gone flows. Add escape-to-close and focus trapping on modals. Add entrance animations for drawers, modals, and toasts. Make grids responsive, add sortable inventory headers, working CSV/JSON export, time-aware greeting, focus-visible outlines, search clear button, and hover chevrons on inventory rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+51
-12
@@ -183,6 +183,21 @@ export function Inventory({
|
||||
color: "var(--ink)",
|
||||
}}
|
||||
/>
|
||||
{search && (
|
||||
<button
|
||||
onClick={() => setSearch("")}
|
||||
style={{
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
padding: 2,
|
||||
display: "inline-flex",
|
||||
color: "var(--ink-3)",
|
||||
}}
|
||||
>
|
||||
<Icon name="close" size={12} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Select
|
||||
@@ -212,7 +227,7 @@ export function Inventory({
|
||||
</Card>
|
||||
|
||||
<Card padded={false}>
|
||||
<HeaderRow />
|
||||
<HeaderRow sortBy={sortBy} onSort={setSortBy} />
|
||||
{sorted.length === 0 && (
|
||||
<div style={{ padding: 60, textAlign: "center", color: "var(--ink-3)" }}>
|
||||
No items match these filters.
|
||||
@@ -278,9 +293,13 @@ function Segmented<T extends string>({
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderRow() {
|
||||
const COL_SORT: (SortKey | null)[] = [null, "name", null, null, "thc", "price", "remaining", "audit", null];
|
||||
const COL_LABELS = ["", "Item", "Brand", "Shop", "THC %", "Price", "Remaining", "Last checked", "Bin"];
|
||||
|
||||
function HeaderRow({ sortBy, onSort }: { sortBy: SortKey; onSort: (k: SortKey) => void }) {
|
||||
return (
|
||||
<div
|
||||
className="inv-header"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: GRID_COLS,
|
||||
@@ -294,15 +313,34 @@ function HeaderRow() {
|
||||
letterSpacing: "0.08em",
|
||||
}}
|
||||
>
|
||||
<div></div>
|
||||
<div>Item</div>
|
||||
<div>Brand</div>
|
||||
<div>Shop</div>
|
||||
<div>THC %</div>
|
||||
<div>Price</div>
|
||||
<div>Remaining</div>
|
||||
<div>Last checked</div>
|
||||
<div>Bin</div>
|
||||
{COL_LABELS.map((label, i) => {
|
||||
const sk = COL_SORT[i];
|
||||
if (!sk) return <div key={i}>{label}</div>;
|
||||
const active = sortBy === sk;
|
||||
return (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => onSort(sk)}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
padding: 0,
|
||||
cursor: "pointer",
|
||||
fontSize: "inherit",
|
||||
textTransform: "inherit",
|
||||
letterSpacing: "inherit",
|
||||
fontWeight: active ? 600 : "inherit",
|
||||
color: active ? "var(--ink)" : "var(--ink-3)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 4,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
{active && <span style={{ fontSize: 9 }}>▼</span>}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -492,8 +530,9 @@ function ItemRow({
|
||||
<span style={{ fontStyle: "italic" }}>never</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: "var(--ink-3)" }}>
|
||||
<div style={{ fontSize: 12, color: "var(--ink-3)", display: "flex", alignItems: "center", gap: 6 }}>
|
||||
{bin ? bin.name : <span style={{ fontStyle: "italic" }}>—</span>}
|
||||
<span className="inv-row-chevron" style={{ color: "var(--ink-3)", marginLeft: "auto", fontSize: 14 }}>›</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user