feat(shift): current shift in the list + modal actions; full-width layout everywhere

Shift screen:
- The standalone ShiftControl block is gone from /shift. The open/CURRENT shift now
  appears at the TOP of the shift list (CURRENT badge, live figures synthesized from
  the X-report), unified with history. Selecting it shows its live activity log.
- Shift ACTIONS moved into the current shift's detail pane, each opening a MODAL:
  End shift (confirm → signed Z-report result), drawer voucher (Mandat in/out),
  takings-so-far (X-report). When no shift is open, a Start-shift button shows.
- The current shift's log auto-refreshes (5s); a closed shift is bounded by its
  window. /setup/shifts stays read-only history (no manage props). Deleted the now-
  orphaned ShiftControl.tsx.

Layout:
- Every screen is now full-width like /booth — stripped the per-screen
  `mx-auto max-w-*` caps (Logs, Subscriptions, Plans, Tariff, Users, Roles, Setup
  layout, Shifts). The shell <main> already provides padding.

Build+lint 12/12 (i18n parity). Verified a live open shift surfaces as the CURRENT
list entry.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-20 23:44:27 +02:00
parent 1b54775b4d
commit ae736a9e3e
13 changed files with 337 additions and 364 deletions
+10 -13
View File
@@ -24,7 +24,6 @@ import { TariffComposer } from "./TariffComposer.js";
import { TariffLab } from "./TariffLab.js";
import { SubscriptionManager } from "./SubscriptionManager.js";
import { SubscriptionPlansManager } from "./SubscriptionPlansManager.js";
import { ShiftControl } from "./ShiftControl.js";
import { SiteSettings } from "./SiteSettings.js";
import { UsersManager } from "./UsersManager.js";
import { RolesManager } from "./RolesManager.js";
@@ -78,7 +77,7 @@ function SetupLayout() {
const { t } = useTranslation();
const show = (perm: Permission) => can(user, perm);
return (
<div className="mx-auto max-w-4xl">
<div className="">
<nav className="mb-4 flex flex-wrap items-center gap-1 border-b border-term-border">
{show("site:update") && <SetupTab to="/setup" label={t("nav.devices")} exact />}
{show("tariff:read") && <SetupTab to="/setup/tariff" label={t("nav.tariff")} />}
@@ -344,18 +343,16 @@ const shiftRoute = createRoute({
path: "/shift",
component: function ShiftRoute() {
const { user } = rootRoute.useRouteContext();
// Top: the shift CONTROL (open/close, drawer vouchers, X-report). The drawer-voucher
// form is operator-RAISED (shift:create); an admin authorizes with their password.
// Below: the shift LIST + per-shift activity log (scoped server-side by permission).
// The shift hub: list (current/open shift on top + history) + per-shift activity log.
// The CURRENT shift's pane carries the actions (open/close, drawer voucher, takings),
// each opening a modal. `canManage` = shift:create (start/end + raise vouchers); a
// voucher additionally needs an admin's password sign-off server-side.
return (
<div className="mx-auto max-w-6xl px-4 py-4">
<ShiftControl canVoucher={can(user, "shift:create")} />
{can(user, "shift:read") && (
<div className="mt-6">
<ShiftsHistory user={user} />
</div>
)}
</div>
<ShiftsHistory
user={user}
canManage={can(user, "shift:create")}
canVoucher={can(user, "shift:create")}
/>
);
},
});