// ----------------------------- // Projektübersicht — Server-Kommunikation (JSON-API) // ----------------------------- // Diese Datei kapselt die HTTP-Kommunikation mit dem Express-Server (siehe // server.js im Projekt-Root) und wird nach js/date-picker.js von beiden // HTML-Einstiegspunkten geladen. Sie stellt baseUrl, blobToDataUrl, // sendWeekToServer und deleteWeekFromServer bereit. Genutzt wird sie von // js/store.js (syncAllToServer, Speichern aller Wochen), js/weeks.js // (Wochen-Löschung) und js/plan/plan-state.js (savePlan schreibt direkt die // aktuelle Woche über sendWeekToServer). Die Daten werden als strukturierte // JSON-Objekte an die SQLite-API des Servers übergeben (keine CSV-Strings // mehr). Die globale Personenliste hat seit der Ablösung von // people/person_roles/person_absences keinen eigenen Schreibpfad mehr // (Projektbeteiligte werden über die Benutzerverwaltung gepflegt, siehe // /admin/users bzw. /users/:id/absences). // baseUrl: dynamische Server-URL, damit die App auch von anderen Geraeten im Netzwerk erreichbar ist. // Fallback auf localhost:3003, wenn die Seite als lokale Datei geladen wurde. // Parameter: keine. Rückgabe: String mit Basis-URL. function baseUrl(){ const origin = window.location.origin; if(typeof origin === 'string' && /^https?:\/\//.test(origin)){ return origin; } return 'http://localhost:3003'; } // blobToDataUrl: liest einen Blob als Data-URL ein (z. B. für Bilddaten) // Parameter: blob (Blob). Rückgabe: Promise, das die Data-URL auflöst. function blobToDataUrl(blob){ return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); } // sendWeekToServer: POSTet ein komplettes Wochen-Objekt (JSON) an // /save-week. Parameter: week (Objekt). Rückgabe: Promise mit // Server-Antwort. `baseUpdatedAt` übermittelt die Revision, auf der die // lokalen Daten beruhen; meldet der Server 409 (conflict), wird ein Error // mit `err.conflict = true` und `err.updatedAt` geworfen, damit der Aufrufer // den Konflikt anzeigen kann. function sendWeekToServer(week){ if(!window.fetch) return Promise.reject(new Error('fetch nicht verfügbar')); return fetch(baseUrl() + '/save-week',{ method:'POST', headers:{'Content-Type':'application/json; charset=utf-8'}, body:JSON.stringify({ week, baseUpdatedAt:(week && week.updatedAt) ? week.updatedAt : undefined }) }).then(r=>{ return r.json().then(json => { if(r.ok && json) return json; if(json && json.conflict){ const err = new Error(json.error || 'Konflikt: Sitzung wurde zwischenzeitlich geändert'); err.conflict = true; err.label = (week && week.label) || ''; err.updatedAt = json.updatedAt; throw err; } const err = new Error((json && json.error) || ('Server HTTP '+r.status)); err.status = r.status; err.label = (week && week.label) || ''; throw err; }); }); } // deleteWeekFromServer: fordert das Löschen einer Woche über POST /delete-week an // Parameter: label (String, das Wochen-Label). Rückgabe: Promise mit Server-Antwort. // Bei einer Fehler-Antwort (z. B. 403 „Keine Berechtigung“) trägt der geworfene // Error zusätzlich `status` (HTTP-Statuscode), damit Aufrufer (js/weeks.js) // gezielt darauf reagieren können. function deleteWeekFromServer(label){ if(!window.fetch) return Promise.reject(new Error('fetch nicht verfügbar')); return fetch(baseUrl() + '/delete-week', { method: 'POST', headers: {'Content-Type':'application/json; charset=utf-8'}, body: JSON.stringify({ label }) }).then(r=>{ if(!r.ok){ return r.json().catch(()=>({})).then(json => { const err = new Error((json && json.error) || ('Server HTTP '+r.status)); err.status = r.status; throw err; }); } return r.json(); }); }