- parseInt(month/year) now passes radix 10 consistently.
- minGap eligibility guards null/undefined (c.minGapDays != null && > 0).
- The corrupt-localStorage load and the createEmployee catch now console.warn
instead of swallowing the error silently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- saveState is now debounced (~400ms) to coalesce rapid changes; a pending
write is flushed on beforeunload and on visibilitychange→hidden so the last
change is never lost.
- The SheetJS <script> gets defer so it no longer blocks initial parse; the
typeof XLSX guard already covers any not-yet-available case.
Verified headless: app loads without JS/CSP errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
saveState now logs a console.warn on failure and alerts the user once per
session (storage full/unavailable) so they can export their data and avoid
silent data loss.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- loadFromJSON and the localStorage startup load now run employees through
normalizeEmployees(): fills missing constraints, drops unusable rows,
de-duplicates ids, and discards invalid vacation ranges — preventing
later crashes (e.g. c.neverDays on undefined) and same-day double-books.
- Excel import is wrapped in try/catch with a typeof XLSX guard and user
feedback; the Excel export button gets the same guard.
- createEmployee reuses the shared defaultConstraints() factory.
Adds normalizeEmployees tests (defaults, dropping, dedup, vacations) and a
loadFromJSON normalization test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>