Commit Graph

255 Commits

Author SHA1 Message Date
BOHA
094c190ae1 feat(mui): migrate Users (Uživatelé) page onto MUI kit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:42:08 +02:00
BOHA
9aa90ff5f4 feat(mui): showcase Phase 3 kit components in /ui-kit (Select, Tabs, StatusChip, Pagination, sortable DataTable, DateField, Checkbox, Alert)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:26:25 +02:00
BOHA
645d0127ec style(mui): DataTable bold-cell option; restore SPZ/Aktualni-km emphasis + mono Menlo fallback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 20:50:38 +02:00
BOHA
c550763f2a feat(mui): migrate Vozidla (Vehicles) page onto MUI kit
DataTable (dense, mono numerics, inactive dimming, Chip status toggle), Modal add/edit form (Field + TextField + SwitchField), ConfirmDialog delete. All data logic, mutations (invalidate vehicles+trips), validation (SPZ/Nazev), and SPZ-uppercasing preserved verbatim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 20:46:45 +02:00
BOHA
859ae1a458 feat(mui): dev-only /ui-kit showcase proving the theme + data-theme bridge
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>
2026-06-06 19:16:42 +02:00
BOHA
b504d9d0b2 fix(dates): use AdminDatePicker everywhere + mode-aware default placeholder
- 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>
2026-06-06 16:57:52 +02:00
BOHA
80803cc868 refactor(modals): migrate 8 ad-hoc modals to the shared FormModal
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>
2026-06-06 14:53:28 +02:00
BOHA
e0d2fccf50 fix(projects): edit-page responsible picker shows only active users (keeps current assignee)
Mirror the create-modal fix on ProjectDetail: filter the 'Zodpovědná osoba'
select to active users (already scoped to projects.view), but keep the
project's currently-assigned user selectable even if inactive, labelled
'(neaktivní)', so the saved value still shows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 14:24:41 +02:00
BOHA
a1c251650b fix(projects): responsible-person picker shows only active users with projects.view
The create-project modal's 'Zodpovědná osoba' select listed every user
(including inactive ones). Filter the query server-side by projects.view and
hide inactive users client-side.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 14:19:15 +02:00
BOHA
337feb8b3a feat(orders-ui): manual order price line item + PO upload (hybrid submit)
Adds price/quantity fields (→ one line item) and a PDF attachment picker
to the manual-order create modal. Submit now uses apiFetch directly:
FormData (multipart) when a file is attached, JSON otherwise. Navigates
to the new order on success. Removes the old useApiMutation-based
createMutation; deleteMutation is untouched.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 13:58:21 +02:00
BOHA
30dd5a3f3b feat(orders-ui): add manual order create modal (header only, optional project)
Mirrors the Projects.tsx pattern: useQuery-driven customers + next-number
fetches (enabled only when modal is open), OrderCreatePayload type normalises
customer_id "" → null so the server's CreateOrderSchema never receives 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:17:53 +02:00
BOHA
5140d23d73 feat(projects-ui): add project create modal, type badge, empty-state copy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:13:43 +02:00
BOHA
fdf180809d fix(plan): make Plán prací usable on mobile + dev port → 3050
Mobile work-plan (Plán prací) responsive fixes — desktop unchanged:
- Toolbar stacks into clean full-width rows on ≤640px (nav group,
  centered range label, full-width segmented view toggle, full-width
  category button); drops the fixed 230px label min-width and the
  margin-left:auto that made the toolbar ragged on phones.
- Grid scroll container: 100dvh-based max-height, momentum scroll, and
  overscroll containment so a fling does not bounce the whole page.
- ≤480px tightens columns (date col 72px via the colgroup <col>, person
  cols 132px) so ~2 people are comfortably visible at 360–390px.
- Touch (@media hover:none): faint persistent + on empty editable
  cells, since the hover-reveal add hint never fires on a phone.

