Stufe 9 aus dem Plan: "Eskalationsstufe sichtbar". Damit ist Stufe 9
bis auf einen Punkt durch.
DER MANGEL, GEMESSEN
"Ueberfaellig" war 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. Stehen auf einem Brett zwanzig
Karten "ueberfaellig", sagt keine davon mehr etwas; man ueberliest sie
alle, auch die, die seit einem Monat blockiert.
ZWEI VERSCHIEDENE FRAGEN, DIE NICHT VERMISCHT WERDEN DUERFEN
FRIST UEBERSCHRITTEN Es war etwas versprochen, der Termin ist
vorbei. Eine Tatsache.
faellig (ab 1 Tag) -> liegt (ab 3) -> haengt (ab 8)
NICHTS PASSIERT Keine Frist, aber seit 14 Tagen hat niemand
die Karte angefasst. Eine Beobachtung.
Die erste ist haerter und steht vorn. Eine Aufgabe ohne Frist ist nicht
"zu spaet" -- sie ist nur still. Beides in einen Topf zu werfen hiesse,
jemandem einen gebrochenen Termin vorzuwerfen, den er nie zugesagt hat.
Deshalb ist "still" auch farblos gezeichnet, gestrichelt statt gefuellt.
DAS IST KEINE MAHNUNG
Dieselbe Haltung wie bei der Standzeit im Talente-Trichter: Die Zahl ist
eine Auskunft ueber UNS, kein Vorwurf an eine Person. Deshalb steht
nirgends ein Name, und die hoechste Stufe heisst "haengt", nicht
"versaeumt".
DIE SCHWELLEN SIND GESETZT, NICHT GEMESSEN -- und das steht so im Code:
unter 3 Tagen reicht ein Wochenende; ab 3 ist es ein Zustand; ab 8 (ueber
eine Woche) kommt es ohne Hilfe auch naechste Woche nicht dazu. Aendert
sich eine Zahl, aendert sie sich an einer Stelle, und die Oberflaeche
zieht mit -- sie bekommt das WORT vom Server.
AM SERVER GERECHNET. Startseite, Brett und Uebersicht 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.
DIE FARBE IST NICHT DIE AUSKUNFT: In jedem Kaestchen steht ein Wort
("liegt seit 5 Tagen"), nicht nur ein roterer Punkt.
PRUEFUNGEN: pruef-eskalation neu mit 40, davon 6 im Browser. Gemessen
wird an den GRENZEN, nicht in der Mitte -- jede Schwelle dreimal: einen
Tag davor, genau darauf, einen Tag danach. Die wichtigste ist die erste:
am Tag der Frist ist nichts ueberfaellig. Wer bis zum 15. Zeit hat, ist
am 15. puenktlich; eine Eskalation, die einen Tag zu frueh anschlaegt,
verliert genau das Vertrauen, das sie braucht.
Die Pruefung benutzt einen FESTEN Zeitpunkt als Eingabe, keine
Wanderuhr -- sonst misst sie irgendwann das Gegenteil.
Co-Authored-By: Claude Opus 5 <[email protected]>
152 lines
6.1 KiB
JavaScript
152 lines
6.1 KiB
JavaScript
/* =====================================================================
|
|
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;
|
|
}
|