Neuer Slogan, und Hay-Grades stehen als Leiter statt nach Haeufigkeit
Some checks failed
CI / Lint, Typen, Tests, Build (push) Successful in 11m48s
CI / Migrationen auf leerer Datenbank (push) Has been cancelled

Auf der Anmeldeseite steht jetzt "Alles im Blick. Alles Manner." statt des
bisherigen Satzes.

Die Berichte sortieren Gruppen nach der Kennzahl, gross zuerst. Bei Bereich
oder Standort ist das die Antwort auf die Frage, die der Bericht stellt. Bei
einer Leiter ist es keine: HG09 vor HG10 ist die Reihenfolge, in der die Werte
sind, und nach Haeufigkeit umgestellt liest sie sich als Zufall.

Die Sonderbehandlung, die es fuer Wochentage schon gab, ist dafuer zu einer
Liste "Dimensionen mit eigener Reihenfolge" verallgemeinert -- Wochentage und
Hay-Grades stehen darin, und die naechste Leiter ist ein Eintrag statt einer
dritten Verzweigung. Gilt damit auch fuer die Aufschluesselung innerhalb einer
Gruppe und fuer die Spalten im Berichtsexport, die dieselbe Funktion nutzen.

Der Generic Grade steht vor HG09: er ist keine Stufe, sondern ihr Fehlen, und
vor der niedrigsten faellt das am wenigsten als Aussage auf.
This commit is contained in:
2026-09-29 18:26:38 +02:00
parent 2eda038c2e
commit 3664b9a435
3 changed files with 58 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
import { ABSENCE_TYPES } from "./absence";
import { BEENDIGUNGSART_WERTE } from "./beendigung";
import { fmtName, todayIso, yearsBetweenIso } from "./format";
import { HAY_GRADE_WERTE } from "./hay-grade";
import { WOCHENTAGE } from "./wochentage";
import type { EmploymentStatus, HistoryEventType, Weekday } from "./types";
@@ -235,20 +236,43 @@ export function groupKeyFor(e: ReportEmployee, dim: GroupDimension, lookups: Org
}
}
function weekdayRank(key: string): number {
const i = WOCHENTAGE.indexOf(key as Weekday);
return i === -1 ? WOCHENTAGE.length : i;
/**
* Dimensionen, deren Werte eine eigene Reihenfolge haben.
*
* Alles andere wird nach der Kennzahl sortiert, gross zuerst — bei „Bereich"
* oder „Standort" ist das die Antwort auf die Frage, die der Bericht stellt.
* Bei einer Leiter ist es keine: Montag vor Dienstag und HG09 vor HG10 sind
* die Reihenfolge, in der die Werte *sind*, und eine nach Häufigkeit
* umgestellte Leiter liest sich als Zufall.
*
* HG09 ist die unterste Stufe, HG20 die oberste. Der „Generic Grade" (`-`)
* steht vor allen: er ist keine Stufe, sondern ihr Fehlen, und vor der
* niedrigsten ist der Platz, an dem das am wenigsten nach einer Aussage
* aussieht.
*/
const EIGENE_REIHENFOLGE: Partial<Record<GroupDimension, readonly string[]>> = {
weekday: WOCHENTAGE,
paygrade: HAY_GRADE_WERTE,
};
function rang(dim: GroupDimension, key: string): number {
const liste = EIGENE_REIHENFOLGE[dim];
if (!liste) return 0;
const i = liste.indexOf(key);
// Unbekanntes hinten, nicht vorn: ein Wert, den die Liste nicht kennt, soll
// auffallen und nicht die Leiter anführen.
return i === -1 ? liste.length : i;
}
function sortByWeekday<T extends { key: string }>(items: T[]): T[] {
return [...items].sort((a, b) => weekdayRank(a.key) - weekdayRank(b.key));
function sortiereNachReihenfolge<T extends { key: string }>(items: T[], dim: GroupDimension): T[] {
return [...items].sort((a, b) => rang(dim, a.key) - rang(dim, b.key));
}
// Reused by ReportsPageClient (split legend) and the report export route
// (split columns) to render a `weekday` split chronologically rather than
// (split columns) to render a split in the order its values have rather than
// in first-encountered order; a no-op for every other dimension.
export function sortKeysForDimension(keys: string[], dim: GroupDimension): string[] {
return dim === "weekday" ? [...keys].sort((a, b) => weekdayRank(a) - weekdayRank(b)) : keys;
return EIGENE_REIHENFOLGE[dim] ? [...keys].sort((a, b) => rang(dim, a) - rang(dim, b)) : keys;
}
// Every dimension other than `weekday` is a strict single-key partition
@@ -326,11 +350,11 @@ export function aggregateReport(
value: measureValue(sRows, measure, asOf),
count: sRows.length,
}));
row.split = split === "weekday" ? sortByWeekday(splitRows) : splitRows;
row.split = EIGENE_REIHENFOLGE[split] ? sortiereNachReihenfolge(splitRows, split) : splitRows;
}
rows.push(row);
}
return group === "weekday" ? sortByWeekday(rows) : rows.sort((a, b) => b.value - a.value);
return EIGENE_REIHENFOLGE[group] ? sortiereNachReihenfolge(rows, group) : rows.sort((a, b) => b.value - a.value);
}
export function sumValues(rows: { value: number }[]): number {