/* ===================================================================== Wie lange geht das schon so? (15.09.2026) Stufe 9 aus dem Plan: „Eskalationsstufe sichtbar". --------------------------------------------------------------------- DER MANGEL, GEMESSEN Überfällig war bisher ein Schalter: an oder aus. Eine Aufgabe, deren Frist gestern lief, sah genauso aus wie eine, die seit drei Wochen liegt — dasselbe Wort, dieselbe Farbe, derselbe Platz. Damit verliert das Wort seine Bedeutung. Wenn auf einem Brett zwanzig Karten „überfällig" stehen, sagt keine davon mehr etwas; man überliest sie alle, auch die, die seit einem Monat blockiert. --------------------------------------------------------------------- ZWEI VERSCHIEDENE FRAGEN — UND SIE DÜRFEN NICHT VERMISCHT WERDEN FRIST ÜBERSCHRITTEN Es war etwas versprochen, und der Termin ist vorbei. Eine Tatsache. NICHTS PASSIERT Es gibt keine Frist, aber seit Wochen hat niemand die Karte angefasst. Eine Beobachtung. Die erste ist härter als die zweite, und sie steht deshalb vorn. Eine Aufgabe ohne Frist ist nicht „zu spät" — sie ist nur still. Beides in einen Topf zu werfen hieße, jemandem einen gebrochenen Termin vorzuwerfen, den er nie zugesagt hat. --------------------------------------------------------------------- DAS IST KEINE MAHNUNG Dieselbe Haltung wie bei der Standzeit im Talente-Trichter und bei „Was auf dich wartet": Die Zahl ist eine Auskunft über UNS, nicht ein Vorwurf an eine Person. Deshalb steht hier auch nirgends ein Name, und deshalb heißt die höchste Stufe „hängt" und nicht „versäumt". --------------------------------------------------------------------- DIE SCHWELLEN SIND GESETZT, NICHT GEMESSEN Ehrlich benannt: 3 und 8 Tage sind ein begründeter Anfang, kein Messergebnis. Die Überlegung dahinter: unter 3 Tagen Ein Wochenende reicht dafür. Wer Freitag eine Frist reißt und Montag liefert, hat nichts versäumt — nur den Kalender nicht gelesen. ab 3 Tagen Länger als ein Wochenende. Jetzt ist es keine Verzögerung mehr, sondern ein Zustand. ab 8 Tagen Über eine Woche. Wer so lange nicht dazu kommt, kommt ohne Hilfe auch nächste Woche nicht dazu. Wenn sich zeigt, dass die Stufen zu früh oder zu spät greifen, wird hier eine Zahl geändert — an einer Stelle, und die Oberfläche zieht mit, weil sie das Wort vom Server bekommt. ===================================================================== */ /** Die Stufen, von der mildesten zur härtesten. * * `ab` ist die Anzahl Tage, ab der die Stufe gilt. Die Liste wird von * hinten durchsucht — die härteste zutreffende gewinnt. */ export const STUFEN = [ { schluessel: "faellig", ab: 1, rang: 1 }, { schluessel: "liegt", ab: 3, rang: 2 }, { schluessel: "haengt", ab: 8, rang: 3 }, ]; /** Ohne Frist: ab wie vielen Tagen ohne Bewegung es auffällt. * * 14 Tage — zwei Wochen. Kürzer wäre bei einer Aufgabe ohne Termin * Panikmache: Nicht alles, was daliegt, ist liegengeblieben. */ export const STILL_TAGE = 14; const TAG = 86400000; /** Ganze Tage zwischen zwei Datumsangaben (a später als b). */ function tageSeit(zeitpunkt, jetzt) { const t = Date.parse(zeitpunkt); if (!Number.isFinite(t)) return null; return Math.floor((jetzt - t) / TAG); } /** Wie lange geht das mit dieser Aufgabe schon so? * * @param {object} a eine Zeile aus `aufgaben` * @param {number} [jetzt] Zeitpunkt in ms (für die Prüfung setzbar) * @returns {{stufe: string, tage: number, grund: string, wort: string}|null} * null, wenn nichts anliegt */ export function eskalation(a, jetzt = Date.now()) { if (!a) return null; /* Erledigtes und Abgebrochenes eskaliert nicht. Eine Karte, die fertig ist, war vielleicht zu spät fertig -- aber sie liegt niemandem mehr im Weg, und eine Warnung daran wäre nur noch ein Vorwurf. */ if (a.status === "erledigt" || a.status === "abgebrochen") return null; /* ---- 1. Die Frist. Eine Tatsache, und sie steht vorn. ---- */ if (a.frist) { /* Auf den ANFANG des Tages nach der Frist gerechnet: Wer bis zum 15. Zeit hat, ist am 15. nicht zu spät. Die Grenze ist Mitternacht danach, nicht der Moment des Nachsehens. */ const ende = Date.parse(a.frist + "T23:59:59"); if (Number.isFinite(ende) && jetzt > ende) { const tage = Math.max(1, Math.floor((jetzt - ende) / TAG) + 1); const treffer = [...STUFEN].reverse().find((s) => tage >= s.ab); if (treffer) { return { stufe: treffer.schluessel, rang: treffer.rang, tage, grund: "frist", wort: treffer.schluessel === "faellig" ? "überfällig" : (treffer.schluessel === "liegt" ? `liegt seit ${tage} Tagen` : `hängt seit ${tage} Tagen`), }; } } /* MIT FRIST UND NOCH NICHT ÜBERSCHRITTEN: Hier endet es. Eine Karte, deren Termin erst nächste Woche ist, ist nicht „still" -- sie ist eingeplant. */ return null; } /* ---- 2. Ohne Frist: seit wann hat niemand sie angefasst? ---- */ const zuletzt = a.geaendert || a.erstellt; const still = zuletzt ? tageSeit(zuletzt, jetzt) : null; if (still !== null && still >= STILL_TAGE) { return { stufe: "still", rang: 1, tage: still, grund: "keine_bewegung", wort: `seit ${still} Tagen unberührt`, }; } return null; } /** Setzt `eskalation` an jede Zeile einer Liste. * * AM SERVER UND NICHT IM BROWSER: Startseite, Aufgabenbrett und * Übersicht lesen dieselbe Liste. Drei Stellen, die dieselbe Frage * selbst beantworten, geben irgendwann drei Antworten -- genau der * Fehler, der im Projekt schon bei der Rollenreihenfolge und beim Wort * „Agentur" passiert ist. */ export function eskalationSetzen(liste, jetzt = Date.now()) { for (const a of liste || []) a.eskalation = eskalation(a, jetzt); return liste; }