Dev: `npm run dev` (backend) now listens on 3050 instead of 3000 to
avoid colliding with other local apps; CLAUDE.md dev-port note updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 10:16:00 +02:00
BOHA
1f05ef6a55 fix(dates): use local 'today' for date-input defaults instead of the UTC date
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>
2026-06-06 02:38:58 +02:00
BOHA
9ae49c0d6a refactor: route/schema consistency (parseId, broad invalidation, Czech messages, Zod 4 strictObject)
- OfferDetail: invalidate broad ["offers"] not ["offers","list"] (CLAUDE.md convention)
- trips.ts/sessions.ts: use parseId helper (validated 400) instead of raw parseInt for route ids
- audit-log.ts: z.object().strict() -> z.strictObject() (Zod 4 idiom)
- attendance.ts, orders/offers schemas: English user-facing strings -> Czech
- project-files.ts: unify 'not found' phrasing to the codebase-dominant 'nenalezen' form

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:29:04 +02:00
BOHA
522afefc80 refactor(frontend): fix undefined --danger-color, dedup formatDate & require2FAOptions, drop double plan.css import
- base.css: var(--danger-color) (undefined, no fallback -> inherited color) -> var(--danger)
- attendanceHelpers: re-export formatDate from formatters instead of a byte-identical copy
- dashboard.ts: remove dead duplicate require2FAOptions (canonical copy is in settings.ts)
- settings.ts: remove unused @deprecated systemSettingsOptions alias
- PlanWork: drop redundant per-page plan.css import (loaded globally in AdminApp)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:22:51 +02:00
BOHA
c5f986adc1 fix(audit): correct + unify entity_type label map to match server values
Audit Log + dashboard feed used legacy keys (invoices_invoice, offers_quotation, trips, vehicles) that never matched the server's entity_type (invoice, quotation, trip, vehicle, ...) and omitted all warehouse_* entities, so many audit rows showed raw English. Single source of truth in lib/entityTypeLabels.ts, keyed to the actual EntityType values, with Czech labels for every type the server writes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:19:10 +02:00
BOHA
4a78d31a7f fix(types): resolve all client TypeScript errors (attendance + plan rollback)
- AttendanceAdmin: declare worked_hours_raw on UserTotalData (the hook sets it at runtime; the local interface omitted it)
- usePlanWork.rollbackMutation: type the mutation param as unknown + explicit cast so passing the TanStack mutation result (with the dynamically-stashed _rolled) no longer trips the weak-type check
tsc -p tsconfig.app.json --noEmit is now clean (0 errors, was 9).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 01:20:24 +02:00
BOHA
aef4ab92ec feat(plan): label plan_category audit rows in Czech
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 01:00:30 +02:00
BOHA
b94f4879e3 feat(plan): add Sprava kategorii management modal and button
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:55:20 +02:00
BOHA
dab5ad8aa5 feat(plan): drive category select and view label from DB categories
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:52:07 +02:00
BOHA
8b77a0a905 feat(plan): render category colors from DB via --cat-color
Replace 14 hardcoded per-category CSS rules with a single --cat-color
custom property set inline on each filled cell button, driven by the DB
categories list threaded from PlanWork → PlanGrid → PlanRangeChips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:47:53 +02:00
BOHA
4a9c283789 fix(plan): readable audit descriptions, project labels, dashboard invalidation, view-only & sticky-column UI
Work-plan fixes from this session:
- Czech audit descriptions for plan entries/overrides (was empty '-')
- server-embedded project label on grid cells (was '-' past the 100-project cap)
- dashboard activity + audit-log cache invalidation on plan mutations
- read-only cells: no '+' on empty, keep hover on cells with a record
- view modal: Czech category + formatted dates
- sticky date column made opaque (no bleed-through on horizontal scroll)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:24:33 +02:00
BOHA
2f76fe0bc1 fix(plan): add 'Upravit celý rozsah' button, use class-based action rows 2026-06-05 13:58:07 +02:00
BOHA
9a11a8f001 feat(plan): show project name instead of raw id in cell 2026-06-05 13:51:27 +02:00
BOHA
a0f351b0aa feat(plan): page, sidebar entry, and route registration 2026-06-05 13:17:16 +02:00
BOHA
5233db2002 v1.8.0: warehouse module (16 commits), docházka mzda model, leave_type=holiday removal, api.ts race fix
Highlights:
- Warehouse module: receipts, issues, reservations, inventory, reports, dashboard,
  master data (categories, suppliers, locations), FIFO service, integration tests
- Docházka: mzda PDF counting model (Odpracováno / Vč. svátků / Přesčas / Svátek /
  So/Ne / Noc) with Czech weekday names and decimal hours
- AttendanceAdmin/AttendanceHistory KPI cards unified to mzda formula with
  fund bar colored by delta, badges for Práce/Dov/Nem/Sv/Nep
- Remove leave_type=holiday entirely (auto-computed from Czech public holidays)
- Allow multiple work shifts per day (overlap detection only)
- Pre-flight refresh in api.ts eliminates spurious 401s on fresh page loads
- Prisma: company_settings gets 6 nullable columns for warehouse numbering
  (PRI/VYD/INV prefixes, default patterns); migration seeds defaults
