feat: tabbed setup, user metadata, light theme, scoped shift history
Consolidate the config screens under a single /setup hub with permission- gated tabs (Devices/Tariff/Subscriptions/Site/Users/Roles/Shifts), collapsing the top nav to Booth·Shift·Setup; old top-level paths redirect. Users: add optional profile metadata (full name, phone, email, address) on create/edit. Theme: a light palette saved to the user's profile (users.theme), toggled in the header beside the language switch and applied on load like the language preference. Both ride on a single additive migration (0008). Shift history: a new GET /api/shifts folds the signed shift_z_report chain into completed shifts, SCOPED server-side — operators see only their own; holders of shift:cash see all with an operator + date-range filter. Surfaced as the Shifts tab; an operator cannot read another operator's takings (param spoofing is ignored). These three features share the router, api client and i18n catalogs, so they land together. Verified live: theme persists across reload, metadata round- trips to the DB, and shift scoping holds (operator self-only, admin all+filter). Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -355,3 +355,57 @@ body {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
LIGHT THEME
|
||||
The booth defaults to dark (dark room), but a user may prefer light; the
|
||||
choice is saved to their profile (users.theme) and applied on <html> as
|
||||
`.theme-light`. Every screen reads colour through the --color-term-* tokens,
|
||||
so re-pointing them here re-skins the whole app. The TRM "paper/ink" scale
|
||||
supplies the surfaces; accents are tuned a shade darker for contrast on white.
|
||||
A few component-layer values are literal hex (input borders, the inset
|
||||
"recessed" shadow, primary-button text) — those are overridden too so fields
|
||||
and buttons keep their affordance on a light background.
|
||||
============================================================ */
|
||||
html.theme-light {
|
||||
/* Surfaces — TRM paper scale (light → slightly darker for layering). */
|
||||
--color-term-bg: #fafaf7; /* paper */
|
||||
--color-term-panel: #f2f2ee; /* paper-2 */
|
||||
--color-term-panel-2: #e8e8e2; /* paper-3 */
|
||||
--color-term-border: #d2d2c8;
|
||||
--color-term-muted: #5a5a53; /* ink-3 — readable secondary text */
|
||||
--color-term-text: #14171c; /* near-black ink */
|
||||
|
||||
/* Accents — a step darker than the dark-theme values for white-bg contrast. */
|
||||
--color-term-amber: #b8740a;
|
||||
--color-term-green: #1f6a36;
|
||||
--color-term-red: #c8331f;
|
||||
--color-term-cyan: #1a4fa8;
|
||||
}
|
||||
|
||||
/* Component-layer literals that must flip for light (the rest read tokens). */
|
||||
html.theme-light .input,
|
||||
html.theme-light .select,
|
||||
html.theme-light .textarea {
|
||||
border-color: #c2c2b8;
|
||||
box-shadow: inset 0 1px 2px rgba(20, 23, 28, 0.08);
|
||||
}
|
||||
html.theme-light .input:hover,
|
||||
html.theme-light .select:hover,
|
||||
html.theme-light .textarea:hover {
|
||||
border-color: #a8a89e;
|
||||
}
|
||||
html.theme-light .input:focus,
|
||||
html.theme-light .select:focus,
|
||||
html.theme-light .textarea:focus {
|
||||
box-shadow: inset 0 1px 2px rgba(20, 23, 28, 0.08), 0 0 0 1px var(--color-term-amber);
|
||||
}
|
||||
html.theme-light .btn {
|
||||
border-color: #c2c2b8;
|
||||
}
|
||||
html.theme-light .btn:hover:not(:disabled) {
|
||||
background: #dcdcd4;
|
||||
border-color: #a8a89e;
|
||||
}
|
||||
/* Filled buttons keep light text; primary uses dark-on-amber, kept legible. */
|
||||
html.theme-light .btn-primary {
|
||||
color: #fafaf7;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user