Commit Graph

40 Commits

Author SHA1 Message Date
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
87dbde5c59 fix: remove as-any casts, type Dashboard data properly
- Route handlers: add exhaustive return after error checks so TypeScript
  narrows the union and result properties are accessible without casts
- attendance.service: use Prisma.attendanceGetPayload for included relations
- projects.service: remove unnecessary cast on orders relation
- Dashboard.tsx: replace Record<string,any> with proper DashData interface

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:20:43 +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
872be42107 feat: Czech public holidays in work fund calculation
- Created czech-holidays.ts with 11 fixed + 2 Easter-based holidays
- Fund now automatically excludes public holidays (no manual records needed)
- covered = worked + vacation + sick (NOT holidays — already in fund)
- Renamed "Odpracováno" to "Pokryto" (worked + leave = what counts)
- Removed dependency on holiday attendance records per employee

Matches PHP CzechHolidays::getMonthlyWorkFund() logic exactly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:37:03 +01:00
BOHA
780a6db001 fix: Odpracováno column shows covered hours (worked + leave + holidays)
Was showing only worked hours, but +/- was calculated against covered.
Now both columns are consistent — Odpracováno includes vacation, sick,
and holiday hours so the numbers make sense with the +/- column.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:21:05 +01:00
BOHA
c3bb0a6782 fix: March card header shows prorated fund (136h/17 dnů) matching the +/- values
Card header, per-user +/-, progress bar, and yearly table all now
use the same prorated fund_to_date for the current month.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:15:53 +01:00
BOHA
03e830f97b fix: monthly cards and table show same +/- using prorated fund for current month
Backend: per-user missing/overtime in current month now calculated
against bizDaysToDate (working days up to today), not full month.

Frontend: monthly card percentage and fulfilled check also use
fund_to_date for current month.

Now both the yearly summary table and the monthly cards agree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:14:28 +01:00
BOHA
9724a7b2e9 fix: separate full month fund from prorated fund
Monthly cards show full month fund (e.g., 168h for 21 days).
Yearly summary table uses fund_to_date (prorated to today for
current month) so the +/- column is accurate mid-month.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:12:13 +01:00
BOHA
0ec2cde5e5 fix: scope template editor — use RichEditor instead of textarea for content 2026-03-24 19:01:30 +01:00
BOHA
19912ecbe6 fix: scope template edit — read scope_template_sections from API response
API returns scope_template_sections (Prisma relation name) but
frontend was reading sections. Now checks both field names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:59:21 +01:00
BOHA
3bc7fb6800 fix: lock heartbeat every 10s, timeout after 30s (was 2min/5min) 2026-03-24 11:24:51 +01:00
BOHA
9e6ce4359a fix: use RichEditor with readOnly prop instead of raw HTML for locked/invalidated offers
RichEditor now supports readOnly prop — hides toolbar and disables
editing via ReactQuill's built-in readOnly. Content renders with
proper Quill CSS (list margins, indentation, fonts) instead of
broken browser defaults from dangerouslySetInnerHTML.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:23:34 +01:00
BOHA
b1aaec4fb6 fix: read-only rich text — add word-break to prevent overflow from nbsp 2026-03-24 11:19:19 +01:00
BOHA
f9cb28afa0 fix: read-only rich text — use plain div instead of admin-form-input
admin-form-input has fixed height (36px) causing overflow. Replaced
with a styled div matching the editor appearance. No new CSS needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:15:51 +01:00
BOHA
5593c2a229 fix: read-only rich text content overflowing container
Added section-content class with proper ul/ol/li/p margins and
overflow:hidden. Browser defaults for lists were causing content
to extend outside the form input box.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:14:24 +01:00
BOHA
f8210d667f fix: locked offers — selects, checkboxes, date pickers, rich editor all read-only
Added isLockedByOther check to:
- All disabled={} on selects (currency, language) and checkboxes (apply_vat)
- All conditional renders that swap date pickers for read-only inputs
- Rich editor conditional that swaps editor for static HTML display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:12:39 +01:00
BOHA
0ad0e88853 feat: pessimistic locking for offer editing
When user A opens an offer, a lock is acquired (locked_by + locked_at).
User B opening the same offer sees a warning banner and the form is
read-only. Lock expires after 5 minutes without heartbeat.

