On save/delete success the parent flips loading false a beat before the dialog finishes its fade-out, so the button flashed back to 'Uložit změny'/'Smazat'. Both dialogs now freeze the loading flag (and the label props) while open, so the closing dialog keeps showing 'Ukládám…'/'Pracuji…'. Same derive-state-from-props pattern as the title freeze.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scroll: MUI only locks <body>, but base.css 'html{overflow-x:hidden}' makes <html> the vertical scroller (overflow-x:hidden forces overflow-y:auto), so the page scrolled behind modals. Added a ref-counted useDialogScrollLock that locks <html> (with scrollbar-width padding) + disableScrollLock on the kit dialogs. Labels: Modal now freezes title/subtitle/submitText while closing (derive-state-from-props), so editing then saving no longer briefly flashes the 'create' title/button during the fade-out.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Select value/option restricted to string (MUI sets the literal child value; string|number + onChange:string was a type lie). DataTable sortDirection uses ?? 'asc' so aria-sort is never undefined on the active column. StatusChip sx-merge drops undefined; Pagination clamps page into range; DataTable dev-warns on sortKey without onSort; Alert onClose JSDoc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DateField (committed in 58a3bca) renders MUI X DatePicker, which requires a LocalizationProvider ancestor — without it, it throws at render time (tsc/build don't catch it). Added to MuiProvider.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the useRef-during-render snapshot with React's blessed 'adjust state when a prop changes' pattern (useState + guarded setState during render). Render-pure, same self-contained behavior. Avoids writing to a ref during render.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The message was bound to the caller's selected row, which gets nulled on close at the same instant the Dialog begins its fade-out — so it faded out with empty text. Now the dialog caches the last shown title/message and renders that through the exit transition.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wires the MUI AppShell into the admin route and deletes the legacy chrome (AdminLayout, Sidebar, layout.css). Spinner CSS (.admin-spinner/.admin-loading) lives in base.css and is unaffected. tsc -b clean, build OK, 148 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add src/admin/ui/SidebarNav.tsx: drawer content component using MUI
List/ListItemButton with NavLink polymorphism. Active item uses the
`selected` prop for the white "pill" styling (not NavLink active class).
Renders logo header, permission-filtered nav sections, and user/logout
footer. Not yet wired — next task mounts it in a Drawer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move MenuItem/MenuSection interfaces and the full menuSections array (6 sections,
~30 items with inline SVG icons) verbatim from Sidebar.tsx into src/admin/ui/navData.tsx.
Add exported isItemActive/hasItemPermission pure helpers (folding in the matchAlso
branch that was previously inline in Sidebar's NavLink className callback).
Sidebar updated to import and use these; rendering and active-state behavior unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Define a minimal local interface for colorSchemes light/dark palettes and
assert the imported theme once via `as unknown as ThemeWithColorSchemes`,
replacing the 6 raw accesses that TypeScript rejected under tsc -b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds src/admin/pages/UiKit.tsx and registers a DEV-only <Route path="ui-kit">
sibling to <Route path="login">, outside the AdminLayout shell. The lazy import
uses the ternary `import.meta.env.DEV ? lazy(...) : null` pattern so Vite/Rolldown
evaluates the condition at build time and the UiKit chunk is entirely absent from
production bundles (confirmed: no UiKit-*.js in dist-client/assets after build:client).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wraps AdminApp in MuiProvider (ThemeProvider v7, defaultMode=dark) and
adds MuiColorSchemeSync to mirror ThemeContext's data-theme attribute
into MUI's JS color-scheme state so useColorScheme().mode stays in sync.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds MuiButton, MuiCard, and MuiChip styleOverrides to the theme: pill-shaped
buttons (borderRadius 999) with a red-gradient containedPrimary, 16px soft-
shadow cards, and pill chips. Driven by a new failing test first (TDD).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Install @mui/material@^7, @emotion/react@^11, @emotion/styled@^11,
@mui/x-date-pickers@^8 as production dependencies. Downgrade
@types/react and @types/react-dom from ^19 to ^18.3 to match the
React 18.3.1 runtime. tsc --noEmit and vite build:client both pass
cleanly with no new errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approved design for migrating the admin frontend off ~7,100 lines of hand-written CSS onto MUI, themed to the existing brand, dark/light preserved, modern 'soft-SaaS shell + dense tables' look, rolled out incrementally (foundation-first, always shippable). Verified via 4-way adversarial review (editorial, MUI-docs accuracy, codebase-fit, completeness) with fixes applied. Also gitignores .superpowers/ brainstorm mockups.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Combines the undeployed v1.9.7 (compact 2-up filter bars) with the date-field
unification (all date fields via AdminDatePicker + mode-aware placeholder).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- The project create-modal start/end dates used raw <input type="date">;
converted to <AdminDatePicker> so every date field in the app now goes
through the shared component (react-datepicker on desktop, native on touch).
- AdminDatePicker now defaults its placeholder by mode (date 'dd.mm.rrrr',
month 'mm.rrrr', time 'hh:mm') so empty date fields always show a format
hint; explicit placeholders (e.g. 'Od'/'Do' range filters) still override.
- Dropped WarehouseReceiptForm's ad-hoc 'dd.mm.yyyy' so it inherits the
unified Czech default.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
.admin-search-bar on phones (<=768px) was flex-direction:column with each
control forced full-width (min-width:100% at <=480), so multi-filter bars
(warehouse receipts/issues, sales lists) became a tall stack of huge controls.
Now: the search box spans the row; filter selects/date pickers sit two-per-row;
the reset button spans the row. 44px/16px touch sizing unchanged; applies
consistently to every page using .admin-search-bar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supersedes the undeployed v1.9.5: adds the mobile tab-bar scroll fix and
combobox touch-target fixes on top of the Phase 1+2 responsive rules.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All changes are mobile-only (max-width media queries); desktop unchanged.
Global (systemic):
- forms.css: all .admin-form-row* grids stack to 1 column at <=480px (fixes
detail-page info grids, settings forms, modal forms staying 2-up on phones).
- components.css: .admin-kpi-4/-3 stat grids collapse to 1 column at <=480px.
- tables.css: row-action icons get 44px touch targets at <=768px.
- datepicker.css: constrain the date-picker popper to the viewport at <=480px.
- filemanager.css: toolbar wraps, new-folder input full-width, breadcrumb scrolls.
Area specifics:
- offers.css: Quill picker dropdowns kept on-screen (max-width) at <=480px.
- invoices.css: month-nav wraps + 44px touch targets at <=640px.
- attendance.css: Leaflet map height scales with viewport; clock-card padding
tightened at <=480px.
Audit confirmed all data tables (warehouse incl.) are already wrapped in the
scroll container, so no table overflow/markup changes were needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BulkAttendanceModal, ShiftFormModal, OrderConfirmationModal, DashQuickActions
trip modal, the Attendance leave modal, and DashProfile's 3 modals (edit
profile, 2FA setup, 2FA disable) now use the shared FormModal instead of
hand-rolled overlays, inheriting the canonical open/close animation, blurred
backdrop, ESC-to-close, click-outside, scroll-lock, staggered reveal, mobile
full-screen and reduced-motion. Footers stay pinned via the real FormModal
footer + submitDisabled; multi-step modals (2FA setup, order confirmation)
drive title/footer/close-guards per step. Behaviour preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
submitDisabled greys out the primary (so callers can gate it on validity
instead of a clickable-but-inert button); subtitle renders a muted line under
the title in the header.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>