fix(import): validate/normalize imported data, guard Excel I/O (#3)

- 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>
This commit is contained in:
ferdi2goandClaude Opus 4.8 committed 2026-06-03 12:20:41 +00:00
1 parent e851bb2833
commit 065876ced4
5 files changed
+118 -18

No files matched your search

+22 -7
View File
@@ -69,16 +69,31 @@ export function renderEmployees() {
fileInput.addEventListener('change', async e => {
const file = e.target.files[0];
if (!file) return;
const buf = await file.arrayBuffer();
const names = readEmployeeNames(buf);
for (const name of names) {
if (!state.employees.some(emp => emp.name === name)) {
try { state.employees.push(createEmployee(name)); } catch { /* skip invalid */ }
if (typeof XLSX === 'undefined') {
alert('Excel-Funktion nicht verfügbar (SheetJS konnte nicht geladen werden).');
e.target.value = '';
return;
}
try {
const buf = await file.arrayBuffer();
const names = readEmployeeNames(buf);
if (names.length === 0) {
alert('Keine Namen in der Datei gefunden.');
} else {
let added = 0;
for (const name of names) {
if (!state.employees.some(emp => emp.name === name)) {
try { state.employees.push(createEmployee(name)); added++; } catch { /* skip invalid */ }
}
}
saveState();
refreshList();
if (added === 0) alert('Alle Namen waren bereits vorhanden.');
}
} catch (err) {
alert('Excel-Import fehlgeschlagen: ' + (err?.message || err));
}
e.target.value = '';
saveState();
refreshList();
});
}