Verified flow: clock in on dashboard -> delete the record in /attendance/admin
-> navigate back: 'Zaznamenat odchod', the Pritomni KPI and the presence card
still showed the pre-delete state until F5. Root cause: every attendance
mutation outside the dashboard invalidated only ["attendance"], never
["dashboard"], so within the dashboard query's 60s staleTime the remount was
served from cache.
- useAttendanceAdmin create/bulk/edit/delete, /attendance punch +
leave-request, AttendanceCreate, LeaveApproval approve/reject now also
invalidate ["dashboard"] (CLAUDE.md rule: mutations invalidate every domain
that embeds their data)
- systemic backstop: dashboardOptions uses refetchOnMount "always" - the
dashboard aggregates attendance/offers/invoices/orders/projects/leave, and
domain pages can't all be expected to invalidate it; returning to the
dashboard must never show pre-mutation data
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two overflow fixes on Attendance.tsx: (1) the leave-request modal's Od/Do date row used a bare gridTemplateColumns '1fr 1fr', whose minmax(auto,1fr) tracks couldn't shrink below the two DatePickers' min-content, overflowing the narrow modal (horizontal scroll, 'Do' clipped) — now minmax(0,1fr). (2) On phones the whole page overflowed: the main two-column grid used { xs: '1fr' }, and the left column's 5-col 'Dnešní dokončené směny' table forced a 477px min-content (the TableContainer's overflow-x:auto never engaged because the grid item had min-width:auto). Now the grid uses minmax(0,1fr) and both columns get minWidth:0, so the table scrolls inside its container and the page fits the viewport. Verified at 390px width via DOM measurement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PageEnter sweep had replaced the left grid column's wrapper with a React Fragment, so its two cards (clock + completed shifts) leaked out as separate grid items and the layout broke (shifts table jumped to the right cell, stat box wrapped to row 2). Wrap the left column back in a <Box> so the 2-col grid has exactly two column children again.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt the PageEnter wrapper as every page's outermost render element and remove the ad-hoc per-page entrance motion.div wrappers. Every page now enters the same way — all top-level sections rise+fade in, staggered — so nothing appears instantly and the motion is identical app-wide. Presentation-only: no data/logic/hooks/invalidate/permissions touched. Embedded sub-tabs (CompanySettings, ReceivedInvoices), Login (auth shell) and the dev UiKit are intentionally excluded. Gates: tsc -b --noEmit=0, build=0, vitest 152/152.
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>
11 form defaults across 6 files used new Date().toISOString().split('T')[0] (the UTC date), which lands on yesterday during the post-midnight Prague window (UTC+1/+2). Added todayLocalStr() (local YYYY-MM-DD) in formatters and replaced them. Future-date expressions (e.g. invoice due_date +14d) left untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Replace hand-coded skeleton CSS/JSX with boneyard-js auto-generated bones
- Remove skeleton.css and @keyframes shimmer from base.css
- Add <Skeleton> wrappers with fixtures to all 25+ page components
- Generate 20 bone captures via boneyard CLI (CDP auth-gated capture)
- Refactor data fetching from useEffect+useState to TanStack Query
- Extract query hooks into src/admin/lib/queries/ and apiAdapter
- Add usePaginatedQuery hook replacing useApiCall/useListData
- Fix parseFloat || 0 anti-pattern in OfferDetail and OffersTemplates inputs
- Fix customer_id mandatory validation on offer creation
- Fix leave-requests comma-separated status filter (Prisma enum in: [])
- Add cross-entity cache invalidation for orders/offers/invoices/projects
- Make rate limits configurable via env vars (RATE_LIMIT_MAX, RATE_LIMIT_REFRESH, etc.)
- Add boneyard.config.json with routes and breakpoints
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mountedRef was initialized to true but never reset on mount. The
cleanup function (useEffect return) set it to false on unmount. In
React 18 Strict Mode, components mount-unmount-remount during dev.
After the first cleanup, mountedRef stayed false forever.
Result: handlePunch set submitting=true, geolocation callbacks fired,
but every callback returned early at `if (!mountedRef.current) return`
before calling submitPunch. No server request, button stuck.
Fix: add `mountedRef.current = true` inside the useEffect body.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
On desktop browsers without GPS hardware, getCurrentPosition with
enableHighAccuracy:true can silently hang after the user grants
permission — neither success nor error callback fires.
Previous safety timeout (12s) only reset the button without sending
the punch request, leaving users stuck. Now:
- enableHighAccuracy: false (faster fallback to IP-based location)
- Browser timeout reduced to 5s
- Safety timeout reduced to 6s and automatically calls submitPunch
without GPS data instead of just showing an error
- Wrapped success callback in try/catch as additional safeguard
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
handlePunch set submitting=true before calling geolocation, but the
error callback never reset it. When geolocation was denied or timed out:
- Error alert showed
- GPS confirm modal opened
- Button stayed disabled showing "Zpracovávám..."
- User thought it was stuck; no server request appeared to happen
Also added a 12s safety timeout fallback because some browsers silently
hang on getCurrentPosition without calling either callback.
Fix: call setSubmitting(false) in the error callback and clear the
safety timeout in both success and error paths.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- feat: order confirmation PDF generation with VAT support
- feat: order confirmation modal with custom item editing
- fix: attendance negative duration clamping and switchProject timing
- fix: Quill editor locked to Tahoma 14px, PDF heading sizes
- fix: invoice/offer PDF font consistency (Tahoma enforcement)
- fix: invoice alert cron improvements
- fix: NAS financials manager edge cases
- refactor: numbering service with unique sequence constraints
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- NAS storage for created invoices (PDF via puppeteer), received invoices,
and offers with auto-save on create/edit
- Deterministic file paths derived from DB fields (no file_path column needed)
- Separate NAS mount points: NAS_FINANCIALS_PATH, NAS_OFFERS_PATH
- Invoice language field (cs/en) stored per invoice, replaces lang modal
- Invoices list filtered by month/year matching KPI card selection
- Centralized date helpers (src/utils/date.ts) replacing all .toISOString()
calls that returned UTC instead of local time
- Attendance project switching uses exact time (not rounded)
- Comment cleanup: removed ~100 unnecessary/Czech comments
- Removed as-any casts in orders and attendance
- Prisma migrations: add invoice language, drop received_invoices BLOB columns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>