2026-06-03 23:13:10 +02:00
BOHA
f389c4b3cf feat(warehouse): add reservations, inventory, reports, and dashboard pages
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 14:36:31 +02:00
BOHA
fdc7037e60 feat(warehouse): add issue pages 2026-05-29 14:32:11 +02:00
BOHA
b02f6afe93 feat(warehouse): add receipt pages 2026-05-29 14:29:44 +02:00
BOHA
1b2ab7b4eb feat(warehouse): add items list and detail pages
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 14:27:15 +02:00
BOHA
29975ecad9 feat(warehouse): add categories, suppliers, locations pages 2026-05-29 14:23:31 +02:00
BOHA
705f58e3d1 v1.6.9: cleanup offer dead columns, add item template picker to offer form
- Remove dead DB columns: uuid, sync_version from quotations/quotation_items/scope_sections; is_deleted from quotation_items/scope_sections; content_editor_height from scope_sections
- Remove unused scope_title/scope_description from offer form state
- Remove dead CSS: offers-template-menu, offers-draft-indicator, exchange-rate
- Remove dead scope_title translation key from offers PDF
- Fix duplicate Customer interface in OfferDetail.tsx (use shared type)
- Add missing title field to ScopeTemplate interface
- Add item template picker dropdown next to "Přidat položku" button

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:20:22 +02:00
BOHA
0330453ad6 v1.6.8: remove offer exchange rate, add invoice item description, offer filters, table animations
- Remove exchange_rate and exchange_rate_date from quotations (schema, service, PDF, form)
- Add item_description field to invoice_items (schema, migration, service, form, PDF)
- Add offer status/customer/order filters with tab-based UI
- Clean up offer statuses to active/ordered/invalidated
- Allow invalidate action for non-ordered offers (not just expired)
- Add fade animations on offers and invoices table data changes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:58:51 +02:00
BOHA
66b98e2765 v1.6.7: add settings.system permission, move numbering/VAT to company tab, rename security tab to roles
- New settings.system permission with migration (idempotent INSERT)
- requireAnyPermission helper for route guards accepting multiple perms
- Move document numbering + currency/VAT cards from system tab to CompanySettings
- Rename security tab to roles, add canManageSystem alongside canManageCompany
- TOTP required endpoint and system-info now use settings.system
- Roles list now includes user_count
- Sidebar Settings link includes settings.system

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:03:31 +02:00
BOHA
cd6d3daf43 v1.6.5: fix attendance unique constraint, schema sync, seed script
- 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>
2026-05-17 16:21:34 +02:00
BOHA
4cabba395d v1.6.3: fix project file upload, filter responsible user by permission
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 20:26:32 +02:00
BOHA
3bd0d055d9 v1.6.0: fix offer items mobile layout and localStorage draft save/restore
- Fix items table description column width on mobile (was ~82px, now ~260px)
- Activate unused offers-items-table CSS class in OfferDetail form
- Save all form fields to localStorage draft (language, VAT, exchange rate were missing)
- Use DRAFT_KEY constant in loadOfferDraft, add error logging

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 15:23:42 +02:00
BOHA
e96e51598a v1.5.8: fix audit log table layout (Skeleton outside tbody)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 09:08:15 +02:00
BOHA
9abec36f07 v1.5.7: fix Settings system tab crash and OffersTemplates tab gap
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 08:29:10 +02:00
BOHA
ecd8e3679f fix: replace stray role reference in system settings tab with inline placeholder
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 08:04:22 +02:00
BOHA
ba95723b61 v1.5.6: boneyard-js skeleton migration, TanStack Query refactor, rate-limit config
- 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>
2026-04-28 22:35:43 +02:00
BOHA
82919d39f6 fix: remove manual project creation, smart sequence release, received-invoices schema fix
- Remove ProjectCreate page, POST /projects endpoint, and next-number endpoint
- Projects can only be created through orders (shared numbering sequence)
- Remove dead CreateProjectSchema and createProject service function
- Delete 'order' row from number_sequences (unused; code uses 'shared')
- Smart sequence release: decrement last_number only when deleting the highest number
- Fix received-invoices stats referencing non-existent is_deleted and amount_czk columns
- Update deploy instructions in CLAUDE.md (npm install, prisma migrate deploy)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 11:36:08 +02:00
BOHA
3481b97d47 fix: useEffect anti-patterns, attendance permissions, and received-invoices schema mismatch
- Remove ref-mirror useEffect in AuthContext (cachedUserRef already written at mutation sites)
- Replace useEffect slide direction in ReceivedInvoices with render-time computation
- Fix Login.tsx useEffect dependency array (mount-only alert should have [] deps)
- Move "project created" alert to navigation source in ProjectCreate, remove useEffect in ProjectDetail
- Move companySettings defaults into fetch callbacks in InvoiceDetail and OfferDetail
- Replace due_date useEffect with useMemo in InvoiceDetail
- Capture initial snapshots from API data instead of useEffect in InvoiceDetail, OfferDetail, OrderDetail
- Replace localStorage draft useEffect with lazy useState initializer in OfferDetail
- Fix attendance dropdown to filter by attendance.record permission only
- Fix clock-out 404 on update-address (remove departure_time filter for departure action)
- Fix received-invoices stats endpoint referencing non-existent is_deleted and amount_czk columns

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 10:28:15 +02:00
BOHA
d7c7fbad88 fix: security, validation, and data integrity fixes across 53 files
- Auth: HS256 algorithm restriction on JWT verify, timing-safe bcrypt
  for inactive/locked users, locked_until check in loadAuthData, TOTP
  fixes (async bcrypt, BigInt conversion, future-code counter fix)