Backend:
- POST /:id/lock — acquire lock (returns 423 if locked by another)
- POST /:id/heartbeat — refresh lock timestamp (every 2 min)
- POST /:id/unlock — release lock
- GET /:id — includes locked_by info
- PUT /:id — auto-releases lock on save

Frontend:
- Acquires lock on page load (edit mode only)
- Sends heartbeat every 2 minutes
- Releases lock on page unmount (navigate away)
- Shows warning banner with locker's name
- All inputs read-only + action buttons hidden when locked

Migration: adds locked_by (INT) and locked_at (DATETIME) to quotations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:08:41 +01:00
BOHA
96cbaf3315 fix: stack item description fields vertically with flex column 2026-03-24 07:54:10 +01:00
BOHA
a866384f08 feat: add item_description field to offer items editor (matches PHP) 2026-03-24 07:53:08 +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
2b4a98b958 fix: save item position after drag-and-drop reordering 2026-03-23 19:06:21 +01:00
BOHA
bfb3a975ea fix: restrict item drag to parent table bounds 2026-03-23 19:04:55 +01:00
BOHA
3bef879ff9 fix: move useSensors hook to component top level (React hooks rules) 2026-03-23 19:03:38 +01:00
BOHA
185157fe86 feat: offer items drag-and-drop reordering + fix scope template insertion
1. Item reordering: replaced placeholder with @dnd-kit drag-and-drop.
   Each item row has a drag handle for reordering via vertical drag.
   Uses SortableContext with verticalListSortingStrategy.

2. Scope template insertion: fixed template loading to use already-fetched
   data instead of re-fetching from non-existent endpoint. Templates with
   sections are now stored fully and inserted directly on selection.
   Also copies template description to scope_description.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:02:15 +01:00
BOHA
95065f54eb fix: offer scope sections — add blue EN / red CZ language badges on title labels
Matches PHP styling with offers-lang-badge and offers-lang-badge-cz classes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:56:12 +01:00
BOHA
bcad377f92 fix: dashboard — gate all sections by user permissions
API now only returns data sections the user has permission to see:
- my_shift: attendance.record
- attendance: attendance.admin
- offers: offers.view
- projects: projects.view
- invoices: invoices.view
- orders: orders.view
- leave_pending: attendance.approve
- recent_activity: settings.audit

Frontend hides KPI cards, activity feed, and attendance sections
for users without the matching permissions.

Regular employees now only see their shift status, quick actions,
profile, and sessions — not company KPIs or admin data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:29 +01:00
BOHA
c817e004b7 feat: supplier name autocomplete on received invoices
- Added GET /api/admin/received-invoices/suppliers endpoint (distinct names)
- Upload and edit forms use HTML datalist for browser-native autocomplete
- Suggestions loaded once on page mount

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:32:38 +01:00
BOHA
a4c4a377c9 fix: frontend VAT preview — extract VAT from inclusive amount, not add on top 2026-03-23 13:27:50 +01:00
BOHA
fe12fde9db fix: convert dates to yyyy-MM-dd when opening received invoice edit form
ISO datetime strings from API caused "Invalid time value" in date picker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:19:43 +01:00
BOHA
0baa604ade fix: remove duplicate canSecurity declaration in Settings 2026-03-23 11:43:27 +01:00
BOHA
f40f9d2a4b feat: wire up mandatory 2FA toggle in global settings
Connects the existing UI button to GET/POST /api/admin/totp/required
endpoints. Fetches current state on load, toggles on click.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:40:50 +01:00
BOHA
e0fbae1530 fix: received invoices — show skeleton only on initial load, not on sort/filter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:03:22 +01:00
BOHA
635c6fd0ff fix: table sorting blocked by pointerEvents:none during loading
Root cause: useListData set loading=true on every refetch, and all 4
admin list pages (offers, orders, invoices, projects) applied
pointerEvents:'none' while loading — blocking all clicks including
sort column headers.

Fix: removed setLoading(true) from refetch (matching PHP behavior)
and removed pointerEvents from all list page cards. Opacity fade
kept as visual feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:57:17 +01:00
BOHA
47fb4dc8ac feat: integrate ProjectFileManager into project detail page
Replace the file management placeholder with the actual ProjectFileManager
component, providing projectId, projectNumber, hasPermission, and hasNasFolder
props from the existing page state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:22:10 +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