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:
@@ -62,6 +62,19 @@ export const users = sqliteTable("users", {
|
||||
language: text("language", { enum: ["sq", "en"] })
|
||||
.notNull()
|
||||
.default("sq"),
|
||||
// Preferred UI theme for this user. Persisted like `language` (read on login,
|
||||
// restored from any booth, changed without a token refresh). Dark is the default
|
||||
// (the booth runs in a dark room). Printed tickets are unaffected. See i18n.md.
|
||||
theme: text("theme", { enum: ["dark", "light"] })
|
||||
.notNull()
|
||||
.default("dark"),
|
||||
// Optional operator profile metadata — display name + contact details. All
|
||||
// nullable; only username/password/role are required to create a user. fullName
|
||||
// (when set) is the human label for audit/Z-report display.
|
||||
fullName: text("full_name"),
|
||||
phone: text("phone"),
|
||||
email: text("email"),
|
||||
address: text("address"),
|
||||
createdAt: text("created_at")
|
||||
.notNull()
|
||||
.default(sql`(current_timestamp)`),
|
||||
|
||||
Reference in New Issue
Block a user