- Validation: Zod enums for leave_type/status, numeric transforms on
  foreign keys, VAT 0% coercion fix (Number(v)||21 → v!=null checks)
- Permissions: requirePermission on attendance PUT, attendance_users
  and project_logs access checks, trips users filtered by trips.record
- Prisma queries: fixed roles.is:{OR} pattern (doesn't work on to-one
  relations), attendance_users now filters by attendance.record only
- Transactions: wrapped deleteOrder, createOrder, updateUser, deleteUser,
  duplicateOffer, bulkCreateAttendance, createLeave, scope-templates,
  leave-requests, company-settings, profile updates
- Frontend: mountedRef reset in useListData, blob URL cleanup on unmount,
  null checks on date fields, AdminDatePicker min/max for HH:mm
- Security headers: COOP, CORP, CSP frame-ancestors/form-action/base-uri
- Other: exchange-rate cache TTL, invoice-alert midnight comparison fix,
  numbering.service releaseSequence no-op, nas-offers filename sanitize,
  Content-Disposition header injection fix, mojibake Czech strings

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 08:40:38 +02:00
BOHA
7f07032bf2 fix: attendance clock-in silently aborted by broken mountedRef guard
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>
2026-04-27 08:34:01 +02:00
BOHA
d873c96ae3 fix: attendance clock-in hanging after geolocation confirmation
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>
2026-04-27 08:23:12 +02:00
BOHA
c4f6723042 fix: attendance clock-in button stuck when geolocation fails or hangs
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>
2026-04-24 11:40:51 +02:00
BOHA
9e699c4dd4 fix: React hooks rules violation in Login.tsx causing crash on load
useCallback hooks were placed AFTER conditional early returns.
When authLoading toggled from true -> false, the hook count changed
between renders (14 hooks vs 17 hooks), triggering React's
"Rendered more hooks than during the previous render" error.

Moved all useCallback definitions before the conditional returns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 11:31:00 +02:00
BOHA
aa6c1b5094 refactor: fix all Low findings from FLAWS_REPORT audit
- Auth: TOTP params from config, JWT error logging, audit log failure
  logging, replaced_by_hash validation on token rotation
- Invoices: remove dead VAT code, consistent PDF permissions,
  WebP magic-byte detection, deduped exchange-rate fetches
- Orders/Offers: multipart limit from config, use paginated() helper,
  payment method from DB in PDF
- Projects: verify project exists before creating note
- Attendance: action_type enum validation, consistent local-time
  shift_date construction, holiday attendance in work fund,
  trips.view permission on last-km query
- Users: paginated() helper usage, remove duplicate dashboard keys,
  parallel currency conversion, single hashToken implementation
- Frontend: memoized customInput, reliable print onload, modal prop
  standardization (isOpen), ConfirmModal type icons, id===0 key
  fallback, Login useCallback, CompanySettings ConfirmModal,
  Attendance timeout cleanup, Dashboard memoization, beforeunload
  dirty-state warnings on Invoice/Offer/Order detail
- Schema: invoice_alert_log timestamp, config/env comment on
  Date.prototype.toJSON override
- Utils: exchange-rate inflight dedup

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 08:45:37 +02:00