From 68f35d3e3d49813b6d93123045e0a182e8cd8f36 Mon Sep 17 00:00:00 2001 From: Kenearos Date: Tue, 12 May 2026 18:21:31 +0200 Subject: [PATCH] feat: add dienstplan_vacation key + getVacationMode/setVacationMode + export/import --- storage.js | 63 ++++++++++++++++++++++++++++++++++++++++++--- test-suite.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/storage.js b/storage.js index 3df3678..2a1d334 100644 --- a/storage.js +++ b/storage.js @@ -5,7 +5,8 @@ class DataStorage { constructor() { this.STORAGE_KEY_EMPLOYEES = 'dienstplan_employees'; - this.STORAGE_KEY_DUTIES = 'dienstplan_duties'; + this.STORAGE_KEY_DUTIES = 'dienstplan_duties'; + this.STORAGE_KEY_VACATION = 'dienstplan_vacation'; } /** @@ -260,12 +261,64 @@ class DataStorage { return result; } + /** + * Get vacation mode for an employee in a specific month. + * @param {string} employeeName + * @param {string} yearMonth - format "YYYY-MM" + * @returns {boolean} + */ + getVacationMode(employeeName, yearMonth) { + try { + const raw = localStorage.getItem(this.STORAGE_KEY_VACATION); + if (!raw) return false; + const map = JSON.parse(raw); + if (!map || typeof map !== 'object') return false; + return Boolean(map[employeeName] && map[employeeName][yearMonth]); + } catch (e) { + console.error('Fehler beim Laden des Urlaubsmodus:', e); + return false; + } + } + + /** + * Set vacation mode for an employee in a specific month. + */ + setVacationMode(employeeName, yearMonth, value) { + try { + const raw = localStorage.getItem(this.STORAGE_KEY_VACATION); + const map = raw ? JSON.parse(raw) : {}; + if (!map[employeeName]) map[employeeName] = {}; + map[employeeName][yearMonth] = Boolean(value); + localStorage.setItem(this.STORAGE_KEY_VACATION, JSON.stringify(map)); + } catch (e) { + console.error('Fehler beim Speichern des Urlaubsmodus:', e); + throw e; + } + } + + /** + * Get the full vacation map ({ name: { yearMonth: bool } }). + */ + getAllVacationModes() { + try { + const raw = localStorage.getItem(this.STORAGE_KEY_VACATION); + if (!raw) return {}; + const map = JSON.parse(raw); + if (!map || typeof map !== 'object') return {}; + return map; + } catch (e) { + console.error('Fehler beim Laden des Urlaubsmodus:', e); + return {}; + } + } + /** * Clear all data */ clearAll() { localStorage.removeItem(this.STORAGE_KEY_EMPLOYEES); localStorage.removeItem(this.STORAGE_KEY_DUTIES); + localStorage.removeItem(this.STORAGE_KEY_VACATION); } /** @@ -276,7 +329,8 @@ class DataStorage { try { return JSON.stringify({ employees: this.getEmployees(), - duties: this.getAllDuties() + duties: this.getAllDuties(), + vacation: this.getAllVacationModes() }, null, 2); } catch (e) { console.error('Fehler beim Exportieren der Daten:', e); @@ -296,11 +350,12 @@ class DataStorage { if (data.employees) { this.saveEmployees(data.employees); } - if (data.duties) { this.saveAllDuties(data.duties); } - + if (data.vacation && typeof data.vacation === 'object') { + localStorage.setItem(this.STORAGE_KEY_VACATION, JSON.stringify(data.vacation)); + } return true; } catch (e) { console.error('Import failed:', e); diff --git a/test-suite.js b/test-suite.js index 73a9729..6a04ed0 100644 --- a/test-suite.js +++ b/test-suite.js @@ -393,6 +393,77 @@ runner.test('Storage: Export und Import', (t) => { t.assertEqual(duties.length, 1, 'Sollte 1 Dienst haben'); }); +// ============================================================================ +// Storage - Vacation Mode +// ============================================================================ + +runner.test('Storage: getVacationMode fuer unbekannten MA -> false', (t) => { + const storage = new DataStorage(); + storage.clearAll(); + t.assertFalse(storage.getVacationMode('Niemand', '2025-11'), 'leerer Default false'); +}); + +runner.test('Storage: setVacationMode -> getVacationMode round-trip', (t) => { + const storage = new DataStorage(); + storage.clearAll(); + storage.setVacationMode('Max Mustermann', '2025-11', true); + t.assertTrue(storage.getVacationMode('Max Mustermann', '2025-11'), 'true round-trip'); + t.assertFalse(storage.getVacationMode('Max Mustermann', '2025-12'), 'anderer Monat = false'); + t.assertFalse(storage.getVacationMode('Anna Schmidt', '2025-11'), 'anderer MA = false'); +}); + +runner.test('Storage: setVacationMode kann zurueckgesetzt werden', (t) => { + const storage = new DataStorage(); + storage.clearAll(); + storage.setVacationMode('Max Mustermann', '2025-11', true); + storage.setVacationMode('Max Mustermann', '2025-11', false); + t.assertFalse(storage.getVacationMode('Max Mustermann', '2025-11'), 'wieder false'); +}); + +runner.test('Storage: Export enthaelt dienstplan_vacation', (t) => { + const storage = new DataStorage(); + storage.clearAll(); + storage.addEmployee('Max Mustermann'); + storage.setVacationMode('Max Mustermann', '2025-11', true); + const exported = storage.exportData(); + const parsed = JSON.parse(exported); + t.assertTrue('vacation' in parsed, 'vacation key im Export'); + t.assertEqual(parsed.vacation['Max Mustermann']['2025-11'], true, 'Wert exportiert'); +}); + +runner.test('Storage: Import restauriert vacation', (t) => { + const storage1 = new DataStorage(); + storage1.clearAll(); + storage1.addEmployee('Max Mustermann'); + storage1.setVacationMode('Max Mustermann', '2025-11', true); + const exported = storage1.exportData(); + + const storage2 = new DataStorage(); + storage2.clearAll(); + const ok = storage2.importData(exported); + t.assertTrue(ok, 'Import success'); + t.assertTrue(storage2.getVacationMode('Max Mustermann', '2025-11'), 'vacation restauriert'); +}); + +runner.test('Storage: Import ohne vacation-Feld bleibt fehlerfrei', (t) => { + const storage = new DataStorage(); + storage.clearAll(); + const legacyJson = JSON.stringify({ + employees: ['Max Mustermann'], + duties: {} + }); + const ok = storage.importData(legacyJson); + t.assertTrue(ok, 'Legacy import erfolgreich'); + t.assertFalse(storage.getVacationMode('Max Mustermann', '2025-11'), 'Default false'); +}); + +runner.test('Storage: clearAll entfernt auch vacation', (t) => { + const storage = new DataStorage(); + storage.setVacationMode('Max Mustermann', '2025-11', true); + storage.clearAll(); + t.assertFalse(storage.getVacationMode('Max Mustermann', '2025-11'), 'nach clearAll false'); +}); + // ============================================================================ // Edge Cases & Regression Tests // ============================================================================