// Die Onboarding-Checkliste — ein Verzeichnis, das Oberfläche, Fortschritt // und Auswertung gemeinsam lesen. // // Die Punkte stehen hier und nicht in der Datenbank. Das ist eine Entscheidung // mit einer Kehrseite, also der Grund: eine Checkliste ist ein Firmenprozess, // kein Stammdatum. Sie ändert sich selten, und wenn, dann soll die Änderung // durch Prüfung und Protokoll gehen wie jede andere. Dafür lässt sie sich // nicht ohne Aufspielen anpassen. // // Gespeichert wird je Person und Punkt **nur die Antwort**, unter dem // Schlüssel des Punktes. Ein später entfernter Punkt lässt die alten Antworten // deshalb stehen — sie verschwinden aus der Liste, aber nicht aus den Daten, // und eine Akte von damals bleibt lesbar. /** * Welche Eingabe ein Punkt braucht — aus der Sache abgeleitet, nicht geraten: * * • `haken` — erledigt oder nicht. Der Normalfall: „Meldezettel" ist da oder * fehlt, ein Zwischenzustand ist nicht vorgesehen. * • `janein` — eine Frage mit zwei Antworten, bei der **offen** etwas anderes * heisst als **nein**. Beim Prämienanspruch stehen im Blatt zwei Kästchen * nebeneinander, und das ist kein Zufall: „nein" ist ein erhobener Befund, * „noch nicht gefragt" nicht. Ein einzelner Haken könnte das nicht sagen. * • `text` — ein Wert, der festgehalten wird. Bei den Grössen ist die Angabe * selbst der Zweck; abgehakt wäre sie wertlos. */ export type AufgabenArt = "haken" | "janein" | "text"; export type AufgabenPunkt = { /** Bleibt stabil — er steht in den Daten. */ key: string; label: string; art: AufgabenArt; /** Zusatz unter der Beschriftung, wo die Bezeichnung allein nicht reicht. */ hinweis?: string; }; export type AufgabenGruppe = { key: string; label: string; punkte: readonly AufgabenPunkt[]; }; export const ONBOARDING_GRUPPEN: readonly AufgabenGruppe[] = [ { key: "aufgaben", label: "Zu erledigen", punkte: [ { key: "dienstzettel", label: "Dienstzettel / Dienstvertrag", art: "haken" }, { key: "dienstzettel_retour", label: "Dienstzettel / Dienstvertrag retour", art: "haken" }, { key: "onboarding_gestartet", label: "Onboarding gestartet", art: "haken" }, { key: "lgv_e9", label: "LGV Anlage / E9", art: "haken" }, { key: "interflex", label: "Interflex Anlage", art: "haken" }, { key: "eurest", label: "Anmeldung EUREST", art: "haken" }, { key: "oegk", label: "Anmeldung ÖGK", art: "haken" }, { key: "webclient", label: "WebClient", art: "haken" }, { key: "datensatzberechtigung", label: "Datensatzberechtigung Führungskraft", art: "haken" }, { key: "praemienanspruch", label: "Prämienanspruch", art: "janein" }, { key: "webreise", label: "Webreise Benutzer", art: "haken", hinweis: "HM 13, Seite 2 Punkt 15, „J“, ganz am Ende mit einfügen", }, ], }, { key: "unterlagen", label: "Unterlagen", punkte: [ { key: "passkopie", label: "Passkopie / Ausweis", art: "haken" }, { key: "arbeitsbewilligung", label: "Arbeitsbewilligung", art: "haken", hinweis: "Nur bei Staatsbürgerschaft ausserhalb EU/EWR und Schweiz", }, { key: "meldezettel", label: "Meldezettel", art: "haken" }, { key: "bankverbindung", label: "Bankverbindung", art: "haken" }, { key: "ecard", label: "E-Card", art: "haken" }, { key: "strafregisterauszug", label: "Strafregisterauszug", art: "haken" }, { key: "salmonellenbefund", label: "Salmonellenbefund", art: "haken" }, { key: "personaldatenblatt", label: "Personaldatenblatt", art: "haken" }, { key: "cv", label: "CV", art: "haken" }, { key: "goodies", label: "Goodies", art: "haken" }, { key: "willkommensmappe", label: "Willkommensmappe", art: "haken" }, ], }, { key: "vermerke", label: "Vermerke — Grösse", punkte: [ { key: "groesse_schuhe", label: "Schuhe", art: "text" }, { key: "groesse_tshirt", label: "T-Shirt", art: "text" }, { key: "groesse_hose", label: "Hose", art: "text" }, ], }, ] as const; export const ONBOARDING_PUNKTE: readonly AufgabenPunkt[] = ONBOARDING_GRUPPEN.flatMap((g) => g.punkte); const NACH_KEY = new Map(ONBOARDING_PUNKTE.map((p) => [p.key, p])); export function punktZu(key: string): AufgabenPunkt | undefined { return NACH_KEY.get(key); } /** Der gespeicherte Stand eines Punktes. */ export type AufgabenStand = { item_key: string; erledigt: boolean; /** Bei `janein` „ja"/„nein", bei `text` die Angabe, bei `haken` leer. */ wert: string | null; kommentar: string | null; updated_at: string | null; updated_by_name: string | null; }; /** * Ob ein Punkt als erledigt zählt — je Art verschieden, und deshalb an einer * Stelle statt an dreien. * * Bei `janein` zählt auch „nein" als erledigt: die Frage ist beantwortet, und * genau das ist die Aufgabe. Bei `text` ist die Angabe selbst die Erledigung; * ein leerer Text ist keine. */ export function istErledigt(punkt: AufgabenPunkt, stand: AufgabenStand | undefined): boolean { if (!stand) return false; if (punkt.art === "haken") return stand.erledigt; return (stand.wert ?? "").trim() !== ""; } export type Fortschritt = { erledigt: number; gesamt: number; offen: string[] }; /** * Wie weit die Liste ist. `offen` nennt die Beschriftungen, nicht die * Schlüssel — die Zahl allein sagt nicht, worauf noch gewartet wird. */ export function fortschritt(staende: AufgabenStand[]): Fortschritt { const karte = new Map(staende.map((s) => [s.item_key, s])); const offen = ONBOARDING_PUNKTE.filter((p) => !istErledigt(p, karte.get(p.key))); return { erledigt: ONBOARDING_PUNKTE.length - offen.length, gesamt: ONBOARDING_PUNKTE.length, offen: offen.map((p) => p.label), }; } /** Was als Wert erlaubt ist — dieselbe Prüfung wie in der Datenbank. */ export function wertIstGueltig(punkt: AufgabenPunkt, wert: string | null): boolean { if (punkt.art === "janein") return wert === null || wert === "ja" || wert === "nein"; if (punkt.art === "haken") return wert === null; return true; }