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:
@@ -5,3 +5,4 @@
|
||||
@import "./tokens-typography.css";
|
||||
@import "./tokens-spacing.css";
|
||||
@import "./app.css";
|
||||
@import "./products.css";
|
||||
|
||||
@@ -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 12–14 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; }
|
||||
}
|
||||
Reference in New Issue
Block a user