Neues Design „Bauplan“, Dashboard, Notizen-Bereich, Seitenleiste
- Gestaltung „Bauplan“: ruhigere Farben, Schriftskala, Abstände im 4-px-Raster, Kalenderwochen und Nummern in Mono-Ziffern, Glas beruhigt - Dashboard „Meine Aufgaben“: Kopf mit Kalenderwoche, Band „Diese Woche“ (Mo–Fr), je Projekt Meine Rolle / Team / Notizen; offene Termine blau hervorgehoben; Monatskalender unverändert - Neuer Bereich „Notizen“ in der Seitenleiste: Notizen aller eigenen Projekte und eigene Notizen ohne Projekt, später einem Projekt zuordenbar - Seitenleiste neu gegliedert (Projekte, Team, Notizen, Studenten, Einstellungen/Admin unten), Admin-Punkte in Personen/Vorgaben/System - Reiter im Projektkopf wie die Seitenleiste hervorgehoben - Behoben: „&amp;…“ in Kommentaren wuchs bei jedem Speichern; bestehende Kommentare werden beim Start repariert - Behoben: Lösch-Rückfrage bei Notizen unlesbar bzw. blieb stehen, Cursor im Notiztitel verdeckt, Dropdown im Dunkelmodus gekachelt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e697057b86
commit
e291ca8438
18 files changed
+2185
-1250
No files matched your search
@@ -0,0 +1,408 @@
|
||||
// -----------------------------
|
||||
// Seitenleiste „Notizen“ (Tab `notizen`, /Notizen)
|
||||
// -----------------------------
|
||||
// Eriks Wunsch: Jedes Konto sieht hier die Notizen ALLER seiner Projekte und
|
||||
// legt auch projektunabhängige Notizen an, die sich beim Bearbeiten einem
|
||||
// Projekt zuordnen lassen. Gleicher Aufbau wie der Reiter „Notizen“ im
|
||||
// Projektbereich (js/plan/plan-notizen.js: Liste + Editor, Werkzeugleiste,
|
||||
// automatisches Speichern), dazu ein Projektfilter und im Editor die Auswahl
|
||||
// „Projekt“. Eigene Element-ids (mn…), weil der Projekt-Reiter gleichzeitig
|
||||
// im DOM liegt.
|
||||
//
|
||||
// Daten: GET /meine-notizen (Notizen meiner Projekte + meine
|
||||
// projektunabhängigen, project_uid = ''), sonst dieselben Routen
|
||||
// /projekt-notizen* wie der Projekt-Reiter; PUT nimmt `uid` zum Zuordnen.
|
||||
// Projektunabhängige Notizen sieht und ändert nur, wer sie angelegt hat.
|
||||
(function(){
|
||||
const SPEICHER_PAUSE_MS = 1200;
|
||||
const OHNE = '__ohne__';
|
||||
|
||||
let liste = [];
|
||||
let meineProjekte = new Set();
|
||||
let geladen = false;
|
||||
let ladeFehler = '';
|
||||
let filter = ''; // '' = alle, OHNE = ohne Projekt, sonst uid
|
||||
let suche = '';
|
||||
let aktivId = null;
|
||||
let basis = '';
|
||||
let ungespeichert = false;
|
||||
let speicherTimer = null;
|
||||
let speichertGerade = false;
|
||||
let beobachter = null;
|
||||
let loeschenFragt = null;
|
||||
let eigenesSchreiben = 0; // Zeitpunkt der letzten eigenen Änderung (Live-Echo auslassen)
|
||||
|
||||
const el = () => document.getElementById('notizen');
|
||||
const aktiv = () => liste.find(n => n.id === aktivId) || null;
|
||||
const darf = n => !n || !n.uid ? true : (typeof hatRecht === 'function' && hatRecht('proj.plan'));
|
||||
const darfProjekte = () => typeof hatRecht === 'function' && hatRecht('proj.plan');
|
||||
|
||||
// Projekte der aktuellen Sitzung je projectUid (Name und Nummer).
|
||||
function projekte(){
|
||||
const w = typeof currentWeek === 'function' ? currentWeek() : null;
|
||||
const m = new Map();
|
||||
((w && w.projects) || []).forEach(p => { if(p.projectUid) m.set(String(p.projectUid), p); });
|
||||
return m;
|
||||
}
|
||||
function projektName(uid, karte){
|
||||
if(!uid) return 'Ohne Projekt';
|
||||
const p = (karte || projekte()).get(uid);
|
||||
return p ? [p.nr, p.name].filter(Boolean).join(' ') : 'Projekt nicht in der aktuellen Sitzung';
|
||||
}
|
||||
function zeit(iso){
|
||||
if(!iso) return '';
|
||||
const d = new Date(iso);
|
||||
if(isNaN(d)) return '';
|
||||
const heute = new Date();
|
||||
const gestern = new Date(heute); gestern.setDate(heute.getDate() - 1);
|
||||
const uhr = d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
if(d.toDateString() === heute.toDateString()) return 'heute ' + uhr;
|
||||
if(d.toDateString() === gestern.toDateString()) return 'gestern ' + uhr;
|
||||
return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: d.getFullYear() === heute.getFullYear() ? undefined : 'numeric' });
|
||||
}
|
||||
const klartext = html => typeof statusPlainText === 'function' ? statusPlainText(html) : String(html || '').replace(/<[^>]+>/g, ' ');
|
||||
function sortieren(){
|
||||
liste.sort((a, b) => (b.angeheftet - a.angeheftet) || String(b.geaendertAm).localeCompare(String(a.geaendertAm)));
|
||||
}
|
||||
|
||||
// ---------- Laden ----------
|
||||
function laden(){
|
||||
ladeFehler = '';
|
||||
return fetch(baseUrl() + '/meine-notizen')
|
||||
.then(r => r.json().then(d => ({ ok: r.ok, d })))
|
||||
.then(({ ok, d }) => {
|
||||
if(!ok || !d.ok) throw new Error(d.error || 'Laden fehlgeschlagen');
|
||||
liste = d.notizen || [];
|
||||
meineProjekte = new Set(d.meineProjekte || []);
|
||||
sortieren();
|
||||
geladen = true;
|
||||
})
|
||||
.catch(e => { ladeFehler = e.message || 'Die Notizen konnten nicht geladen werden.'; liste = []; geladen = true; });
|
||||
}
|
||||
|
||||
// ---------- Darstellung ----------
|
||||
window.renderMeineNotizen = function(){
|
||||
const box = el();
|
||||
if(!box) return;
|
||||
box.innerHTML = '<div class="notizen-laedt">Notizen werden geladen…</div>';
|
||||
jetztSpeichern();
|
||||
laden().then(aufbauen);
|
||||
};
|
||||
|
||||
function sichtbar(){
|
||||
const s = suche.trim().toLowerCase();
|
||||
return liste.filter(n => (!filter || (filter === OHNE ? !n.uid : n.uid === filter))
|
||||
&& (!s || (n.titel + ' ' + klartext(n.html) + ' ' + projektName(n.uid)).toLowerCase().includes(s)));
|
||||
}
|
||||
|
||||
function filterOptionen(){
|
||||
const karte = projekte();
|
||||
const mitNotizen = new Set(liste.filter(n => n.uid).map(n => n.uid));
|
||||
const uids = Array.from(new Set([...meineProjekte, ...mitNotizen]))
|
||||
.sort((a, b) => projektName(a, karte).localeCompare(projektName(b, karte), 'de'));
|
||||
const zahl = f => liste.filter(n => f === OHNE ? !n.uid : n.uid === f).length;
|
||||
return '<option value="">Alle Projekte (' + liste.length + ')</option>'
|
||||
+ '<option value="' + OHNE + '"' + (filter === OHNE ? ' selected' : '') + '>Ohne Projekt (' + zahl(OHNE) + ')</option>'
|
||||
+ uids.map(u => '<option value="' + esc(u) + '"' + (filter === u ? ' selected' : '') + '>' + esc(projektName(u, karte)) + ' (' + zahl(u) + ')</option>').join('');
|
||||
}
|
||||
|
||||
function aufbauen(){
|
||||
const box = el();
|
||||
if(!box) return;
|
||||
const treffer = sichtbar();
|
||||
if(!treffer.some(n => n.id === aktivId)) aktivId = treffer.length ? treffer[0].id : null;
|
||||
box.innerHTML = `
|
||||
<div class="mn-seite">
|
||||
<div class="mn-kopf">
|
||||
<div class="mn-kopf-links">
|
||||
<span class="mn-kopf-unter">Notizen aller meiner Projekte und eigene Notizen ohne Projekt</span>
|
||||
<h2 class="mn-titel">Notizen</h2>
|
||||
</div>
|
||||
<label class="mn-filter">Projekt
|
||||
<select id="mnFilter" onchange="mnFilter(this.value)">${filterOptionen()}</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="notizen mn-notizen">
|
||||
<aside class="notizen-liste">
|
||||
<div class="notizen-suchzeile">
|
||||
<input type="search" id="mnSuche" class="notizen-suche" placeholder="Notizen durchsuchen…"
|
||||
aria-label="Notizen durchsuchen" value="${esc(suche)}" oninput="mnSuchen(this.value)">
|
||||
<button type="button" class="notizen-neu" onclick="mnNeu()">+ Neu</button>
|
||||
</div>
|
||||
<div id="mnEintraege" class="notizen-eintraege"></div>
|
||||
</aside>
|
||||
<div class="notizen-editor" id="mnEditor"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
listeZeichnen();
|
||||
editorZeichnen();
|
||||
}
|
||||
|
||||
function listeZeichnen(){
|
||||
const box = document.getElementById('mnEintraege');
|
||||
if(!box) return;
|
||||
if(ladeFehler){ box.innerHTML = '<div class="notizen-leer ist-fehler">' + esc(ladeFehler) + '</div>'; return; }
|
||||
const treffer = sichtbar();
|
||||
if(!liste.length){ box.innerHTML = '<div class="notizen-leer">Noch keine Notizen. Mit „+ Neu“ eine eigene anlegen.</div>'; return; }
|
||||
if(!treffer.length){ box.innerHTML = '<div class="notizen-leer">' + (suche ? 'Keine Notiz enthält „' + esc(suche) + '“.' : 'Keine Notizen in dieser Auswahl.') + '</div>'; return; }
|
||||
const karte = projekte();
|
||||
const zeile = n => {
|
||||
const vorschau = klartext(n.html).replace(/\s+/g, ' ').trim().slice(0, 90);
|
||||
return `<button type="button" class="notizen-eintrag${n.id === aktivId ? ' ist-aktiv' : ''}" onclick="mnWaehlen('${esc(n.id)}')">
|
||||
<span class="notizen-eintrag-titel">${esc(n.titel || 'Ohne Titel')}${n.quelle === 'mail' ? ' <span class="notizen-chip">Mail</span>' : ''}</span>
|
||||
<span class="notizen-eintrag-text">${esc(vorschau || 'Kein Text')}</span>
|
||||
<span class="notizen-eintrag-meta"><span class="mn-projekt${n.uid ? '' : ' ist-ohne'}">${esc(projektName(n.uid, karte))}</span> · ${esc(zeit(n.geaendertAm))}</span>
|
||||
</button>`;
|
||||
};
|
||||
const oben = treffer.filter(n => n.angeheftet), rest = treffer.filter(n => !n.angeheftet);
|
||||
box.innerHTML = (oben.length ? '<div class="notizen-gruppe"><span class="notizen-pin-punkt"></span>Angeheftet</div>' + oben.map(zeile).join('') : '')
|
||||
+ (rest.length ? (oben.length ? '<div class="notizen-gruppe">Weitere</div>' : '') + rest.map(zeile).join('') : '');
|
||||
}
|
||||
|
||||
// projektAuswahl: Auswahl im Editor — „Ohne Projekt“, meine Projekte, dann
|
||||
// die übrigen der Sitzung. Ohne Recht proj.plan nur „Ohne Projekt“ (und das
|
||||
// aktuelle Projekt, damit der Stand lesbar bleibt).
|
||||
function projektAuswahl(n){
|
||||
const karte = projekte();
|
||||
const name = u => projektName(u, karte);
|
||||
const sort = arr => arr.sort((a, b) => name(a).localeCompare(name(b), 'de'));
|
||||
const opt = u => '<option value="' + esc(u) + '"' + (n.uid === u ? ' selected' : '') + '>' + esc(name(u)) + '</option>';
|
||||
const meine = sort(Array.from(meineProjekte).filter(u => karte.has(u)));
|
||||
const weitere = sort(Array.from(karte.keys()).filter(u => !meineProjekte.has(u)));
|
||||
const sperren = !darf(n) || (!darfProjekte() && !n.uid);
|
||||
let html = '<option value=""' + (!n.uid ? ' selected' : '') + '>Ohne Projekt</option>';
|
||||
if(darfProjekte()){
|
||||
if(meine.length) html += '<optgroup label="Meine Projekte">' + meine.map(opt).join('') + '</optgroup>';
|
||||
if(weitere.length) html += '<optgroup label="Weitere Projekte">' + weitere.map(opt).join('') + '</optgroup>';
|
||||
}
|
||||
if(n.uid && !karte.has(n.uid)) html += opt(n.uid);
|
||||
return '<label class="mn-zuordnung">Projekt <select id="mnProjekt" onchange="mnZuordnen(this.value)"'
|
||||
+ (sperren ? ' disabled title="' + (darfProjekte() ? '' : 'Zum Zuordnen fehlt das Recht, den Terminplan zu bearbeiten.') + '"' : '') + '>' + html + '</select></label>';
|
||||
}
|
||||
|
||||
function editorZeichnen(){
|
||||
const box = document.getElementById('mnEditor');
|
||||
if(!box) return;
|
||||
if(beobachter){ beobachter.disconnect(); beobachter = null; }
|
||||
const n = aktiv();
|
||||
if(!n){
|
||||
box.innerHTML = `<div class="notizen-editor-leer">
|
||||
<b>${liste.length ? 'Notiz links auswählen' : 'Noch keine Notizen'}</b>
|
||||
<span>Mit „+ Neu“ eine Notiz anlegen. Sie gehört zunächst nur Ihnen und lässt sich oben rechts einem Projekt zuordnen.</span>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
const schreiben = darf(n);
|
||||
basis = n.geaendertAm;
|
||||
ungespeichert = false;
|
||||
const projekt = n.uid ? projekte().get(n.uid) : null;
|
||||
box.innerHTML = `
|
||||
<div class="notizen-editor-kopf">
|
||||
<input type="text" id="mnTitel" class="notizen-titel" value="${esc(n.titel)}" placeholder="Titel der Notiz"
|
||||
aria-label="Titel der Notiz" ${schreiben ? '' : 'readonly'} oninput="mnGeaendert()" onblur="mnJetztSpeichern()">
|
||||
${schreiben ? `<button type="button" class="notizen-symbol${n.angeheftet ? ' ist-an' : ''}" onclick="mnAnheften()"
|
||||
aria-pressed="${n.angeheftet}">${n.angeheftet ? 'Angeheftet' : 'Anheften'}</button>
|
||||
<button type="button" class="notizen-symbol ist-gefahr" id="mnLoeschen" onclick="mnLoeschen()">Löschen</button>` : ''}
|
||||
</div>
|
||||
<div class="mn-editor-zeile">
|
||||
${projektAuswahl(n)}
|
||||
${projekt ? `<a class="mn-projekt-link" href="#" onclick="return mnProjektOeffnen('${esc(projekt.id)}')">Im Projekt öffnen</a>` : ''}
|
||||
${!n.uid ? '<span class="mn-hinweis">Nur für Sie sichtbar, bis sie einem Projekt zugeordnet ist</span>' : ''}
|
||||
</div>
|
||||
<div class="richtext-wrap notizen-richtext">
|
||||
${schreiben ? richtextToolbarHtml('#mnText') : ''}
|
||||
${richtextFeldHtml('notizen-text', sanitizeStatusHtml(n.html || ''), 'Notiz schreiben…', 'id="mnText" onblur="mnJetztSpeichern()"', schreiben)}
|
||||
</div>
|
||||
<div class="notizen-fuss">
|
||||
<span>Angelegt von ${esc(n.erstelltVon)} ${esc(zeit(n.erstelltAm))}${n.geaendertAm !== n.erstelltAm ? ' · zuletzt geändert von ' + esc(n.geaendertVon) + ' ' + esc(zeit(n.geaendertAm)) : ''}</span>
|
||||
<span id="mnStatus" class="notizen-status"></span>
|
||||
</div>`;
|
||||
const text = document.getElementById('mnText');
|
||||
if(text && schreiben){
|
||||
beobachter = new MutationObserver(() => mnGeaendert());
|
||||
beobachter.observe(text, { childList: true, subtree: true, characterData: true, attributes: true });
|
||||
}
|
||||
}
|
||||
|
||||
function status(text, art){
|
||||
const s = document.getElementById('mnStatus');
|
||||
if(!s) return;
|
||||
s.className = 'notizen-status' + (art ? ' ist-' + art : '');
|
||||
s.innerHTML = text;
|
||||
}
|
||||
function hinweis(text){ if(typeof zeigeHinweis === 'function') zeigeHinweis(text); else status(esc(text), 'fehler'); }
|
||||
|
||||
// ---------- Bedienung ----------
|
||||
window.mnFilter = function(wert){ jetztSpeichern(); filter = wert || ''; aufbauen(); };
|
||||
window.mnSuchen = function(wert){ suche = wert || ''; listeZeichnen(); };
|
||||
window.mnWaehlen = function(id){
|
||||
if(id === aktivId) return;
|
||||
jetztSpeichern();
|
||||
aktivId = id;
|
||||
listeZeichnen();
|
||||
editorZeichnen();
|
||||
};
|
||||
window.mnGeaendert = function(){
|
||||
const n = aktiv();
|
||||
if(!n || !darf(n)) return;
|
||||
ungespeichert = true;
|
||||
status('Ungespeichert');
|
||||
clearTimeout(speicherTimer);
|
||||
speicherTimer = setTimeout(jetztSpeichern, SPEICHER_PAUSE_MS);
|
||||
};
|
||||
window.mnJetztSpeichern = function(){ jetztSpeichern(); };
|
||||
|
||||
function senden(n, nutzlast){
|
||||
eigenesSchreiben = Date.now();
|
||||
return fetch(baseUrl() + '/projekt-notizen/' + encodeURIComponent(n.id), {
|
||||
method: 'PUT', headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: JSON.stringify(nutzlast)
|
||||
}).then(r => r.json().then(d => ({ r, d })));
|
||||
}
|
||||
|
||||
function jetztSpeichern(ohneBasis){
|
||||
clearTimeout(speicherTimer);
|
||||
const n = aktiv();
|
||||
if(!n || !ungespeichert || !darf(n)) return Promise.resolve();
|
||||
if(speichertGerade){ speicherTimer = setTimeout(jetztSpeichern, 400); return Promise.resolve(); }
|
||||
const titelEl = document.getElementById('mnTitel');
|
||||
const textEl = document.getElementById('mnText');
|
||||
const titel = titelEl ? titelEl.value : n.titel;
|
||||
const html = textEl ? textEl.innerHTML : n.html;
|
||||
ungespeichert = false;
|
||||
speichertGerade = true;
|
||||
status('Speichert…');
|
||||
return senden(n, { titel, html, basis: ohneBasis ? '' : basis }).then(({ r, d }) => {
|
||||
if(r.status === 409 && d.konflikt){
|
||||
ungespeichert = true;
|
||||
status(esc(d.error) + ' <button type="button" class="notizen-link" onclick="mnKonflikt(true)">Meine Fassung speichern</button>'
|
||||
+ ' <button type="button" class="notizen-link" onclick="mnKonflikt(false)">Neuen Stand laden</button>', 'warnung');
|
||||
n._fremd = d.notiz;
|
||||
return;
|
||||
}
|
||||
if(!r.ok || !d.ok) throw new Error(d.error || 'Speichern fehlgeschlagen');
|
||||
Object.assign(n, d.notiz, { titel, html });
|
||||
if(n.id === aktivId) basis = d.notiz.geaendertAm;
|
||||
sortieren();
|
||||
listeZeichnen();
|
||||
if(!ungespeichert) status('Gespeichert ✓', 'ok');
|
||||
}).catch(e => {
|
||||
ungespeichert = true;
|
||||
status('Nicht gespeichert: ' + esc(e.message) + ' — wird erneut versucht.', 'fehler');
|
||||
speicherTimer = setTimeout(jetztSpeichern, 5000);
|
||||
}).finally(() => { speichertGerade = false; });
|
||||
}
|
||||
|
||||
window.mnKonflikt = function(meine){
|
||||
const n = aktiv();
|
||||
if(!n) return;
|
||||
if(meine){ ungespeichert = true; jetztSpeichern(true); return; }
|
||||
if(n._fremd) Object.assign(n, n._fremd);
|
||||
delete n._fremd;
|
||||
ungespeichert = false;
|
||||
listeZeichnen();
|
||||
editorZeichnen();
|
||||
};
|
||||
|
||||
// mnNeu: neue Notiz — im gefilterten Projekt, sonst ohne Projekt.
|
||||
window.mnNeu = function(){
|
||||
jetztSpeichern();
|
||||
const uid = filter && filter !== OHNE && darfProjekte() ? filter : '';
|
||||
eigenesSchreiben = Date.now();
|
||||
fetch(baseUrl() + '/projekt-notizen', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||
body: JSON.stringify({ uid, titel: '', html: '' })
|
||||
}).then(r => r.json()).then(d => {
|
||||
if(!d.ok) throw new Error(d.error || 'Anlegen fehlgeschlagen');
|
||||
liste = liste.filter(x => x.id !== d.notiz.id).concat([d.notiz]);
|
||||
sortieren();
|
||||
aktivId = d.notiz.id;
|
||||
suche = '';
|
||||
aufbauen();
|
||||
const t = document.getElementById('mnTitel');
|
||||
if(t) t.focus();
|
||||
}).catch(e => hinweis('Notiz konnte nicht angelegt werden: ' + e.message));
|
||||
};
|
||||
|
||||
// mnZuordnen: Notiz einem Projekt zuordnen (oder wieder lösen). Offene
|
||||
// Änderungen werden vorher gespeichert.
|
||||
window.mnZuordnen = function(uid){
|
||||
const n = aktiv();
|
||||
if(!n) return;
|
||||
jetztSpeichern().then(() => senden(n, { uid: uid || '' })).then(({ r, d }) => {
|
||||
if(!r.ok || !d.ok) throw new Error(d.error || 'Zuordnen fehlgeschlagen');
|
||||
Object.assign(n, d.notiz);
|
||||
if(n.uid) meineProjekte.add(n.uid);
|
||||
if(filter && filter !== (n.uid || OHNE)) filter = n.uid || OHNE;
|
||||
aufbauen();
|
||||
status(n.uid ? 'Dem Projekt zugeordnet ✓' : 'Vom Projekt gelöst ✓', 'ok');
|
||||
}).catch(e => { hinweis(e.message); editorZeichnen(); });
|
||||
};
|
||||
|
||||
window.mnAnheften = function(){
|
||||
const n = aktiv();
|
||||
if(!n) return;
|
||||
jetztSpeichern().then(() => senden(n, { angeheftet: !n.angeheftet })).then(({ d }) => {
|
||||
if(!d.ok) throw new Error(d.error || 'Fehler');
|
||||
n.angeheftet = d.notiz.angeheftet;
|
||||
sortieren();
|
||||
listeZeichnen();
|
||||
editorZeichnen();
|
||||
}).catch(e => hinweis('Anheften fehlgeschlagen: ' + e.message));
|
||||
};
|
||||
|
||||
window.mnLoeschen = function(){
|
||||
const n = aktiv();
|
||||
const knopf = document.getElementById('mnLoeschen');
|
||||
if(!n || !knopf) return;
|
||||
if(!loeschenFragt){
|
||||
knopf.textContent = 'Wirklich löschen?';
|
||||
knopf.classList.add('ist-fragend');
|
||||
loeschenFragt = setTimeout(() => {
|
||||
loeschenFragt = null;
|
||||
const k = document.getElementById('mnLoeschen');
|
||||
if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); }
|
||||
}, 4000);
|
||||
return;
|
||||
}
|
||||
clearTimeout(loeschenFragt); loeschenFragt = null;
|
||||
clearTimeout(speicherTimer); ungespeichert = false;
|
||||
eigenesSchreiben = Date.now();
|
||||
fetch(baseUrl() + '/projekt-notizen/' + encodeURIComponent(n.id), { method: 'DELETE' })
|
||||
.then(r => r.json().then(d => ({ r, d }))).then(({ r, d }) => {
|
||||
if(!r.ok || !d.ok) throw new Error(d.error || 'Löschen fehlgeschlagen');
|
||||
liste = liste.filter(x => x.id !== n.id);
|
||||
aktivId = null;
|
||||
aufbauen();
|
||||
}).catch(e => { status(esc(e.message), 'fehler'); const k = document.getElementById('mnLoeschen'); if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); } });
|
||||
};
|
||||
|
||||
window.mnProjektOeffnen = function(pid){
|
||||
jetztSpeichern();
|
||||
location.hash = 'notizen';
|
||||
if(typeof openProjectPlan === 'function') openProjectPlan(pid);
|
||||
return false;
|
||||
};
|
||||
|
||||
// ---------- Live: Änderungen aus anderen Browsern ----------
|
||||
window.meineNotizenLive = function(uid){
|
||||
if(typeof currentTab === 'undefined' || currentTab !== 'notizen' || !geladen) return;
|
||||
if(uid && !meineProjekte.has(uid)) return;
|
||||
const vorher = aktiv();
|
||||
const tippt = document.activeElement && document.activeElement.closest && document.activeElement.closest('#mnEditor');
|
||||
// Das Echo der eigenen Speicherung (kommt auch in diesem Tab an) auslassen.
|
||||
if(ungespeichert || tippt || Date.now() - eigenesSchreiben < 3000) return;
|
||||
laden().then(() => {
|
||||
if(vorher && liste.some(x => x.id === vorher.id)) aktivId = vorher.id;
|
||||
aufbauen();
|
||||
});
|
||||
};
|
||||
|
||||
// Klick irgendwo anders: die Rückfrage „Wirklich löschen?“ zurücknehmen.
|
||||
document.addEventListener('pointerdown', e => {
|
||||
if(!loeschenFragt || (e.target.closest && e.target.closest('#mnLoeschen'))) return;
|
||||
clearTimeout(loeschenFragt); loeschenFragt = null;
|
||||
const k = document.getElementById('mnLoeschen');
|
||||
if(k){ k.textContent = 'Löschen'; k.classList.remove('ist-fragend'); }
|
||||
}, true);
|
||||
window.addEventListener('pagehide', () => { if(ungespeichert) jetztSpeichern(); });
|
||||
})();
|
||||
Reference in new issue
Block a user