Commit Graph

33 Commits

Author SHA1 Message Date
BOHA
decadd895e fix(ui): stack detail-page header action buttons one-per-row on mobile
Detail-page headers put their action buttons in a flex row with
flexWrap:"wrap", so on a phone the 2-4 buttons (Potvrzení / Zahájit
realizaci / Smazat, etc.) wrapped into a ragged grid.

New shared `headerActionsSx` (ui/PageHeader): on xs it's a full-width
column (each button its own full-width row); from sm up it's the usual
right-aligned wrapping row — desktop unchanged. A StatusChip kept in the
same group stays its natural size (alignSelf) instead of stretching.

Applied to PageHeader's own actions slot and every detail-page header
action group: Order/Offer/Invoice (both views)/Project + the Warehouse
Receipt/Issue/Item/Inventory detail headers.

Verified in Chrome at 430px on OrderDetail: the group is flexDirection
column with all three buttons at the same x, full-width, stacked. tsc -b
--noEmit, npm run build, vitest 152/152 clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:44:22 +02:00
BOHA
ca092c6166 fix(ui): unify colored-control text — white on all filled, both themes
The text color on filled colored controls was inconsistent: contained
primary resolved to white in both schemes, but error/success/warning/info
used a per-scheme contrastText (#fff light, #1a1a1a dark). So in DARK mode
a primary (red) button showed white text while an error (red) button —
e.g. every ConfirmDialog delete confirm, the contained "Smazat", and the
dashboard quick-action tiles — showed near-black text. "Black text on one
red button, white on another."

Unify on a single rule: every FILLED colored surface gets WHITE text/glyph
in BOTH themes. The palette .main stays bright in dark mode (it drives text,
outlined buttons and row tints), but a bright fill makes white illegible, so
filled surfaces drop to a darker fill in dark mode (new FILLED_DARK_BG, ≈ the
light-scheme shades; all ≥4.5:1 with white):
- theme MuiButton: contained error/success/warning/info -> white text +
  darker dark-mode fill (disabled state preserved).
- theme MuiChip: filled error/success/warning/info -> white label + darker
  dark-mode fill (covers StatusChip + status pills).
- StatCard icon badges: white glyph + darker dark-mode tile.

Plus two consistency cleanups:
- Detail-page "Smazat" unified to variant=outlined color=error (Offer +
  Invoice now match Order/Project/Warehouse; the contained->confirm pattern
  stays: subtle trigger, strong contained confirm dialog).
- "Zrušit filtry" reset buttons -> color=inherit (neutral), matching the
  rest of the secondary-button family (were default outlined-primary).

Neutral (color=inherit) text/outlined buttons were already correct (label =
ambient text color) and are untouched. tsc -b --noEmit, npm run build and
vitest 152/152 all clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:41:50 +02:00
BOHA
d424ef8c15 refactor(css): scope Quill editor into styled() and delete offers.css
Phase 3 of the "fully MUI" cleanup.

- New src/admin/ui/RichText.tsx exports two theme-aware styled() wrappers:
  RichEditorRoot (the .admin-rich-editor Quill overrides) and RichTextView
  (the .admin-rich-text-view read-only output). All colors resolve via
  theme.vars; picker/tooltip shadows use theme.applyStyles for the
  light/dark difference; the active-button tint uses the primary channel.
- Dropped the ~170 dead font/size picker rules from the old stylesheet —
  the editor toolbar exposes neither picker. Kept the .ql-font-* content
  classes (shared by editor + view) so legacy stored HTML still renders.
- RichEditor uses <RichEditorRoot> (min-height still via --re-min-height);
  InvoiceDetail + OrderDetail use <RichTextView> for the sanitized output.
- offers.css deleted; its import removed from AdminApp.

tsc -b --noEmit and npm run build clean. Verified the editor in Chrome
(light + dark): toolbar, editor surface, text, icon strokes and min-height
all correct and theme-reactive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:37:11 +02:00
BOHA
2367e8a0ff feat(ui): mobile card layout for the remaining item tables
OrderConfirmationModal (the order→confirmation editable items dialog) and InvoiceDetail's read-only paid-invoice items table now render stacked label:value cards on phones (< sm) instead of a wide horizontally-scrolling table; desktop keeps the table. Same pattern as the Offer/Invoice editors. Gates: tsc=0, build=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:48:47 +02:00
BOHA
b168c68264 feat(invoice-detail): mobile card layout for the editable line-items grid
Same treatment as OfferDetail: on phones SortableInvoiceRow renders a stacked card (labeled Popis/Množství/Jednotka/Jedn.cena inputs + DPH select when apply_vat, Celkem footer, drag+remove header) and the wrapper renders a card stack instead of the table (DndContext/SortableContext kept). Verified at 430px on invoices/new: no inner table, no page horizontal scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:40:47 +02:00
BOHA
aeec0b703c fix(ui): wrap detail-page header action buttons on mobile
OfferDetail/OrderDetail/InvoiceDetail/ProjectDetail headers each have a custom action-button row that was flexShrink:0, so (like the alert/PageHeader cases) it stayed at content width and its buttons overflowed/clipped on phones. Dropped flexShrink:0 (and added flexWrap on ProjectDetail which lacked it) so the action row conforms to the available width and the buttons wrap onto multiple lines. Verified on OfferDetail at 430px: buttons span two rows, no container overflow, no page scroll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:36:24 +02:00
BOHA
39fe84ce99 feat(mui): consistent staggered page entrance across all 43 route pages
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>
2026-06-07 10:13:06 +02:00
BOHA
5630beee7c feat(mui): migrate Invoice Detail editor onto MUI kit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 08:27:07 +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
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
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
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
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
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
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
BOHA
528e55991b security: fix all Critical and High findings from FLAWS_REPORT audit
- Auth: pessimistic locking on login tokens and refresh token rotation,
  backup code attempt counter, rate limiting verification
- Schema: unique constraints on business numbers, FK relations,
  unsigned/signed alignment, attendance duplicate prevention
- Invoices/PDFs: DOMPurify sanitization, bounded queries in stats
  and alerts, VAT rounding, Puppeteer error handling
- Orders/Offers: transactional parent+child creation, Zod NaN
  refinement, status enums, uniqueness checks
- Projects/Files: path traversal protection, streamed uploads,
  permission guards, query param validation
- Attendance/HR: duplicate checks, ownership validation, GPS
  restrictions, trip distance validation
- Frontend: modal lock reference counting, XSS escaping in print
  HTML, ref mutation fixes, accessibility attributes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 00:58:35 +02:00
BOHA
07cb428287 1.5.2
- 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>
2026-04-23 17:23:10 +02:00
BOHA
e9f07a4a39 fix: invoice edit/list improvements
- Due date uses days selector in edit mode (same as create)
- Overdue invoices fully editable (same as issued)
- Overdue status reversed to issued when due date moved to future
- Invoice list: edit icon for issued/overdue, eye for paid
- Invoice list: PDF opens blob from NAS (removed lang modal)
- NAS cleanup: properly scans directories when cleaning old PDFs
- Fixed syntax error from leftover else block

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:01:43 +02:00
BOHA
3106aaf314 feat: full invoice editing before payment, NAS cleanup on date change
- Invoice edit mode now uses the same form as create mode (all fields editable)
- Bank account pre-selected by matching IBAN/account number
- Invoice number read-only in edit mode
- Paid invoices remain read-only
- NAS: old PDF deleted when invoice date changes to different month
- Buttons: Zobrazit fakturu, Uložit, Smazat + status transitions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:47:46 +02:00
BOHA
8cdf057ab3 feat: CNB exchange rates, multi-currency KPI stats, invoice PDF VAT in CZK
- ČNB exchange rate service with date-specific rates and caching
- Invoice/received invoice stats convert foreign currencies to CZK
- Dashboard revenue converts all currencies to CZK
- Invoice PDF: VAT recap table always in CZK with CNB rate footer
- Inline styles replaced with utility classes (step 4 cleanup)
- Spinner animation exempt from prefers-reduced-motion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:44:53 +01:00
BOHA
e0ea997c24 refactor: split admin.css monolith, standardize CSS architecture
- Split admin.css (3228 lines) into 12 focused files: variables, base,
  forms, buttons, layout, components, tables, skeleton, datepicker,
  filemanager, pagination, responsive
- Extracted shared styles from offers.css and dashboard.css into
  components.css and forms.css (offers-* → admin-* prefix)
- Standardized naming: dash-kpi-* → admin-kpi-*, session-* → dash-session-*,
  rich-editor → admin-rich-editor
- Deleted duplicate offers-tabs (using admin-tabs everywhere)
- Deduplicated DatePicker and FileManager CSS (~360 lines removed)
- Added 16 utility classes to base.css (font sizes, widths, gaps, margins)
- Deleted empty admin.css

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:00:45 +01:00
BOHA
e6198e1b67 fix: file viewers blocked on mobile — open blank window before async fetch
Mobile browsers block window.open() after async operations. Changed all
file viewers to open a blank window synchronously in the click handler,
then set location.href after fetch completes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:47:45 +01:00
BOHA
6b31b2f74b feat: system settings, dynamic logos, template numbering, permission consolidation
- System settings page with tabs: Security, System, Firma
- Configurable attendance rules (break thresholds, rounding) from DB
- Configurable document numbering with template patterns ({YYYY}/{PREFIX}/{NNN})
- Dynamic logo upload (light/dark variants) served from DB instead of static files
- Email settings (SMTP from/name, alert/leave emails) configurable in UI
- Currency and VAT rate lists configurable, used across all modules
- Permissions simplified: offers.settings + settings.roles + settings.security → settings.manage
- Leaflet bundled locally, removed unpkg.com from CSP
- Silent catch blocks fixed with proper logging
- console.log replaced with app.log.info in server.ts
- Schema renamed: company-settings.schema → settings.schema
- App info section: version, Node.js, uptime, memory, DB status, NAS status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:15:47 +01:00
BOHA
baceb88347 feat: NAS storage for invoices/offers, code cleanup, date/time fixes
- 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>
2026-03-26 10:36:39 +01:00
BOHA
106606f3fa fix: code review — XSS, type safety, validation improvements
Critical:
- InvoiceDetail: sanitize notes HTML with DOMPurify
- OrderDetail: use proper DOMPurify import instead of window fallback

Important:
- AttendanceBalances: add fund_to_date to interface, remove as-any casts
- All schemas: replace z.any() with z.preprocess for boolean fields
- Routes: simplify boolean coercion (Zod handles it now)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:13:20 +01:00
BOHA
3c167cf5c4 style: run prettier on entire codebase 2026-03-24 19:59:14 +01:00
BOHA
c4c4433561 feat: editable billing text on invoices
- Added billing_text column to invoices table (VARCHAR 500)
- Prisma migration: 20260323_add_billing_text
- Form field on invoice create page with placeholder
- PDF uses billing_text, falls back to default translation
- Stored on create and editable on draft invoices

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:47:15 +01:00
BOHA
2540efbec2 refactor: merge InvoiceCreate into InvoiceDetail (single page for create + edit)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:34:16 +01:00
BOHA
5285c3c7c9 fix: VAT select in invoices — use admin-form-select instead of admin-form-input 2026-03-23 19:27:46 +01:00
BOHA
93ea9911f8 fix: invoice items table — match offer detail card style
Changed from offers-editor-section + offers-items-table to
admin-card + admin-card-body + admin-table-responsive, matching
the offer detail page structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:22:01 +01:00
BOHA
892d83cd90 feat: add drag-and-drop item reordering to invoice create and edit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:18:01 +01:00
BOHA
4608494a3f initial commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:46:51 +01:00