/* =================================================================== Start-Check / Erstanalyse (Konzept, Seite 5). Bewusst ohne Speichern-Knopf: Jede Bewertung wird sofort gesichert. Eine Erstanalyse geht man im Gespraech durch -- da will niemand am Ende noch daran denken, unten auf einen Knopf zu druecken. Und: Jeder Punkt mit Handlungsbedarf bietet direkt an, daraus eine Aufgabe zu machen. "Ausgangslage verstehen UND daraus konkrete Arbeitspakete bauen" -- der zweite Teil ist der, der zaehlt. =================================================================== */ (() => { 'use strict'; const $ = (id) => document.getElementById(id); const STUFEN = [ { wert: 'ok', name: 'Passt' }, { wert: 'mittel', name: 'Ausbaufähig' }, { wert: 'handlung', name: 'Handlungsbedarf' }, ]; let ich = null; let daten = null; let aktuell = null; const el = (tag, klasse, text) => { const k = document.createElement(tag); if (klasse) k.className = klasse; if (text !== undefined) k.textContent = text; return k; }; const melde = (t) => { $('fehler').textContent = t || ''; }; async function hole(pfad, optionen = {}) { const a = await fetch(pfad, { credentials: 'same-origin', ...optionen }); if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); } return a; } async function setze(feld, punkt, aenderung) { try { const a = await hole('/workspace/api/startcheck/' + aktuell, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ feld, punkt, ...aenderung }), }); if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Konnte nicht gespeichert werden.'); return false; } melde(''); return true; } catch { return false; } } /* ---------- Ein Punkt ---------------------------------------------------- */ function punktZeile(feld, p) { const k = el('div', 'punkt'); if (p.bewertung) k.dataset.bewertung = p.bewertung; const kopf = el('div', 'punkt__kopf'); const links = el('div'); links.append(el('h3', 'punkt__name', p.name)); links.append(el('p', 'punkt__hinweis', p.hinweis)); kopf.append(links); /* Bewertung */ if (daten.darf_aendern) { const wahl = el('div', 'stufen'); wahl.setAttribute('role', 'group'); wahl.setAttribute('aria-label', 'Bewertung für ' + p.name); for (const s of STUFEN) { const b = el('button', 'stufe-knopf', s.name); b.type = 'button'; b.dataset.wert = s.wert; if (p.bewertung === s.wert) b.dataset.aktiv = 'ja'; b.addEventListener('click', async () => { /* Nochmal auf dieselbe Stufe = zurücknehmen. */ const neu = p.bewertung === s.wert ? null : s.wert; if (await setze(feld.schluessel, p.schluessel, { bewertung: neu, notiz: p.notiz })) { await laden(); } }); wahl.append(b); } kopf.append(wahl); } else if (p.bewertung) { const m = el('span', 'stufe-marke', STUFEN.find((s) => s.wert === p.bewertung)?.name); m.dataset.wert = p.bewertung; kopf.append(m); } k.append(kopf); /* Notiz erst, wenn bewertet wurde. Sechzehn offene Textfelder machen aus einer Checkliste eine Wand -- und vor der Bewertung hat man ohnehin nichts zu notieren. */ if (daten.darf_aendern && (p.bewertung || p.notiz)) { const n = el('textarea', 'punkt__notiz'); n.rows = 2; n.maxLength = 2000; n.placeholder = 'Was ist dir aufgefallen?'; n.value = p.notiz || ''; let warte = null; n.addEventListener('input', () => { clearTimeout(warte); warte = setTimeout(() => { setze(feld.schluessel, p.schluessel, { bewertung: p.bewertung, notiz: n.value }); p.notiz = n.value; }, 700); }); k.append(n); } else if (p.notiz) { k.append(el('p', 'punkt__notiz-lesen', p.notiz)); } /* Aus dem Befund ein Arbeitspaket machen. */ if (daten.darf_aendern && (p.bewertung === 'handlung' || p.bewertung === 'mittel')) { k.append(aufgabenTeil(feld, p)); } if (p.von && p.geaendert) { k.append(el('p', 'punkt__quelle', `${p.von} · ${p.geaendert.slice(0, 10).split('-').reverse().join('.')}`)); } return k; } function aufgabenTeil(feld, p) { const box = el('div', 'paket'); const auf = el('button', 'knopf-still', '+ Daraus eine Aufgabe machen'); auf.type = 'button'; const form = el('form', 'paket__form'); form.hidden = true; const titel = el('input', 'paket__titel'); titel.type = 'text'; titel.maxLength = 160; titel.placeholder = 'Was genau ist zu tun?'; const frist = el('input', 'paket__frist'); frist.type = 'date'; frist.title = 'Frist (optional)'; const ok = el('button', 'knopf knopf--klein', 'Anlegen'); ok.type = 'submit'; form.append(titel, frist, ok); const stand = el('span', 'leise'); auf.addEventListener('click', () => { form.hidden = false; auf.hidden = true; /* Ein sinnvoller Vorschlag statt eines leeren Feldes -- die Notiz ist meistens schon die halbe Aufgabe. */ titel.value = p.notiz ? p.notiz.split(/[.\n]/)[0].trim().slice(0, 160) : p.name; titel.focus(); titel.select(); }); form.addEventListener('submit', async (e) => { e.preventDefault(); ok.disabled = true; try { const a = await hole(`/workspace/api/startcheck/${aktuell}/aufgabe`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ feld: feld.schluessel, punkt: p.schluessel, titel: titel.value, frist: frist.value, }), }); if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } const r = await a.json(); form.hidden = true; stand.textContent = `Im Aufgabenbrett angelegt (Priorität ${r.prioritaet}) ✓`; setTimeout(() => { stand.textContent = ''; auf.hidden = false; }, 4000); } catch { /* umgeleitet */ } finally { ok.disabled = false; } }); box.append(auf, form, stand); return box; } /* ---------- Zeichnen ----------------------------------------------------- */ function zeichne() { const ziel = $('felder'); ziel.textContent = ''; ziel.setAttribute('aria-busy', 'false'); for (const f of daten.felder) { const s = el('section', 'feld-block'); const kopf = el('div', 'feld-block__kopf'); kopf.append(el('h2', 'feld-block__titel', f.name)); const fertig = f.punkte.filter((p) => p.bewertung).length; const z = el('span', 'feld-block__zahl', `${fertig}/${f.punkte.length}`); if (fertig === f.punkte.length) z.dataset.fertig = 'ja'; kopf.append(z); s.append(kopf); const box = el('div', 'feld-block__punkte'); for (const p of f.punkte) box.append(punktZeile(f, p)); s.append(box); ziel.append(s); } /* Fortschritt */ $('fortschritt').hidden = false; $('f-zahl').textContent = `${daten.geprueft} von ${daten.gesamt}`; const anteil = Math.round((daten.geprueft / daten.gesamt) * 100); $('f-balken').style.width = anteil + '%'; const aussen = $('f-balken-aussen'); aussen.setAttribute('aria-valuenow', String(daten.geprueft)); aussen.setAttribute('aria-valuemax', String(daten.gesamt)); const h = $('f-hinweis'); if (daten.geprueft === 0) { h.textContent = daten.darf_aendern ? 'Noch nichts geprüft. Am besten im Gespräch durchgehen – gespeichert wird sofort.' : 'Die Erstanalyse wurde noch nicht ausgefüllt.'; } else if (daten.handlungsbedarf) { h.textContent = `${daten.handlungsbedarf} ${daten.handlungsbedarf === 1 ? 'Punkt braucht' : 'Punkte brauchen'} Handlung` + (daten.darf_aendern ? ' – daraus lassen sich direkt Aufgaben machen.' : '.'); } else if (daten.geprueft === daten.gesamt) { h.textContent = 'Vollständig geprüft, nichts Dringendes offen.'; } else { h.textContent = ''; } $('fortschritt').dataset.stufe = daten.handlungsbedarf ? 'handlung' : daten.geprueft === daten.gesamt ? 'fertig' : 'laeuft'; } async function laden() { try { const a = await hole('/workspace/api/startcheck/' + aktuell); if (a.status === 404) { $('felder').textContent = ''; $('felder').append(el('p', 'leer-hinweis', 'Dieser Start-Check ist für dich nicht sichtbar.')); return; } if (!a.ok) { melde('Start-Check konnte nicht geladen werden.'); return; } daten = await a.json(); zeichne(); } catch { /* umgeleitet */ } } /* ---------- Start --------------------------------------------------------- */ (async () => { try { const a = await hole('/workspace/api/ich'); if (!a.ok) { location.assign('/workspace/'); return; } ich = await a.json(); } catch { return; } $('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle); let liste; try { liste = await (await hole('/workspace/api/startcheck/creator')).json(); } catch { return; } if (!liste.creator.length) { $('felder').textContent = ''; $('felder').append(el('p', 'leer-hinweis', ich.rolle === 'scout' ? 'Dir ist noch kein Creator zugeteilt. DogFather legt fest, wen du betreust.' : 'Es gibt noch keinen Creator.')); return; } if (!liste.eigen) { $('creator-block').hidden = false; for (const c of liste.creator) { const o = document.createElement('option'); o.value = String(c.id); o.textContent = c.name; $('f-creator').append(o); } $('f-creator').addEventListener('change', () => { aktuell = Number($('f-creator').value); laden(); }); } else { $('unterzeile').textContent = 'Die Erstanalyse zu deinem Kanal – Grundlage für deinen 90-Tage-Plan.'; } aktuell = liste.creator[0].id; await laden(); })(); })();