Nichts verschwindet mehr ohne eine Nachfrage, die sagt was passiert
Filipe: "Es darf vor allem keine Stellen geben, an denen ein Benutzer
etwas falsch machen kann, nur weil die Seite es nicht verstaendlich
genug erklaert."
Gemessen: 30 Stellen in 16 Dateien benutzten confirm() oder prompt().
Das Haus hatte die richtige Bauweise laengst -- einen <dialog>, in
aufgaben.html sogar ausfuehrlich begruendet -- aber sie stand IN EINER
SEITE. Wer anderswo etwas loeschen liess, hatte sie nicht.
confirm('Wirklich loeschen?') stellt die falsche Frage: Es fragt, ob
man sicher ist, und nennt nicht, WAS passiert, was BLEIBT und ob es
ZURUECK geht. Jetzt beantwortet jeder der 41 Dialoge alle drei.
Neu: workspace/assets/js/nachfrage.js -- window.frageNach() mit
Pflichtgrund, Zahlenfeld, einzeiliger Eingabe und Abtippsicherung.
Drei Ausgaenge: <dialog> / confirm()-Notnagel fuer Safari vor 15.4 /
Abbruch (Esc, Klick daneben, "Doch nicht" -- immer false).
DREIMAL DERSELBE FALLSTRICK, dreimal nachgemessen statt vermutet:
.dialog stand in aufgaben.css und leistung.css -> auf dateien.html
waere der Dialog ein weisser Systemkasten gewesen. 14 Regeln
klammergenau nach module.css verschoben (Klammern gezaehlt, nicht
per Muster geschnitten -- heute frueh hat ein nicht-gieriges
Muster schon einmal CSS zerrissen).
Das Formular trug .neu neu--blank -- und .neu gibt seine Abstaende
nur in aufgaben.css. Gemessen: padding 0px, und die Felder
verloren ihre height:44px. Jetzt steht alles unter
.nachfrage__form in module.css; der Dialog borgt nichts mehr.
Die erste Fassung der Pruefung zaehlte nachfrage.js SELBST als
Nutzer -- damit war jede Seite trivialerweise "Nutzer" und die
Pruefung gruen ohne Inhalt. Jetzt ausdruecklich ausgenommen.
ZWEI FUNDE NEBENBEI:
hilfeAufraeumen() wird im Betrieb NIE aufgerufen. Der Kommentar
behauptete "wird beim Start aufgerufen (siehe index.js)" -- das
war nie wahr; einziger Aufrufer ist die eigene Pruefung. Folge:
geschlossene vertrauliche Faelle bleiben unbegrenzt stehen. NICHT
eingeschaltet (das loescht echte Daten und ist Filipes
Entscheidung), sondern der Kommentar richtiggestellt.
Einen Hilfe-Fall zu schliessen ist endgueltig -- es gibt keine
Route, die ihn wieder oeffnet. Vorher stand darueber nur die
Frage nach einem Schlusswort. Jetzt sagt der Dialog es.
pruef-struktur hat meine eigene Pruefung von heute Nachmittag
erwischt: Sie bildete ihr Datum aus UTC. Beim Beheben erst
heuteLokal(datum) genommen -- die Funktion nimmt gar kein Argument
und haette still "heute" statt "+3 Tage" geliefert. Jetzt tagLokal(3),
nachgerechnet: Abstand 3 Tage.
Am Bildschirm angesehen (Rechner 1280, Handy 390): passt rein, Esc
ergibt false, Fokus liegt auf dem harmlosen Knopf, Knoepfe 44px auf
Touch. Der Platzhalter im Abtippfeld zeigte den erwarteten Namen --
das sah aus wie ein schon ausgefuelltes Feld, entfernt.
Neu: server/pruef-nachfrage.mjs -- 17/0, mit sechs Gegenproben und
beiden Richtungen (wer fragt, laedt die Datei; wer nie fragt, laedt
sie nicht -- sonst truege die Anmeldewand 4,8 KB fuer nichts).
pruef-meldungen 8/0, pruef-css-klassen gruen, pruef-struktur gruen,
pruef-leistung gruen.
This commit is contained in:
@@ -110,9 +110,17 @@
|
||||
es sonst ab, und das ist richtig so. Hier wird deshalb
|
||||
gleich danach gefragt statt eine Fehlermeldung zu zeigen. */
|
||||
if (wert === 'verbessern') {
|
||||
grund = prompt('Was genau soll verbessert werden?\n\n'
|
||||
+ 'Ohne diesen Satz kann der Creator nichts damit anfangen.', grund) || '';
|
||||
if (grund.trim().length < 5) return;
|
||||
const ag = await frageNach({
|
||||
titel: 'Noch verbessern',
|
||||
was: 'Der Creator sieht diesen Satz an seinem Eintrag.',
|
||||
bleibt: 'Der Eintrag bleibt stehen – er wird nur als „noch '
|
||||
+ 'verbessern" markiert und kann überarbeitet werden.',
|
||||
ja: 'Zurückgeben',
|
||||
grund: 'Was genau soll verbessert werden?', grundPflicht: true,
|
||||
platzhalter: 'z. B. Das Licht ist zu dunkel – bitte heller nachdrehen.',
|
||||
});
|
||||
if (!ag) return;
|
||||
grund = ag.grund;
|
||||
}
|
||||
k.disabled = true;
|
||||
try {
|
||||
@@ -204,7 +212,12 @@
|
||||
weg.type = 'button';
|
||||
weg.title = 'Eigene Nachricht zurücknehmen';
|
||||
weg.addEventListener('click', async () => {
|
||||
if (!confirm('Diese Nachricht zurücknehmen?')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Nachricht zurücknehmen?',
|
||||
was: 'Der Text verschwindet für alle, die ihn noch nicht gelesen haben.',
|
||||
bleibt: 'Wer ihn schon gelesen hat, erinnert sich trotzdem daran.',
|
||||
endgueltig: true, ja: 'Zurücknehmen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/eintrag/${eintragId}/nachricht/${n.id}`,
|
||||
{ method: 'DELETE' });
|
||||
|
||||
@@ -129,7 +129,12 @@
|
||||
weg.title = 'Rückmeldung entfernen';
|
||||
weg.setAttribute('aria-label', 'Rückmeldung entfernen');
|
||||
weg.addEventListener('click', async () => {
|
||||
if (!confirm('Diese Rückmeldung entfernen?')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Rückmeldung entfernen?',
|
||||
was: 'Sie verschwindet aus der Aufgabe.',
|
||||
bleibt: 'Die Aufgabe selbst bleibt unverändert stehen.',
|
||||
endgueltig: true, ja: 'Entfernen',
|
||||
})) return;
|
||||
const r = await hole('/workspace/api/notizen/' + n.id, { method: 'DELETE' });
|
||||
if (!r.ok) { melde('Entfernen hat nicht geklappt.'); return; }
|
||||
await laden();
|
||||
@@ -997,11 +1002,15 @@
|
||||
});
|
||||
|
||||
$('b-loeschen').addEventListener('click', async () => {
|
||||
if (!confirm(`Aufgabe wirklich loeschen?
|
||||
|
||||
"${bearbeitet.titel}"
|
||||
|
||||
Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerweise stehen bleiben.`)) return;
|
||||
/* Loeschen ist hier fast nie das Richtige -- deshalb nennt der
|
||||
Dialog den sanfteren Weg beim Namen, statt nur zu warnen. */
|
||||
if (!await frageNach({
|
||||
titel: `„${bearbeitet.titel}" löschen?`,
|
||||
was: 'Die Aufgabe wird samt Rückmeldungen und Verlauf entfernt.',
|
||||
bleibt: 'Wenn sie nur nicht mehr stören soll: „Abbrechen" legt sie '
|
||||
+ 'beiseite, ohne etwas wegzuwerfen – dort ist sie später noch da.',
|
||||
endgueltig: true, ja: 'Endgültig löschen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/aufgaben/${bearbeitet.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { $('b-fehler').textContent = 'Loeschen ging nicht.'; return; }
|
||||
|
||||
@@ -518,8 +518,13 @@
|
||||
b.type = 'button';
|
||||
b.title = 'Schickt Art, Titel und Text als Nachricht in den Chat.';
|
||||
b.addEventListener('click', async () => {
|
||||
if (!confirm(`„${e.titel}“ als Nachricht schicken?\n\n`
|
||||
+ 'Die Person bekommt Art, Titel und Text im Chat – einmalig.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${e.titel}" als Nachricht schicken?`,
|
||||
was: 'Die Person bekommt Art, Titel und Text im Chat – einmalig.',
|
||||
bleibt: 'Der Eintrag bleibt hier stehen. Zurückholen lässt sich '
|
||||
+ 'die Nachricht nicht, sobald sie unterwegs ist.',
|
||||
ja: 'Schicken',
|
||||
})) return;
|
||||
b.disabled = true;
|
||||
const a = await hole(`/workspace/api/bereich/entwicklung/${e.id}/senden`,
|
||||
{ method: 'POST', headers: { 'Content-Type': 'application/json' } });
|
||||
@@ -897,9 +902,17 @@
|
||||
const vomTeam = !!e.erstellt_rolle && e.erstellt_rolle !== 'gast';
|
||||
if (!lage.team && e.erstellt_von !== ich.id && !vomTeam) {
|
||||
zeile.append(tuKnopf('melden', async () => {
|
||||
const grund = prompt('Was ist daran nicht in Ordnung?\n\n'
|
||||
+ 'Bitte schreib es in einem Satz – sonst können wir es nicht prüfen.');
|
||||
if (grund === null) return;
|
||||
const a0 = await frageNach({
|
||||
titel: 'Diesen Beitrag melden?',
|
||||
was: 'Das Team sieht sich den Beitrag an. Du bleibst dabei ungenannt.',
|
||||
bleibt: 'Der Beitrag bleibt vorerst stehen – gemeldet heißt noch '
|
||||
+ 'nicht entfernt.',
|
||||
ja: 'Melden',
|
||||
grund: 'Was ist daran nicht in Ordnung?', grundPflicht: true,
|
||||
platzhalter: 'Bitte in einem Satz – sonst können wir es nicht prüfen.',
|
||||
});
|
||||
if (!a0) return;
|
||||
const grund = a0.grund;
|
||||
const a = await hole('/workspace/api/treff/eintrag/' + e.id + '/melden', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ grund }),
|
||||
@@ -956,7 +969,13 @@
|
||||
dass er einer wird, entscheidet ein Mensch. */
|
||||
if (bereich === 'mitmachen' && e.erstellt_rolle === 'gast') {
|
||||
zeile.append(tuKnopf('zu den Talenten', async () => {
|
||||
if (!confirm('„' + e.titel + '“ zu den Talenten übernehmen?')) return;
|
||||
if (!await frageNach({
|
||||
titel: '„' + e.titel + '" zu den Talenten übernehmen?',
|
||||
was: 'Die Person erscheint danach in der Talente-Liste.',
|
||||
bleibt: 'Der Beitrag hier bleibt stehen. Übernehmen heißt noch '
|
||||
+ 'keine Zusage – es entscheidet weiterhin ein Mensch.',
|
||||
ja: 'Übernehmen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/treff/mitmachen/' + e.id + '/uebernehmen',
|
||||
{ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
|
||||
const j = await a.json().catch(() => ({}));
|
||||
@@ -971,9 +990,17 @@
|
||||
fuer den eigenen Beitrag, und der braucht keine Begruendung. */
|
||||
if (e.erstellt_von !== ich.id) {
|
||||
zeile.append(tuKnopf('entfernen', async () => {
|
||||
const grund = prompt('Warum wird „' + e.titel + '“ entfernt?\n\n'
|
||||
+ 'Der Grund wird festgehalten – die Person hat ein Recht darauf.');
|
||||
if (grund === null) return;
|
||||
const a1 = await frageNach({
|
||||
titel: '„' + e.titel + '" entfernen?',
|
||||
was: 'Der Beitrag verschwindet vom Brett.',
|
||||
bleibt: 'Der Grund wird festgehalten und der Person mitgeteilt – '
|
||||
+ 'sie hat ein Recht darauf (DSA Art. 17).',
|
||||
endgueltig: true, ja: 'Entfernen',
|
||||
grund: 'Warum wird er entfernt?', grundPflicht: true,
|
||||
platzhalter: 'z. B. Beleidigende Sprache gegenüber einem anderen Mitglied.',
|
||||
});
|
||||
if (!a1) return;
|
||||
const grund = a1.grund;
|
||||
/* DER GRUND REIST IN DER ADRESSE, nicht im Koerper -- aus
|
||||
demselben gemessenen Grund wie oben. Er ist kein
|
||||
Geheimnis: Er wird der Person, die den Beitrag
|
||||
@@ -1208,7 +1235,12 @@
|
||||
|
||||
if (LEITUNG.has(ich.rolle) || e.erstellt_von === ich.id) {
|
||||
knoepfe.append(tuKnopf('löschen', async () => {
|
||||
if (!confirm(`„${e.titel}" wirklich löschen?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${e.titel}" löschen?`,
|
||||
was: 'Der Eintrag verschwindet vom Brett – für alle.',
|
||||
bleibt: 'Nachrichten, die dazu geschrieben wurden, gehen mit.',
|
||||
endgueltig: true, ja: 'Löschen',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/bereich/${bereich}/${e.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen hat nicht geklappt.'); return; }
|
||||
await laden();
|
||||
|
||||
@@ -368,11 +368,13 @@
|
||||
if (ich?.rolle === 'admin') {
|
||||
const weg = el('div', 'kk__knoepfe');
|
||||
weg.append(tuKnopf('Protokoll löschen', async () => {
|
||||
if (!confirm('Protokoll zu „' + c.titel + '" löschen?\n\n'
|
||||
+ 'Der Text ist danach weg und lässt sich nicht wiederholen.\n\n'
|
||||
+ 'Das Gespräch selbst bleibt im Kalender und steht danach '
|
||||
+ 'wieder unter „Protokoll fehlt".\n\n'
|
||||
+ 'Bereits daraus entstandene Aufgaben bleiben ebenfalls stehen.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Protokoll zu „' + c.titel + '" löschen?',
|
||||
was: 'Der Text ist danach weg und lässt sich nicht wiederholen.',
|
||||
bleibt: 'Das Gespräch bleibt im Kalender und steht danach wieder '
|
||||
+ 'unter „Protokoll fehlt". Daraus entstandene Aufgaben bleiben auch.',
|
||||
endgueltig: true, ja: 'Protokoll löschen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/calls/' + c.id + '/protokoll', { method: 'DELETE' });
|
||||
if (!a.ok) {
|
||||
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Löschen ging nicht.'));
|
||||
|
||||
+21
-14
@@ -1418,8 +1418,12 @@
|
||||
}
|
||||
|
||||
async function zuruecknehmen(id) {
|
||||
if (!window.confirm('Diese Nachricht zurücknehmen? Der Text ist dann weg, '
|
||||
+ 'im Verlauf bleibt ein Hinweis stehen.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Nachricht zurücknehmen?',
|
||||
was: 'Der Text ist danach weg – auch bei allen anderen.',
|
||||
bleibt: 'Im Verlauf bleibt ein Hinweis stehen, dass hier etwas stand.',
|
||||
endgueltig: true, ja: 'Zurücknehmen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/chat/nachrichten/${id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Ging nicht.'); return; }
|
||||
@@ -2220,11 +2224,13 @@
|
||||
$('gespraech-weg').addEventListener('click', async () => {
|
||||
if (!offen) return;
|
||||
const wer = offen.name;
|
||||
if (!window.confirm(
|
||||
`Gespräch mit ${wer} wegräumen?\n\n`
|
||||
+ 'Es verschwindet nur bei dir. Der Verlauf beim Gegenüber bleibt '
|
||||
+ 'vollständig.\n\nSchreibt euch jemand wieder, kommt das Gespräch '
|
||||
+ 'mit dem Neuen zurück – das Alte bleibt weg.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `Gespräch mit ${wer} wegräumen?`,
|
||||
was: 'Es verschwindet nur bei dir aus der Liste.',
|
||||
bleibt: `Der Verlauf bei ${wer} bleibt vollständig. Schreibt ihr euch `
|
||||
+ 'wieder, kommt das Gespräch mit dem Neuen zurück – das Alte bleibt weg.',
|
||||
ja: 'Wegräumen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/chat/raeume/${offen.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Ging nicht.'); return; }
|
||||
@@ -2252,13 +2258,14 @@
|
||||
if (!offen || !offen.darf_aufloesen) return;
|
||||
const wasIstEs = offen.art === 'kanal' ? 'Kanal' : 'Gruppe';
|
||||
const andere = Math.max(0, (offen.teilnehmer?.length || 1) - 1);
|
||||
if (!window.confirm(
|
||||
`${wasIstEs} „${offen.name}" für alle auflösen?\n\n`
|
||||
+ `Der ganze Verlauf wird gelöscht – auch bei ${andere === 1
|
||||
? 'der anderen Person' : `den ${andere} anderen`}, samt aller `
|
||||
+ 'Anhänge. Das lässt sich nicht rückgängig machen.\n\n'
|
||||
+ 'Wenn du sie nur bei dir loswerden willst, nimm den Knopf '
|
||||
+ 'daneben – dann behalten die anderen alles.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `${wasIstEs} „${offen.name}" für alle auflösen?`,
|
||||
was: `Der ganze Verlauf wird gelöscht – auch bei ${andere === 1
|
||||
? 'der anderen Person' : `den ${andere} anderen`}, samt aller Anhänge.`,
|
||||
bleibt: 'Wenn du sie nur bei dir loswerden willst, nimm den Knopf '
|
||||
+ 'daneben – dann behalten die anderen alles.',
|
||||
endgueltig: true, ja: 'Für alle auflösen',
|
||||
})) return;
|
||||
const knopf = $('raum-aufloesen');
|
||||
knopf.disabled = true;
|
||||
try {
|
||||
|
||||
@@ -440,9 +440,17 @@
|
||||
const neu = stufe === wert ? 'offen' : wert;
|
||||
let grund = stand?.begruendung || '';
|
||||
if (neu === 'verbessern') {
|
||||
grund = prompt(`„${p.titel}“\n\nWas genau soll verbessert werden?\n\n`
|
||||
+ 'Ohne diesen Satz kann er nichts damit anfangen.', grund) || '';
|
||||
if (grund.trim().length < 5) return;
|
||||
const ag = await frageNach({
|
||||
titel: `„${p.titel}" – noch verbessern`,
|
||||
was: 'Dein Satz steht danach an diesem Punkt.',
|
||||
bleibt: 'Der Punkt bleibt offen und kann jederzeit wieder '
|
||||
+ 'auf „in Ordnung" gesetzt werden.',
|
||||
ja: 'Zurückgeben',
|
||||
grund: 'Was genau soll verbessert werden?', grundPflicht: true,
|
||||
platzhalter: 'z. B. Fehlt noch das Impressum unten auf der Seite.',
|
||||
});
|
||||
if (!ag) return;
|
||||
grund = ag.grund;
|
||||
}
|
||||
b.disabled = true;
|
||||
try {
|
||||
@@ -549,7 +557,12 @@
|
||||
weg.type = 'button';
|
||||
weg.title = 'Eigene Nachricht zurücknehmen';
|
||||
weg.addEventListener('click', async () => {
|
||||
if (!confirm('Diese Nachricht zurücknehmen?')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Nachricht zurücknehmen?',
|
||||
was: 'Der Text verschwindet aus diesem Punkt.',
|
||||
bleibt: 'Der Punkt selbst und sein Stand bleiben unverändert.',
|
||||
endgueltig: true, ja: 'Zurücknehmen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/checkliste/nachricht/${n.id}`,
|
||||
{ method: 'DELETE' });
|
||||
|
||||
@@ -213,7 +213,12 @@
|
||||
knoepfe.append(tuKnopf('→ LIVE', 'Als LIVE-Thema vormerken', () => uebernehmen(e, 'live')));
|
||||
}
|
||||
knoepfe.append(tuKnopf('löschen', 'Dieses Video entfernen', async () => {
|
||||
if (!confirm(`„${e.titel}" wirklich löschen?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${e.titel}" löschen?`,
|
||||
was: 'Die Idee verschwindet aus der Planung.',
|
||||
bleibt: 'Ein schon eingetragener Drehtermin im Kalender bleibt stehen.',
|
||||
endgueltig: true, ja: 'Löschen',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/bereich/content/${e.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen hat nicht geklappt.'); return; }
|
||||
await laden();
|
||||
@@ -383,7 +388,13 @@
|
||||
Ausgeblendet ist nicht dasselbe wie nicht vorhanden. */
|
||||
if (!darfEintragen()) { ziel.append(z); continue; }
|
||||
z.append(tuKnopf('löschen', 'Säule entfernen, Videos bleiben', async () => {
|
||||
if (!confirm(`Säule „${s.name}" entfernen? Die Videos bleiben, sie verlieren nur die Zuordnung.`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `Säule „${s.name}" entfernen?`,
|
||||
was: 'Die Säule verschwindet aus der Auswahl.',
|
||||
bleibt: 'Alle Videos bleiben – sie stehen danach nur ohne Säule da '
|
||||
+ 'und lassen sich jederzeit einer anderen zuordnen.',
|
||||
ja: 'Säule entfernen',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/content/saeulen/${s.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Ging nicht.'); return; }
|
||||
await laden();
|
||||
|
||||
@@ -220,7 +220,12 @@
|
||||
}
|
||||
if (LEITUNG.has(ich.rolle) || (d.hochgeladen_von === ich.id && d.status !== 'freigegeben')) {
|
||||
knoepfe.append(tuKnopf('löschen', async () => {
|
||||
if (!confirm(`„${d.name_original}" wirklich löschen?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${d.name_original}" löschen?`,
|
||||
was: 'Die Datei wird vom Server entfernt.',
|
||||
bleibt: 'Wer sie schon heruntergeladen hat, behält seine Kopie.',
|
||||
endgueltig: true, ja: 'Löschen',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/dateien/${d.id}`, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen hat nicht geklappt.'); return; }
|
||||
await laden();
|
||||
|
||||
@@ -287,8 +287,25 @@
|
||||
}
|
||||
|
||||
async function fallSchliessen(id) {
|
||||
const wort = prompt('Ein Schlusswort? (Kann auch leer bleiben)');
|
||||
if (wort === null) return;
|
||||
/* DER EINZIGE WEG IST VORWAERTS. Gemessen am 19.09.2026: Es gibt
|
||||
keine Route, die einen geschlossenen Fall wieder oeffnet -- weder
|
||||
fuer die Leitung noch fuer die meldende Person. Ein Klick hier
|
||||
ist also endgueltig, und vorher stand darueber nur die Frage
|
||||
nach einem Schlusswort. Wer sie las, konnte nicht ahnen, dass
|
||||
damit der Fall zugeht. */
|
||||
const antwort = await frageNach({
|
||||
titel: 'Diesen Fall abschließen?',
|
||||
was: 'Der Fall gilt danach als erledigt. Es kann niemand mehr '
|
||||
+ 'antworten – auch die Person nicht, die ihn gemeldet hat.',
|
||||
bleibt: 'Der ganze Verlauf bleibt lesbar. Wieder aufmachen lässt '
|
||||
+ 'sich der Fall aber nicht – dafür müsste er neu gemeldet werden.',
|
||||
endgueltig: true,
|
||||
ja: 'Abschließen',
|
||||
grund: 'Ein Schlusswort für die Person? (darf leer bleiben)',
|
||||
platzhalter: 'z. B. Wir haben mit beiden gesprochen, es ist geklärt.',
|
||||
});
|
||||
if (!antwort) return;
|
||||
const wort = antwort.grund;
|
||||
try {
|
||||
const a = await hole('/workspace/api/hilfe/' + id + '/schliessen', {
|
||||
method: 'POST',
|
||||
|
||||
@@ -64,10 +64,13 @@
|
||||
}
|
||||
|
||||
async function erzeugen(erneuern) {
|
||||
if (erneuern && !window.confirm(
|
||||
'Neuen Link erzeugen?\n\nDer alte hört im selben Moment auf zu funktionieren. '
|
||||
+ 'Ein Kalender, der ihn abonniert hat, bekommt danach keine Termine mehr, '
|
||||
+ 'bis du ihm den neuen gibst.')) return;
|
||||
if (erneuern && !await frageNach({
|
||||
titel: 'Neuen Link erzeugen?',
|
||||
was: 'Der alte hört im selben Moment auf zu funktionieren.',
|
||||
bleibt: 'Deine Termine selbst bleiben. Ein Kalender, der den alten Link '
|
||||
+ 'abonniert hat, bekommt aber keine mehr – bis du ihm den neuen gibst.',
|
||||
endgueltig: true, ja: 'Neuen Link erzeugen',
|
||||
})) return;
|
||||
melde('');
|
||||
try {
|
||||
const a = await hole('/workspace/api/ics', {
|
||||
@@ -83,9 +86,13 @@
|
||||
}
|
||||
|
||||
async function abschalten() {
|
||||
if (!window.confirm(
|
||||
'Abschalten?\n\nDeine Termine verschwinden dann aus jedem Kalender, '
|
||||
+ 'der sie abonniert hat.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Kalender-Link abschalten?',
|
||||
was: 'Deine Termine verschwinden aus jedem Kalender, der sie abonniert hat.',
|
||||
bleibt: 'Die Termine selbst bleiben hier im Workspace vollständig stehen. '
|
||||
+ 'Du kannst jederzeit einen neuen Link erzeugen.',
|
||||
ja: 'Abschalten',
|
||||
})) return;
|
||||
melde('');
|
||||
try {
|
||||
const a = await hole('/workspace/api/ics', { method: 'DELETE' });
|
||||
|
||||
@@ -949,11 +949,14 @@
|
||||
if (s.darf_aendern) {
|
||||
const knoepfe = el('div', 'k-serie__knoepfe');
|
||||
knoepfe.append(tuKnopf(s.aktiv ? 'abstellen' : 'wieder starten', async () => {
|
||||
if (s.aktiv && !confirm(
|
||||
'Wiederholung „' + s.titel + '" abstellen?\n\n'
|
||||
+ 'Kommende Termine, die noch niemand angefasst hat, werden aus dem '
|
||||
+ 'Kalender genommen. Vergangene und bearbeitete Termine bleiben stehen.\n\n'
|
||||
+ 'Du kannst sie jederzeit wieder starten.')) return;
|
||||
if (s.aktiv && !await frageNach({
|
||||
titel: 'Wiederholung „' + s.titel + '" abstellen?',
|
||||
was: 'Kommende Termine, die noch niemand angefasst hat, werden aus '
|
||||
+ 'dem Kalender genommen.',
|
||||
bleibt: 'Vergangene und bearbeitete Termine bleiben stehen. Du kannst '
|
||||
+ 'die Wiederholung jederzeit wieder starten.',
|
||||
ja: 'Abstellen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/serien/' + s.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -963,10 +966,14 @@
|
||||
await laden();
|
||||
}));
|
||||
knoepfe.append(tuKnopf('löschen', async () => {
|
||||
if (!confirm('Wiederholung „' + s.titel + '" ganz löschen?\n\n'
|
||||
+ 'Die Regel ist damit weg. Kommende, unangetastete Termine werden '
|
||||
+ 'entfernt; vergangene und bearbeitete bleiben als normale Termine '
|
||||
+ 'stehen.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Wiederholung „' + s.titel + '" ganz löschen?',
|
||||
was: 'Die Regel ist damit weg. Kommende, noch unangetastete Termine '
|
||||
+ 'werden entfernt.',
|
||||
bleibt: 'Vergangene und schon bearbeitete Termine bleiben als '
|
||||
+ 'normale Termine stehen.',
|
||||
endgueltig: true, ja: 'Wiederholung löschen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/serien/' + s.id, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen ging nicht.'); return; }
|
||||
await laden();
|
||||
@@ -1221,12 +1228,20 @@
|
||||
dass NUR dieser eine Tag entfällt. Sonst löscht jemand ihn
|
||||
in der Annahme, die Wiederholung sei damit abgestellt --
|
||||
und wundert sich nächste Woche. */
|
||||
const frage = e.serie_id
|
||||
? 'Nur diesen einen Termin am ' + tagLang(e.tag) + ' streichen?\n\n'
|
||||
+ 'Die Wiederholung „' + e.titel + '" läuft weiter. Zum Abstellen '
|
||||
+ 'den Schalter unter „Läuft von allein" benutzen.'
|
||||
: 'Termin „' + e.titel + '" löschen?';
|
||||
if (!confirm(frage)) return;
|
||||
|
||||
if (!await frageNach({
|
||||
titel: e.serie_id
|
||||
? 'Nur diesen einen Termin am ' + tagLang(e.tag) + ' streichen?'
|
||||
: 'Termin „' + e.titel + '" löschen?',
|
||||
was: e.serie_id
|
||||
? 'Nur dieser eine Tag entfällt.'
|
||||
: 'Der Termin verschwindet aus dem Kalender.',
|
||||
bleibt: e.serie_id
|
||||
? 'Die Wiederholung „' + e.titel + '" läuft weiter. Zum Abstellen '
|
||||
+ 'den Schalter unter „Läuft von allein" benutzen.'
|
||||
: 'Alle anderen Termine bleiben stehen.',
|
||||
endgueltig: true, ja: e.serie_id ? 'Diesen Tag streichen' : 'Löschen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/termine/' + e.id, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen ging nicht.'); return; }
|
||||
await laden();
|
||||
|
||||
@@ -404,7 +404,13 @@
|
||||
|
||||
async function tagLoeschen() {
|
||||
const d = $('tag-dialog');
|
||||
if (!window.confirm('Diese Zeile wirklich löschen? Die Zahlen des Tages sind dann weg.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Zahlen dieses Tages löschen?',
|
||||
was: 'Die eingetragenen Zahlen für diesen Tag werden entfernt.',
|
||||
bleibt: 'Alle anderen Tage bleiben unverändert. Du kannst den Tag '
|
||||
+ 'jederzeit neu eintragen.',
|
||||
endgueltig: true, ja: 'Zeile löschen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/leistung/${offenerCreator}/${d.dataset.tag}`,
|
||||
{ method: 'DELETE' });
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
/* =====================================================================
|
||||
NACHFRAGEN, BEVOR ETWAS WEG IST — window.frageNach()
|
||||
|
||||
Filipe, 19.09.2026: „Es darf vor allem keine Stellen geben, an denen
|
||||
ein Benutzer etwas falsch machen kann, nur weil die Seite es nicht
|
||||
verstaendlich genug erklaert." Und: „Fehler moeglichst verhindern,
|
||||
bevor sie entstehen."
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WARUM ES DIESE DATEI GIBT
|
||||
|
||||
Das Haus hatte die richtige Bauweise laengst -- in `aufgaben.html`
|
||||
steht sie sogar begruendet: ein <dialog>, kein confirm(), weil
|
||||
confirm die ganze Seite anhaelt, kein Textfeld aufnimmt und auf
|
||||
jedem Rechner anders aussieht.
|
||||
|
||||
Nur stand diese Bauweise IN EINER SEITE. Wer auf einer anderen Seite
|
||||
etwas loeschen liess, hatte sie nicht -- und griff zu confirm().
|
||||
Gemessen am 19.09.2026: 30 Stellen im Workspace, in 16 Dateien.
|
||||
Also wandert sie hierher, wo jede Seite sie hat.
|
||||
|
||||
Das ist dieselbe Lehre wie bei den Fehlermeldungen (`meldung.js`)
|
||||
und bei den Kachelfarben: Was an 30 Stellen gepflegt werden muss,
|
||||
wird an 30 Stellen unterschiedlich. Eine Stelle, die alle benutzen,
|
||||
kann nicht auseinanderlaufen.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WAS EIN GUTER NACHFRAGE-DIALOG SAGT
|
||||
|
||||
confirm('Wirklich loeschen?') stellt die falsche Frage. Es fragt, ob
|
||||
man sicher ist -- und wer soll das wissen, wenn nicht dasteht, was
|
||||
danach passiert? Der Dialog hier beantwortet drei Dinge:
|
||||
|
||||
1. WAS passiert -- "Die Datei wird geloescht."
|
||||
2. Was NICHT passiert -- "Der Chat-Verlauf bleibt."
|
||||
3. Ob es ZURUECK geht -- "Das laesst sich nicht rueckgaengig machen."
|
||||
|
||||
Punkt 3 ist der wichtigste und in confirm() gar nicht ausdrueckbar.
|
||||
Deshalb traegt der Bestaetigungsknopf bei `endgueltig: true` eine
|
||||
sichtbare Warnfarbe UND der Text sagt es noch einmal in Worten --
|
||||
Farbe allein traegt keine Aussage (Rot-Gruen-Schwaeche, Kontrastmodus).
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DREI AUSGAENGE, wie ueberall im Haus
|
||||
|
||||
<dialog> vorhanden -> der richtige Dialog
|
||||
<dialog> fehlt (alt) -> confirm() als Notnagel, mit DEMSELBEN
|
||||
Text, damit der Nutzer dieselbe
|
||||
Auskunft bekommt -- nur haesslicher
|
||||
Nutzer bricht ab -> false, und zwar immer: bei Esc, beim
|
||||
Klick daneben und beim "Doch nicht"
|
||||
|
||||
Der mittlere Ausgang ist nicht theoretisch: showModal gibt es erst ab
|
||||
Safari 15.4. Ein iPhone, das nicht aktualisiert wurde, wuerde ohne
|
||||
Notnagel gar nichts anzeigen -- und der Knopf taete stillschweigend
|
||||
nichts. Das waere schlimmer als ein haesslicher Dialog.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
AUFRUF
|
||||
|
||||
if (!await frageNach({
|
||||
titel: 'Datei loeschen?',
|
||||
was: 'Die Datei wird vom Server entfernt.',
|
||||
bleibt:'Die Nachricht im Chat bleibt stehen.',
|
||||
endgueltig: true,
|
||||
ja: 'Loeschen',
|
||||
})) return;
|
||||
|
||||
Mit Grund (gibt dann { grund } statt true zurueck):
|
||||
|
||||
const a = await frageNach({ titel: '...', grund: 'Warum?',
|
||||
grundPflicht: true });
|
||||
if (!a) return; // abgebrochen
|
||||
... a.grund ...
|
||||
===================================================================== */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* Ein einziger Dialog fuer die ganze Seite. Er wird beim ersten
|
||||
Gebrauch gebaut und danach wiederverwendet -- zwei offene Dialoge
|
||||
gleichzeitig kann der Browser ohnehin nicht, und ein neuer je
|
||||
Aufruf wuerde den Fokus verlieren. */
|
||||
var dlg = null, feld = null, tippfeld = null, zahlfeld = null, fehlerZeile = null;
|
||||
|
||||
function bauen() {
|
||||
if (dlg) return dlg;
|
||||
dlg = document.createElement('dialog');
|
||||
dlg.className = 'dialog nachfrage';
|
||||
dlg.innerHTML =
|
||||
'<form method="dialog" class="nachfrage__form">'
|
||||
+ '<h2 class="dialog__titel" data-teil="titel"></h2>'
|
||||
+ '<p class="dialog__unter" data-teil="was"></p>'
|
||||
+ '<p class="dialog__unter dialog__unter--still" data-teil="bleibt"></p>'
|
||||
+ '<p class="nachfrage__endgueltig" data-teil="endgueltig" hidden>'
|
||||
+ 'Das lässt sich danach nicht mehr rückgängig machen.</p>'
|
||||
+ '<div class="nachfrage__zeile" data-teil="tippzeile" hidden>'
|
||||
+ '<label class="feld-schild" data-teil="tippschild" for="nachfrage-tipp"></label>'
|
||||
+ '<input id="nachfrage-tipp" type="text" autocomplete="off"'
|
||||
+ ' autocapitalize="off" spellcheck="false" />'
|
||||
+ '</div>'
|
||||
+ '<div class="nachfrage__zeile" data-teil="grundzeile" hidden>'
|
||||
+ '<label class="feld-schild" data-teil="grundschild" for="nachfrage-grund"></label>'
|
||||
+ '<textarea id="nachfrage-grund" rows="3" maxlength="500"></textarea>'
|
||||
+ '</div>'
|
||||
+ '<div class="nachfrage__zeile" data-teil="zahlzeile" hidden>'
|
||||
+ '<label class="feld-schild" data-teil="zahlschild" for="nachfrage-zahl"></label>'
|
||||
+ '<input id="nachfrage-zahl" type="number" min="1" inputmode="numeric" />'
|
||||
+ '<p class="nachfrage__hinweis" data-teil="zahlhinweis"></p>'
|
||||
+ '</div>'
|
||||
+ '<p class="nachfrage__fehler" data-teil="fehler" role="alert"></p>'
|
||||
+ '<div class="dialog__knoepfe">'
|
||||
+ '<button type="submit" class="knopf knopf--klein" data-teil="ja"></button>'
|
||||
+ '<button type="button" class="abmelden" data-teil="nein">Doch nicht</button>'
|
||||
+ '</div></form>';
|
||||
document.body.appendChild(dlg);
|
||||
feld = dlg.querySelector('#nachfrage-grund');
|
||||
tippfeld = dlg.querySelector('#nachfrage-tipp');
|
||||
zahlfeld = dlg.querySelector('#nachfrage-zahl');
|
||||
fehlerZeile = dlg.querySelector('[data-teil="fehler"]');
|
||||
return dlg;
|
||||
}
|
||||
|
||||
function teil(name) { return dlg.querySelector('[data-teil="' + name + '"]'); }
|
||||
|
||||
/** Setzt Text und blendet die Zeile aus, wenn es keinen gibt.
|
||||
* Eine leere Zeile, die Platz einnimmt, sieht aus wie ein Fehler. */
|
||||
function zeile(name, text) {
|
||||
var e = teil(name);
|
||||
e.textContent = text || '';
|
||||
e.hidden = !text;
|
||||
}
|
||||
|
||||
window.frageNach = function frageNach(o) {
|
||||
o = o || {};
|
||||
var titel = o.titel || 'Sicher?';
|
||||
var was = o.was || '';
|
||||
var bleibt = o.bleibt || '';
|
||||
var ja = o.ja || 'Ja, machen';
|
||||
var nein = o.nein || 'Doch nicht';
|
||||
var endgueltig = !!o.endgueltig;
|
||||
var grund = o.grund || '';
|
||||
var grundPflicht = !!o.grundPflicht;
|
||||
var platzhalter = o.platzhalter || '';
|
||||
/* ABTIPPEN STATT ANKLICKEN. Fuer die Handgriffe, bei denen ein
|
||||
Fehlklick nicht zu reparieren ist (eine Person loeschen). Wer den
|
||||
Namen abtippt, hat ihn gelesen -- das ist der eigentliche Zweck,
|
||||
nicht die Huerde. */
|
||||
var tippen = o.tippen || '';
|
||||
/* EIN EINZEILIGES FELD, wenn nach einem Namen gefragt wird und
|
||||
nicht nach einem Grund. Ein Textfeld ueber drei Zeilen fuer einen
|
||||
Vornamen sieht aus, als erwarte jemand einen Aufsatz. Benutzt
|
||||
dasselbe Feld wie `tippen` -- beides gleichzeitig kommt nicht vor
|
||||
und waere auch nicht sinnvoll. */
|
||||
var eingabe = o.eingabe || '';
|
||||
var eingabeWert = o.eingabeWert || '';
|
||||
var eingabeMin = o.eingabeMin === undefined ? 2 : o.eingabeMin;
|
||||
/* EINE ZAHL IM SELBEN DIALOG. Vorher fragte die Moderation zweimal
|
||||
hintereinander (Grund, dann Dauer) -- zwei Kaesten nacheinander,
|
||||
und wer beim zweiten abbrach, hatte den ersten umsonst getippt.
|
||||
Reihenfolge bleibt: erst WARUM, dann WIE LANGE. Wer zuerst nach
|
||||
der Dauer gefragt wird, hat die Strafe festgelegt, bevor er sie
|
||||
begruendet hat. */
|
||||
var zahl = o.zahl || '';
|
||||
var zahlWert = o.zahlWert === undefined ? '' : String(o.zahlWert);
|
||||
var zahlMax = o.zahlMax || 0;
|
||||
var zahlLeer = !!o.zahlLeer;
|
||||
var zahlHinweis = o.zahlHinweis || '';
|
||||
var NIE_ZURUECK = 'Das lässt sich danach nicht mehr rückgängig machen.';
|
||||
|
||||
/* ---- Ausgang 2: kein <dialog> -> confirm() mit DEMSELBEN Text ----
|
||||
Nicht "Sicher?" -- wer den Notnagel bekommt, soll dieselbe
|
||||
Auskunft haben wie alle anderen. */
|
||||
if (typeof HTMLDialogElement === 'undefined'
|
||||
|| !HTMLDialogElement.prototype.showModal) {
|
||||
var text = [titel, was, bleibt, endgueltig ? NIE_ZURUECK : '']
|
||||
.filter(Boolean).join('\n\n');
|
||||
if (!window.confirm(text)) return Promise.resolve(false);
|
||||
if (tippen) {
|
||||
var t0 = window.prompt('Zum Bestätigen genau eingeben: ' + tippen, '');
|
||||
if (t0 === null || t0.trim() !== tippen) return Promise.resolve(false);
|
||||
}
|
||||
var erg = {};
|
||||
if (eingabe) {
|
||||
var e0 = window.prompt(eingabe, eingabeWert);
|
||||
if (e0 === null || e0.trim().length < eingabeMin) return Promise.resolve(false);
|
||||
erg.eingabe = e0.trim();
|
||||
}
|
||||
if (grund) {
|
||||
var g0 = window.prompt(grund, '');
|
||||
if (g0 === null) return Promise.resolve(false);
|
||||
if (grundPflicht && g0.trim().length < 3) return Promise.resolve(false);
|
||||
erg.grund = g0.trim();
|
||||
}
|
||||
if (zahl) {
|
||||
var z0 = window.prompt(zahl + (zahlHinweis ? ' (' + zahlHinweis + ')' : ''),
|
||||
zahlWert);
|
||||
if (z0 === null) return Promise.resolve(false);
|
||||
erg.zahl = z0.trim() === '' ? null : Number(z0);
|
||||
}
|
||||
return Promise.resolve((grund || zahl || eingabe) ? erg : true);
|
||||
}
|
||||
|
||||
bauen();
|
||||
teil('titel').textContent = titel;
|
||||
zeile('was', was);
|
||||
zeile('bleibt', bleibt);
|
||||
teil('endgueltig').hidden = !endgueltig;
|
||||
teil('ja').textContent = ja;
|
||||
teil('nein').textContent = nein;
|
||||
/* Warnfarbe NUR zusaetzlich zum Satz oben, nie statt seiner. */
|
||||
teil('ja').classList.toggle('knopf--warn', endgueltig);
|
||||
teil('tippzeile').hidden = !(tippen || eingabe);
|
||||
if (eingabe) {
|
||||
teil('tippschild').textContent = eingabe;
|
||||
tippfeld.value = eingabeWert;
|
||||
tippfeld.placeholder = '';
|
||||
} else if (tippen) {
|
||||
teil('tippschild').textContent = 'Zum Bestätigen genau eingeben: ' + tippen;
|
||||
tippfeld.value = '';
|
||||
/* KEIN Platzhalter mit dem erwarteten Wort. Er sieht in einem
|
||||
leeren Feld aus wie ein bereits eingetragener Text -- und wer
|
||||
glaubt, es stehe schon da, drueckt auf "Loeschen" und wundert
|
||||
sich ueber die Fehlermeldung. Der Name steht ohnehin in der
|
||||
Beschriftung direkt darueber. */
|
||||
tippfeld.placeholder = '';
|
||||
}
|
||||
teil('grundzeile').hidden = !grund;
|
||||
if (grund) {
|
||||
teil('grundschild').textContent = grund;
|
||||
feld.value = '';
|
||||
feld.placeholder = platzhalter;
|
||||
}
|
||||
teil('zahlzeile').hidden = !zahl;
|
||||
if (zahl) {
|
||||
teil('zahlschild').textContent = zahl;
|
||||
teil('zahlhinweis').textContent = zahlHinweis;
|
||||
zahlfeld.value = zahlWert;
|
||||
if (zahlMax) zahlfeld.max = String(zahlMax); else zahlfeld.removeAttribute('max');
|
||||
zahlfeld.required = !zahlLeer;
|
||||
}
|
||||
fehlerZeile.textContent = '';
|
||||
|
||||
return new Promise(function (fertig) {
|
||||
var antwort = false;
|
||||
var form = dlg.querySelector('form');
|
||||
|
||||
function aufraeumen() {
|
||||
dlg.removeEventListener('close', beiSchliessen);
|
||||
form.removeEventListener('submit', beiJa);
|
||||
teil('nein').removeEventListener('click', beiNein);
|
||||
dlg.removeEventListener('click', beiDaneben);
|
||||
}
|
||||
function beiSchliessen() { aufraeumen(); fertig(antwort); }
|
||||
function beiNein() { antwort = false; dlg.close(); }
|
||||
function beiDaneben(e) {
|
||||
/* Klick auf den abgedunkelten Rand = abbrechen. Das erwartet
|
||||
jeder, und ohne es bleibt der Dialog am Handy gefuehlt
|
||||
haengen, weil dort kein Esc zur Verfuegung steht. */
|
||||
if (e.target === dlg) { antwort = false; dlg.close(); }
|
||||
}
|
||||
function beiJa(e) {
|
||||
e.preventDefault();
|
||||
if (eingabe && tippfeld.value.trim().length < eingabeMin) {
|
||||
fehlerZeile.textContent = eingabeMin > 1
|
||||
? 'Bitte mindestens ' + eingabeMin + ' Zeichen eintragen.'
|
||||
: 'Bitte etwas eintragen.';
|
||||
tippfeld.focus(); return;
|
||||
}
|
||||
if (tippen && tippfeld.value.trim() !== tippen) {
|
||||
/* Sagt, WAS nicht stimmt -- "falsch" allein hilft niemandem,
|
||||
der sich vertippt hat. */
|
||||
fehlerZeile.textContent = 'Das stimmt noch nicht. Erwartet wird genau: '
|
||||
+ tippen;
|
||||
tippfeld.focus();
|
||||
tippfeld.select();
|
||||
return;
|
||||
}
|
||||
var erg2 = {};
|
||||
if (eingabe) erg2.eingabe = tippfeld.value.trim();
|
||||
if (grund) {
|
||||
var g = feld.value.trim();
|
||||
if (grundPflicht && g.length < 3) {
|
||||
fehlerZeile.textContent = 'Bitte kurz sagen, warum.';
|
||||
feld.focus();
|
||||
return; /* NICHT schliessen -- sonst waere die Eingabe weg */
|
||||
}
|
||||
erg2.grund = g;
|
||||
}
|
||||
if (zahl) {
|
||||
var roh = zahlfeld.value.trim();
|
||||
if (roh === '' && !zahlLeer) {
|
||||
fehlerZeile.textContent = 'Bitte eine Anzahl eintragen.';
|
||||
zahlfeld.focus(); return;
|
||||
}
|
||||
var n = roh === '' ? null : Number(roh);
|
||||
if (n !== null && !(n > 0)) {
|
||||
fehlerZeile.textContent = 'Das ist keine gültige Anzahl.';
|
||||
zahlfeld.focus(); return;
|
||||
}
|
||||
if (n !== null && zahlMax && n > zahlMax) {
|
||||
fehlerZeile.textContent = 'Mehr als ' + zahlMax + ' geht hier nicht.';
|
||||
zahlfeld.focus(); return;
|
||||
}
|
||||
erg2.zahl = n;
|
||||
}
|
||||
antwort = (grund || zahl || eingabe) ? erg2 : true;
|
||||
dlg.close();
|
||||
}
|
||||
|
||||
dlg.addEventListener('close', beiSchliessen);
|
||||
form.addEventListener('submit', beiJa);
|
||||
teil('nein').addEventListener('click', beiNein);
|
||||
dlg.addEventListener('click', beiDaneben);
|
||||
|
||||
dlg.showModal();
|
||||
/* Der Fokus liegt auf dem harmlosen Knopf, nicht auf "Loeschen".
|
||||
Wer aus Gewohnheit Enter drueckt, bricht damit ab -- das ist
|
||||
die richtige Richtung fuer einen Fehlgriff. Ausser es gibt ein
|
||||
Textfeld, dann gehoert der Fokus dorthin. */
|
||||
if (tippen || eingabe) tippfeld.focus();
|
||||
else if (grund) feld.focus();
|
||||
else teil('nein').focus();
|
||||
});
|
||||
};
|
||||
})();
|
||||
@@ -311,8 +311,14 @@
|
||||
allein sagt nicht, WAS sich ändert -- und es ist der Handgriff,
|
||||
der jemandem den Zugang zu einer ganzen Seite nimmt oder gibt. */
|
||||
const name = $('rolle-wahl').querySelector('.rollenwahl__knopf[data-an="ja"] .rollenwahl__name')?.textContent || gewaehlt;
|
||||
if (!confirm(`${rolleFuer.name} wird von "${ROLLENNAME[rolleFuer.rolle] || rolleFuer.rolle}" zu "${name}".\n\n`
|
||||
+ 'Der Zugangscode bleibt derselbe. Offene Sitzungen werden beendet.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `${rolleFuer.name}: Rolle ändern?`,
|
||||
was: `Von „${ROLLENNAME[rolleFuer.rolle] || rolleFuer.rolle}" zu „${name}".`
|
||||
+ ' Damit ändert sich, welche Seiten die Person öffnen darf.',
|
||||
bleibt: 'Der Zugangscode bleibt derselbe. Offene Sitzungen werden beendet, '
|
||||
+ 'danach kann sie sich mit dem alten Code wieder anmelden.',
|
||||
ja: `Zu „${name}" machen`,
|
||||
})) return;
|
||||
|
||||
const a = await hole(`/workspace/api/verwaltung/personen/${rolleFuer.id}/rolle`, {
|
||||
method: 'PUT',
|
||||
@@ -577,18 +583,17 @@ Der Code gilt einmal fuer die `
|
||||
|
||||
knoepfe.append(tuKnopf('Neuer Code', async () => {
|
||||
const eigen = p.id === ich.id;
|
||||
const frage = eigen
|
||||
/* Der Satz stimmte bis zum 02.09.2026 -- und beschrieb genau die
|
||||
Falle: Man bestätigte, der Code erschien, und im selben Moment
|
||||
war die Sitzung weg. Jetzt bleibt sie bestehen, und der Text
|
||||
sagt, was wirklich passiert. */
|
||||
? 'Neuer Code für dich selbst?\n\n'
|
||||
+ 'Der alte Code gilt danach nicht mehr, und alle ANDEREN Geräte '
|
||||
+ 'werden abgemeldet.\n\n'
|
||||
+ 'Hier bleibst du angemeldet – der neue Code steht danach auf dem '
|
||||
+ 'Bildschirm, bis du ihn selbst wegklickst.'
|
||||
: `Neuer Code für ${p.name}?\n\nDer alte Code gilt dann nicht mehr, offene Sitzungen werden beendet.`;
|
||||
if (!confirm(frage)) return;
|
||||
if (!await frageNach({
|
||||
titel: eigen ? 'Neuer Code für dich selbst?' : `Neuer Code für ${p.name}?`,
|
||||
was: 'Der alte Code gilt danach nicht mehr.',
|
||||
bleibt: eigen
|
||||
? 'Alle ANDEREN Geräte werden abgemeldet – hier bleibst du '
|
||||
+ 'angemeldet. Der neue Code steht danach auf dem Bildschirm, '
|
||||
+ 'bis du ihn selbst wegklickst.'
|
||||
: `Offene Sitzungen von ${p.name} werden beendet. Alles andere `
|
||||
+ 'bleibt unverändert – nur der Code ist neu.',
|
||||
endgueltig: true, ja: 'Neuen Code erzeugen',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/verwaltung/personen/${p.id}/code`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -601,7 +606,13 @@ Der Code gilt einmal fuer die `
|
||||
|
||||
if (p.id !== ich.id) {
|
||||
knoepfe.append(tuKnopf(p.aktiv ? 'Sperren' : 'Entsperren', async () => {
|
||||
if (p.aktiv && !confirm(`${p.name} sperren?\n\nOffene Sitzungen werden sofort beendet.`)) return;
|
||||
if (p.aktiv && !await frageNach({
|
||||
titel: `${p.name} sperren?`,
|
||||
was: 'Offene Sitzungen werden sofort beendet. Anmelden geht danach nicht mehr.',
|
||||
bleibt: 'Alle Daten bleiben stehen – Aufgaben, Nachrichten, Steckbrief. '
|
||||
+ 'Entsperren geht jederzeit mit demselben Knopf.',
|
||||
ja: 'Sperren',
|
||||
})) return;
|
||||
const a = await hole(`/workspace/api/verwaltung/personen/${p.id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -704,43 +715,42 @@ Der Code gilt einmal fuer die `
|
||||
|
||||
if (v.hindernis) { melde(v.hindernis); return; }
|
||||
|
||||
const zeilen = [];
|
||||
const mit = Object.entries(v.mit).filter(([, n]) => n > 0);
|
||||
const bleibt = Object.entries(v.bleibt).filter(([, n]) => n > 0);
|
||||
|
||||
zeilen.push(`„${v.person.name}" wirklich löschen?`, '');
|
||||
if (mit.length) {
|
||||
zeilen.push('Wird MITGELÖSCHT:');
|
||||
for (const [was, n] of mit) zeilen.push(` · ${was}: ${n}`);
|
||||
zeilen.push('');
|
||||
}
|
||||
if (bleibt.length) {
|
||||
zeilen.push('BLEIBT erhalten, verliert nur die Zuordnung:');
|
||||
for (const [was, n] of bleibt) zeilen.push(` · ${was}: ${n}`);
|
||||
zeilen.push('');
|
||||
}
|
||||
if (v.verliertBetreuung.length) {
|
||||
zeilen.push(`ACHTUNG: ${v.verliertBetreuung.join(', ')} `
|
||||
+ `${v.verliertBetreuung.length === 1 ? 'steht' : 'stehen'} danach ohne zuständige Person da.`, '');
|
||||
}
|
||||
if (!mit.length && !bleibt.length && !v.verliertBetreuung.length) {
|
||||
zeilen.push('An dieser Person hängt nichts.', '');
|
||||
}
|
||||
zeilen.push('Vorher wird automatisch eine Sicherung geschrieben.', '',
|
||||
`Zum Bestätigen den Namen genau eingeben: ${v.person.name}`);
|
||||
/* Was mitgeht und was bleibt, sind ZWEI Aussagen -- deshalb stehen
|
||||
sie im Dialog auch in zwei Zeilen und nicht in einer Liste, die
|
||||
man von oben nach unten durchliest und in der Mitte aufgibt. */
|
||||
const wasWeg = mit.length
|
||||
? 'Mitgelöscht wird: ' + mit.map(([w, n]) => `${w} (${n})`).join(', ') + '.'
|
||||
: 'An dieser Person hängt nichts, was mitgelöscht würde.';
|
||||
const wasBleibt = [
|
||||
bleibt.length
|
||||
? 'Erhalten bleibt, verliert aber die Zuordnung: '
|
||||
+ bleibt.map(([w, n]) => `${w} (${n})`).join(', ') + '.'
|
||||
: '',
|
||||
v.verliertBetreuung.length
|
||||
? `${v.verliertBetreuung.join(', ')} `
|
||||
+ `${v.verliertBetreuung.length === 1 ? 'steht' : 'stehen'} danach `
|
||||
+ 'ohne zuständige Person da.'
|
||||
: '',
|
||||
'Vorher wird automatisch eine Sicherung geschrieben.',
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
const eingabe = prompt(zeilen.join('\n'), '');
|
||||
if (eingabe === null) return;
|
||||
if (eingabe.trim() !== v.person.name) {
|
||||
melde('Der Name stimmt nicht – es wurde nichts gelöscht.');
|
||||
return;
|
||||
}
|
||||
if (!await frageNach({
|
||||
titel: `„${v.person.name}" löschen?`,
|
||||
was: wasWeg,
|
||||
bleibt: wasBleibt,
|
||||
endgueltig: true,
|
||||
ja: 'Endgültig löschen',
|
||||
tippen: v.person.name,
|
||||
})) return;
|
||||
|
||||
try {
|
||||
const a = await hole(`/workspace/api/verwaltung/personen/${person.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: eingabe.trim() }),
|
||||
body: JSON.stringify({ name: v.person.name }),
|
||||
});
|
||||
const d = await a.json().catch(() => ({}));
|
||||
if (!a.ok) { melde(sagWas(d.fehler, 'Löschen hat nicht geklappt.')); return; }
|
||||
|
||||
@@ -109,9 +109,13 @@
|
||||
sieht sofort jemand. Etwas aufzumachen merkt unter Umständen
|
||||
lange niemand — und genau das ist der Klick, bei dem eine
|
||||
Sekunde Nachdenken etwas wert ist. */
|
||||
if (erlaubt && !confirm(
|
||||
`„${rolle.name}" darf danach ${seite.datei} öffnen.\n\n`
|
||||
+ 'Das gilt sofort, für alle mit dieser Rolle. Sicher?')) return;
|
||||
if (erlaubt && !await frageNach({
|
||||
titel: `„${rolle.name}" darf danach ${seite.datei} öffnen`,
|
||||
was: 'Das gilt sofort, für alle mit dieser Rolle.',
|
||||
bleibt: 'Zurücknehmen geht jederzeit mit demselben Schalter – aber bis '
|
||||
+ 'dahin hat jeder mit dieser Rolle Zugang.',
|
||||
ja: 'Freigeben',
|
||||
})) return;
|
||||
|
||||
knopf.disabled = true;
|
||||
try {
|
||||
@@ -167,8 +171,13 @@
|
||||
const b = el('button', 'schritt', 'Alles zurück auf Grundstand');
|
||||
b.type = 'button';
|
||||
b.addEventListener('click', async () => {
|
||||
if (!confirm(`${daten.abweichungen} Änderungen zurücknehmen?\n\n`
|
||||
+ 'Danach gilt wieder genau das, was im Code steht.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `${daten.abweichungen} Änderungen zurücknehmen?`,
|
||||
was: 'Danach gilt wieder genau das, was im Code steht.',
|
||||
bleibt: 'Die Rechte selbst gehen nicht verloren – nur die von Hand '
|
||||
+ 'gesetzten Abweichungen. Du kannst sie jederzeit neu setzen.',
|
||||
ja: 'Auf Grundstand zurück',
|
||||
})) return;
|
||||
b.disabled = true;
|
||||
const a = await hole('/workspace/api/rechte/abweichungen', { method: 'DELETE' });
|
||||
b.disabled = false;
|
||||
|
||||
@@ -166,9 +166,14 @@
|
||||
const b = el('button', 'knopf knopf--klein', 'Durchgegangen und bestätigt');
|
||||
b.type = 'button';
|
||||
b.addEventListener('click', async () => {
|
||||
if (!confirm('Bestätigen, dass ihr das Dokument gemeinsam durchgegangen seid?\n\n'
|
||||
+ 'Sobald BEIDE bestätigt haben, ist der Nachweis endgültig und lässt sich '
|
||||
+ 'nicht mehr ändern.')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Gemeinsam durchgegangen?',
|
||||
was: 'Deine Bestätigung wird mit Datum festgehalten.',
|
||||
bleibt: 'Sobald BEIDE bestätigt haben, ist der Nachweis endgültig '
|
||||
+ 'und lässt sich nicht mehr ändern. Solange nur du bestätigt '
|
||||
+ 'hast, ist noch nichts festgeschrieben.',
|
||||
ja: 'Ja, bestätigen',
|
||||
})) return;
|
||||
b.disabled = true;
|
||||
b.textContent = 'wird gespeichert …';
|
||||
try {
|
||||
|
||||
@@ -329,7 +329,12 @@
|
||||
const wg = el('button', 'knopf-still knopf-still--warn', 'Löschen');
|
||||
wg.type = 'button';
|
||||
wg.addEventListener('click', async () => {
|
||||
if (!confirm(`„${l.name}" wirklich aus der Pipeline entfernen?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${l.name}" aus der Pipeline entfernen?`,
|
||||
was: 'Der Lead verschwindet aus der Liste, samt Notizen und Verlauf.',
|
||||
bleibt: 'Andere Leads bleiben unberührt.',
|
||||
endgueltig: true, ja: 'Entfernen',
|
||||
})) return;
|
||||
if (await schicke('/workspace/api/leads/' + l.id, 'DELETE')) { offen.delete(l.id); await laden(); }
|
||||
});
|
||||
unten.append(wg);
|
||||
@@ -344,7 +349,13 @@
|
||||
}
|
||||
|
||||
async function onboarding(l, knopf) {
|
||||
if (!confirm(`Aus „${l.name}" jetzt einen Creator mit eigenem Zugang anlegen?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `Aus „${l.name}" einen Creator mit eigenem Zugang anlegen?`,
|
||||
was: 'Es entsteht ein Konto mit eigenem Zugangscode.',
|
||||
bleibt: 'Der Lead bleibt in der Pipeline stehen und wird als '
|
||||
+ 'übernommen markiert.',
|
||||
ja: 'Creator anlegen',
|
||||
})) return;
|
||||
knopf.disabled = true;
|
||||
const r = await schicke('/workspace/api/leads/' + l.id + '/onboarding', 'POST', { name: l.name });
|
||||
knopf.disabled = false;
|
||||
|
||||
@@ -160,7 +160,14 @@
|
||||
});
|
||||
|
||||
$('sb-bild-weg').addEventListener('click', async () => {
|
||||
if (!confirm('Profilbild entfernen?')) return;
|
||||
if (!await frageNach({
|
||||
titel: 'Profilbild entfernen?',
|
||||
was: 'Dein Bild verschwindet überall – aus der Liste, aus dem Chat, '
|
||||
+ 'aus deinem Steckbrief.',
|
||||
bleibt: 'Alles andere an deinem Steckbrief bleibt stehen. Du kannst '
|
||||
+ 'jederzeit ein neues Bild hochladen.',
|
||||
ja: 'Bild entfernen',
|
||||
})) return;
|
||||
try {
|
||||
const a = await hole('/workspace/api/steckbrief/bild', { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Entfernen ging nicht.'); return; }
|
||||
|
||||
@@ -650,12 +650,18 @@
|
||||
if (!darfAnlegen()) { melde('Zugänge legt DogFather an.'); return null; }
|
||||
|
||||
const vorschlag = String(k.titel || '').split(/[\s,(–-]+/)[0] || '';
|
||||
const name = prompt(
|
||||
'Wie soll die Person im Workspace heißen?\n\n'
|
||||
+ 'Das ist der Name, den euer Team sieht. Gleich danach bekommst du '
|
||||
+ 'einmalig ihren Zugangscode – danach nie wieder.',
|
||||
vorschlag);
|
||||
if (name === null) return null; // abgebrochen
|
||||
const an = await frageNach({
|
||||
titel: 'Zugang anlegen',
|
||||
was: 'Die Person bekommt ein eigenes Konto im Workspace.',
|
||||
bleibt: 'Gleich danach siehst du einmalig ihren Zugangscode – danach '
|
||||
+ 'nie wieder. Schreib ihn dir vorher auf oder hab den Chat offen.',
|
||||
ja: 'Zugang anlegen',
|
||||
eingabe: 'Wie soll die Person im Workspace heißen?',
|
||||
eingabeWert: vorschlag,
|
||||
eingabeMin: 2,
|
||||
});
|
||||
if (!an) return null; // abgebrochen
|
||||
const name = an.eingabe;
|
||||
if (String(name).trim().length < 2) { melde('Der Name ist zu kurz.'); return null; }
|
||||
|
||||
let a;
|
||||
|
||||
@@ -181,10 +181,24 @@
|
||||
}
|
||||
|
||||
async function entscheiden(id, status) {
|
||||
const entscheid = prompt(status === 'erledigt'
|
||||
? 'Was hast du getan?\n\nEin Satz genügt – er wird zur Meldung festgehalten.'
|
||||
: 'Warum ist daran nichts dran?\n\nEin Satz genügt – er wird festgehalten.');
|
||||
if (entscheid === null) return;
|
||||
const ae = await frageNach({
|
||||
titel: status === 'erledigt' ? 'Meldung als erledigt schließen'
|
||||
: 'Meldung als unbegründet schließen',
|
||||
was: status === 'erledigt'
|
||||
? 'Die Meldung gilt danach als bearbeitet.'
|
||||
: 'Die Meldung gilt danach als geprüft, ohne Folgen für die Person.',
|
||||
bleibt: 'Dein Satz wird zur Meldung festgehalten und bleibt im Verlauf '
|
||||
+ 'lesbar. Der Beitrag selbst ändert sich dadurch nicht.',
|
||||
ja: 'Schließen',
|
||||
grund: status === 'erledigt' ? 'Was hast du getan?'
|
||||
: 'Warum ist daran nichts dran?',
|
||||
grundPflicht: true,
|
||||
platzhalter: status === 'erledigt'
|
||||
? 'z. B. Beitrag entfernt und die Person angeschrieben.'
|
||||
: 'z. B. Der Beitrag verstößt gegen keine Regel.',
|
||||
});
|
||||
if (!ae) return;
|
||||
const entscheid = ae.grund;
|
||||
const a = await hole('/workspace/api/treff/meldung/' + id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -270,24 +284,36 @@
|
||||
wird, hat die Strafe schon festgelegt, bevor er sie begründet hat.
|
||||
So steht am Anfang die Frage, was eigentlich passiert ist. */
|
||||
async function massnahme(m, art, darf) {
|
||||
const grund = prompt(art === 'hinweis'
|
||||
? `Hinweis an ${m.name} – worum geht es?`
|
||||
: `${art === 'pause' ? 'Pause' : 'Ausschluss'} für ${m.name} – warum?\n\n`
|
||||
+ 'Der Grund wird festgehalten, und die Person hat ein Recht darauf.');
|
||||
if (grund === null) return;
|
||||
|
||||
let tage;
|
||||
if (art !== 'hinweis') {
|
||||
const hinweis = darf?.frist_tage
|
||||
? `Du kannst bis zu ${darf.frist_tage} Tage geben.`
|
||||
: 'Leer lassen = dauerhaft (nur bei Ausschluss).';
|
||||
const ein = prompt(`Für wie viele Tage?\n\n${hinweis}`,
|
||||
darf?.frist_tage ? String(darf.frist_tage) : '7');
|
||||
if (ein === null) return;
|
||||
tage = ein.trim() === '' ? null : Number(ein);
|
||||
if (tage !== null && !(tage > 0)) { melde('Das ist keine Anzahl Tage.'); return; }
|
||||
if (tage === null && art === 'pause') { melde('Eine Pause braucht eine Dauer.'); return; }
|
||||
}
|
||||
const istHinweis = art === 'hinweis';
|
||||
const wieLang = istHinweis ? null : (darf?.frist_tage || 0);
|
||||
const a0 = await frageNach({
|
||||
titel: istHinweis
|
||||
? `Hinweis an ${m.name}`
|
||||
: `${art === 'pause' ? 'Pause' : 'Ausschluss'} für ${m.name}`,
|
||||
was: istHinweis
|
||||
? 'Die Person bekommt deinen Text zu lesen. Sie darf weiter mitmachen.'
|
||||
: (art === 'pause'
|
||||
? 'Die Person kann für die eingetragene Zeit nichts schreiben.'
|
||||
: 'Die Person kann nichts mehr schreiben.'),
|
||||
bleibt: 'Der Grund wird festgehalten und der Person mitgeteilt – sie hat '
|
||||
+ 'ein Recht darauf. Aufheben geht jederzeit.',
|
||||
ja: istHinweis ? 'Hinweis schicken' : 'Eintragen',
|
||||
grund: istHinweis ? 'Worum geht es?' : 'Warum?', grundPflicht: true,
|
||||
platzhalter: istHinweis
|
||||
? 'z. B. Bitte nicht mehrfach dasselbe posten.'
|
||||
: 'z. B. Wiederholte Beleidigungen trotz Hinweis.',
|
||||
zahl: istHinweis ? '' : 'Für wie viele Tage?',
|
||||
zahlWert: wieLang ? String(wieLang) : '7',
|
||||
zahlMax: wieLang || 0,
|
||||
/* Leer = dauerhaft, aber NUR beim Ausschluss -- eine Pause ohne
|
||||
Ende waere ein Ausschluss unter falschem Namen. */
|
||||
zahlLeer: art === 'ausschluss' && !wieLang,
|
||||
zahlHinweis: wieLang ? `Höchstens ${wieLang} Tage.`
|
||||
: (art === 'ausschluss' ? 'Leer lassen = dauerhaft.' : ''),
|
||||
});
|
||||
if (!a0) return;
|
||||
const grund = a0.grund;
|
||||
const tage = istHinweis ? undefined : a0.zahl;
|
||||
|
||||
const a = await hole('/workspace/api/treff/massnahme', {
|
||||
method: 'POST',
|
||||
@@ -324,7 +350,13 @@
|
||||
+ ' am ' + datum(m.zeitpunkt)],
|
||||
], laeuft && m.art !== 'hinweis' ? [
|
||||
knopf('aufheben', async () => {
|
||||
if (!confirm(`Maßnahme gegen ${m.person_name} aufheben?`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `Maßnahme gegen ${m.person_name} aufheben?`,
|
||||
was: 'Die Person darf danach sofort wieder mitmachen.',
|
||||
bleibt: 'Die Maßnahme bleibt im Verlauf sichtbar – sie wird nur '
|
||||
+ 'beendet, nicht gelöscht.',
|
||||
ja: 'Aufheben',
|
||||
})) return;
|
||||
const r = await hole('/workspace/api/treff/massnahme/' + m.id, { method: 'DELETE' });
|
||||
if (!r.ok) {
|
||||
melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.'));
|
||||
|
||||
@@ -146,8 +146,13 @@
|
||||
: `Alle ${liste.length} Punkte übernehmen`);
|
||||
allesNehmen.type = 'button';
|
||||
allesNehmen.addEventListener('click', async () => {
|
||||
if (!confirm(`Wirklich alle ${liste.length} übernehmen? `
|
||||
+ 'Sie landen als eigene Einträge in der Liste und lassen sich einzeln löschen.')) return;
|
||||
if (!await frageNach({
|
||||
titel: `Alle ${liste.length} übernehmen?`,
|
||||
was: 'Sie landen als eigene Einträge in deiner Liste.',
|
||||
bleibt: 'Die Vorlage bleibt unverändert. Jeder übernommene Eintrag '
|
||||
+ 'lässt sich einzeln wieder löschen.',
|
||||
ja: `Alle ${liste.length} übernehmen`,
|
||||
})) return;
|
||||
allesNehmen.disabled = true;
|
||||
allesNehmen.textContent = 'wird übernommen …';
|
||||
try {
|
||||
|
||||
@@ -251,7 +251,13 @@
|
||||
const weg = el('button', 'knopf-still knopf-still--warn', 'Löschen');
|
||||
weg.type = 'button';
|
||||
weg.addEventListener('click', async () => {
|
||||
if (!confirm(`„${e.titel}" wirklich löschen?\n\nDie PDF wird dabei auch vom Server entfernt.`)) return;
|
||||
if (!await frageNach({
|
||||
titel: `„${e.titel}" löschen?`,
|
||||
was: 'Der Eintrag verschwindet aus dem Wissen. Die PDF wird dabei '
|
||||
+ 'auch vom Server entfernt.',
|
||||
bleibt: 'Wer sie schon heruntergeladen hat, behält seine Kopie.',
|
||||
endgueltig: true, ja: 'Löschen',
|
||||
})) return;
|
||||
const a = await hole('/workspace/api/wissen/' + e.id, { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Löschen hat nicht geklappt.'); return; }
|
||||
await laden_();
|
||||
|
||||
Reference in New Issue
Block a user