feat(products-ui): admin nav + Products page with import history (§5.2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:29:20 -07:00
parent b5dac8886f
commit 26f84cb916
7 changed files with 395 additions and 22 deletions
+1
View File
@@ -5,3 +5,4 @@
@import "./tokens-typography.css";
@import "./tokens-spacing.css";
@import "./app.css";
@import "./products.css";
+217
View File
@@ -0,0 +1,217 @@
/* Products section (SD-0002 §5) — admin nav strip, the catalog's home page, and the
import-flow primitives (dropzone, tiles, difflist — Tasks 1214 consume these).
Same language as app.css: dark ground, glass chrome, hairline borders, small lifts. */
/* Status accents (SD-0002 design bundle): add / update / error. */
:root {
--st-add: #1F8A5B;
--st-update: #B5830F;
--st-error: #C2513E;
}
/* ── admin nav: horizontal strip under the topbar ───────────────────────────── */
.adminnav {
flex: 0 0 auto;
display: flex;
gap: 26px;
padding: 0 36px;
border-bottom: 1px solid var(--border-soft);
background: rgba(9, 12, 34, .35);
}
.adminnav__item {
font-family: var(--wv-font-display);
font-weight: var(--weight-medium);
font-size: 14px;
color: var(--text-on-dark-mute);
text-decoration: none;
padding: 13px 2px 11px;
border-bottom: 2px solid transparent;
transition: color var(--dur-fast) var(--ease);
}
.adminnav__item:hover { color: var(--wv-starlight); }
.adminnav__item--active { color: var(--wv-starlight); border-bottom-color: var(--wv-gold); }
/* ── products page frame ────────────────────────────────────────────────────── */
/* margin-bottom auto pins the page to the top of the centered .screen__main. */
.products { width: 100%; max-width: 880px; margin-bottom: auto; }
.products--narrow { max-width: 560px; }
.products__header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 16px;
margin-bottom: 28px;
}
.products__header h1 {
font-family: var(--wv-font-display);
font-weight: var(--weight-bold);
letter-spacing: var(--tracking-display);
font-size: 28px;
line-height: 1.1;
margin: 0;
}
.products__count { color: var(--text-on-dark-mute); font-weight: var(--weight-medium); }
.products__actions { display: flex; gap: 12px; align-items: center; }
.products .btn-primary { width: auto; text-decoration: none; }
.products .empty { margin: 24px auto 0; }
.products__history { margin-top: 44px; }
.products__history h2 {
font-family: var(--wv-font-display);
font-weight: var(--weight-semibold);
font-size: 17px;
letter-spacing: var(--tracking-display);
margin: 0 0 14px;
}
/* ── secondary button: outline twin of .btn-primary ─────────────────────────── */
.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: .4em;
font-family: var(--wv-font-display);
font-weight: var(--weight-medium);
font-size: 15.5px;
line-height: 1;
padding: .85rem 1.4rem;
border-radius: var(--radius-pill);
border: var(--btn-border-w) solid var(--border-strong);
background: transparent;
color: var(--text-on-dark-soft);
cursor: pointer;
transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
transform var(--dur-fast) var(--ease);
}
.btn-secondary:hover:not(:disabled) { border-color: var(--wv-lilac); color: var(--wv-starlight); transform: var(--lift-1); }
.btn-secondary:disabled { opacity: .45; cursor: not-allowed; }
.btn-secondary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* ── button that reads as a link (inline retry etc.) ────────────────────────── */
.linklike {
background: none;
border: none;
padding: 0;
font: inherit;
color: var(--wv-lilac);
text-decoration: underline;
cursor: pointer;
transition: color var(--dur-fast) var(--ease);
}
.linklike:hover { color: var(--wv-gold); }
/* ── data tables (import history; errortable shares the bones) ──────────────── */
.datatable, .errortable {
width: 100%;
border-collapse: collapse;
font-size: 13.5px;
}
.datatable th, .errortable th {
text-align: left;
font-family: var(--wv-font-display);
font-weight: var(--weight-medium);
font-size: 12px;
letter-spacing: .06em;
text-transform: uppercase;
color: var(--text-on-dark-mute);
padding: 8px 12px;
border-bottom: 1px solid var(--border-card);
}
.datatable td, .errortable td {
padding: 11px 12px;
border-bottom: 1px solid var(--border-soft);
color: var(--text-on-dark-soft);
}
.datatable__rowlink { cursor: pointer; transition: background var(--dur-fast) var(--ease); }
.datatable__rowlink:hover { background: var(--wv-lilac-08); }
.errortable td:last-child { color: var(--st-error); }
/* ── summary tiles (preview, Task 13) ───────────────────────────────────────── */
.tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.tile {
background: var(--surface-raised);
border: 1px solid var(--border-card);
border-radius: var(--radius-panel);
padding: 16px 18px;
display: flex;
flex-direction: column;
gap: 4px;
align-items: flex-start;
font: inherit;
color: inherit;
text-align: left;
cursor: pointer;
transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.tile:hover { background: var(--surface-raised-hi); }
.tile--active { border-color: var(--wv-gold); }
.tile__num {
font-family: var(--wv-font-display);
font-weight: var(--weight-bold);
font-size: 26px;
line-height: 1;
color: var(--text-on-dark-soft);
}
.tile__label { font-size: 12.5px; color: var(--text-on-dark-mute); }
.tile--add .tile__num { color: var(--st-add); }
.tile--update .tile__num { color: var(--st-update); }
.tile--error .tile__num { color: var(--st-error); }
/* ── diff list (preview records, Task 13) ───────────────────────────────────── */
.difflist { list-style: none; margin: 0; padding: 0; }
.difflist > li { padding: 12px 4px; border-bottom: 1px solid var(--border-soft); }
.diffchange {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12.5px;
line-height: 1.6;
color: var(--text-on-dark-soft);
}
.diffchange__glyph--add { color: var(--st-add); }
.diffchange__glyph--del { color: var(--st-error); }
/* ── sticky confirm/cancel footer (preview, Task 13) ────────────────────────── */
.sticky-footer {
position: sticky;
bottom: 0;
display: flex;
gap: 12px;
align-items: center;
padding: 14px 0;
border-top: 1px solid var(--border-soft);
background: var(--glass-sky);
backdrop-filter: blur(var(--glass-blur));
}
/* ── upload dropzone (Task 12) ──────────────────────────────────────────────── */
.dropzone {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
text-align: center;
padding: 48px 24px;
border: 2px dashed var(--border-strong);
border-radius: var(--radius-card);
cursor: pointer;
transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.dropzone:hover { border-color: var(--wv-lilac); background: var(--wv-lilac-08); }
.dropzone--busy { opacity: .55; pointer-events: none; }
.dropzone input[type="file"] {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* ── small screens ──────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
.adminnav { padding: 0 20px; }
.tiles { grid-template-columns: repeat(2, 1fr); }
.products__header { flex-wrap: wrap; }
}