- 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>
85 lines
3.1 KiB
JavaScript
85 lines
3.1 KiB
JavaScript
import { renderEmployees } from './employees.js';
|
|
import { renderCalendar } from './calendar.js';
|
|
import { renderDataTab } from './history.js';
|
|
import { normalizeEmployees } from './data.js';
|
|
|
|
const STORAGE_KEY = 'morning-planner-state';
|
|
|
|
// Shared mutable state — imported by all other modules
|
|
export const state = {
|
|
employees: [],
|
|
calendar: { holidays: [], companyClosures: [] },
|
|
history: [],
|
|
settings: { moderatorsPerDay: 1 }
|
|
};
|
|
|
|
// Load persisted state from localStorage on startup
|
|
try {
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
if (saved) {
|
|
const parsed = JSON.parse(saved);
|
|
if (Array.isArray(parsed.employees)) state.employees = normalizeEmployees(parsed.employees);
|
|
if (parsed.calendar) state.calendar = parsed.calendar;
|
|
if (Array.isArray(parsed.history)) state.history = parsed.history;
|
|
if (parsed.settings && Number.isInteger(parsed.settings.moderatorsPerDay)
|
|
&& parsed.settings.moderatorsPerDay >= 1 && parsed.settings.moderatorsPerDay <= 5) {
|
|
state.settings = { moderatorsPerDay: parsed.settings.moderatorsPerDay };
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Corrupt storage — start fresh, but leave a breadcrumb for debugging
|
|
console.warn('Gespeicherter Zustand konnte nicht gelesen werden, starte leer:', e);
|
|
}
|
|
|
|
// Persist immediately. Used by the debounced saveState and the flush handlers.
|
|
let storageWarned = false;
|
|
let saveTimer = null;
|
|
function writeState() {
|
|
if (saveTimer !== null) { clearTimeout(saveTimer); saveTimer = null; }
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
employees: state.employees,
|
|
calendar: state.calendar,
|
|
history: state.history,
|
|
settings: state.settings
|
|
}));
|
|
} catch (e) {
|
|
// Storage full or unavailable — warn once so the user can react (export data)
|
|
console.warn('Speichern in localStorage fehlgeschlagen:', e);
|
|
if (!storageWarned) {
|
|
storageWarned = true;
|
|
alert('Achtung: Änderungen konnten nicht gespeichert werden ' +
|
|
'(Speicher voll oder nicht verfügbar). Bitte exportiere deine Daten ' +
|
|
'als JSON, um Datenverlust zu vermeiden.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call after any state mutation. Debounced to coalesce rapid changes; a pending
|
|
// write is flushed on page hide/unload so the last change is never lost.
|
|
export function saveState() {
|
|
if (saveTimer !== null) clearTimeout(saveTimer);
|
|
saveTimer = setTimeout(writeState, 400);
|
|
}
|
|
|
|
const flushPending = () => { if (saveTimer !== null) writeState(); };
|
|
window.addEventListener('beforeunload', flushPending);
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.visibilityState === 'hidden') flushPending();
|
|
});
|
|
|
|
// Tab switching
|
|
document.querySelectorAll('.tab-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const tab = btn.dataset.tab;
|
|
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
document.querySelectorAll('.tab-content').forEach(s => s.classList.add('hidden'));
|
|
btn.classList.add('active');
|
|
document.getElementById('tab-' + tab).classList.remove('hidden');
|
|
if (tab === 'planning') renderCalendar();
|
|
if (tab === 'data') renderDataTab();
|
|
});
|
|
});
|
|
|
|
renderEmployees();
|