Die Kachel verwies auf eine Adresse, die die Liste nicht lesen konnte
Die neue Kachel "Aktives Dienstverhaeltnis" verlinkte auf
?status=Aktiv&status=Karenz. Die Mitarbeiterliste liest den Parameter aber
als *eine* Zeichenkette und trennt selbst an Kommas — zweimal uebergeben
macht Next daraus ein Array, und `.split(",")` lief dagegen. Sichtbar war
nur "Diese Ansicht konnte nicht geladen werden".
Die Kachel schreibt jetzt status=Aktiv,Karenz. Dazu glaettet die Seite alle
ihre Parameter: eine Adresse kommt nicht nur aus der eigenen Anwendung, sie
steht in Lesezeichen und in E-Mails, und ?q=a&q=b haette sie genauso
gefaellt.
Zwei Anmerkungen von Max:
* Die Reihenfolge der Wochentage wurde beim Speichern mitgenommen — "Mo,
Di" und "Di, Mo" waren zwei Werte fuer dieselbe Aussage. Da
change_employee_data die Arbeitstage als zusammengefuegte Zeichenkette
vergleicht, erzeugte jedes Nachsehen und Wiederherstellen eine
Vertragsaenderung in der Akte und einen Protokolleintrag — ueber nichts.
Jetzt sortiert gespeichert (lib/wochentage.ts, an einer Stelle statt in
vier Kopien), auch im Massenimport. Der Bestand richtet sich beim
naechsten Speichern von selbst.
* "Beguenstigt behindert" steht jetzt als eingerueckter Unterpunkt des
Kuendigungsschutzes statt als eigener Block daneben. In der Datenbank
bleiben es getrennte Felder, und das mit Absicht: eine Kopplung liesse
jede Korrektur am Personenkreis scheitern, solange der Grad noch
dransteht.
This commit is contained in:
@@ -26,6 +26,22 @@ import type { EmploymentStatus } from "@/lib/types";
|
||||
|
||||
const PAGE_SIZE = 15;
|
||||
|
||||
/**
|
||||
* Die Parameter, nachdem sie geglättet sind — je einer, oder keiner.
|
||||
*
|
||||
* Was Next tatsächlich liefert, steht in RohParams: derselbe Name zweimal in
|
||||
* der Adresse wird dort zu einem Array. Diese Seite hat das nicht erwartet
|
||||
* und ist daran gescheitert — eine Kachel der Übersicht verwies auf
|
||||
* `?status=Aktiv&status=Karenz`, und `params.status.split(",")` lief gegen
|
||||
* ein Array. Sichtbar war davon nur „Diese Ansicht konnte nicht geladen
|
||||
* werden" mit einer Fehlerkennung.
|
||||
*
|
||||
* Die Kachel ist berichtigt (sie schreibt jetzt `status=Aktiv,Karenz`), aber
|
||||
* eine Adresse kommt nicht nur aus der eigenen Anwendung: sie steht in
|
||||
* Lesezeichen, in Verknüpfungen, in E-Mails. Deshalb wird hier geglättet
|
||||
* statt sich darauf zu verlassen, dass niemand zweimal denselben Namen
|
||||
* schreibt.
|
||||
*/
|
||||
type SearchParams = {
|
||||
q?: string;
|
||||
division?: string;
|
||||
@@ -36,8 +52,27 @@ type SearchParams = {
|
||||
page?: string;
|
||||
};
|
||||
|
||||
type RohParams = Record<string, string | string[] | undefined>;
|
||||
|
||||
/** Der erste Wert eines Parameters — auch wenn er mehrfach in der Adresse steht. */
|
||||
function einWert(wert: string | string[] | undefined): string | undefined {
|
||||
return Array.isArray(wert) ? wert[0] : wert;
|
||||
}
|
||||
|
||||
function glaetten(roh: RohParams): SearchParams {
|
||||
return {
|
||||
q: einWert(roh.q),
|
||||
division: einWert(roh.division),
|
||||
status: einWert(roh.status),
|
||||
location: einWert(roh.location),
|
||||
sort: einWert(roh.sort),
|
||||
dir: einWert(roh.dir),
|
||||
page: einWert(roh.page),
|
||||
};
|
||||
}
|
||||
|
||||
type EmployeesPageProps = {
|
||||
searchParams: Promise<SearchParams>;
|
||||
searchParams: Promise<RohParams>;
|
||||
};
|
||||
|
||||
function pageHref(params: SearchParams, page: number): string {
|
||||
@@ -72,7 +107,7 @@ function sortHref(params: SearchParams, feld: Sortierfeld, aktuell: Sortierfeld,
|
||||
}
|
||||
|
||||
export default async function EmployeesPage({ searchParams }: EmployeesPageProps) {
|
||||
const params = await searchParams;
|
||||
const params = glaetten(await searchParams);
|
||||
const page = Math.max(1, Number(params.page ?? "1") || 1);
|
||||
const sortFeld = parseFeld(params.sort);
|
||||
const sortRichtung = parseRichtung(params.dir);
|
||||
|
||||
@@ -184,7 +184,10 @@ export default async function DashboardPage({
|
||||
label: "Aktives Dienstverhältnis",
|
||||
value: activeCount + karenzCount,
|
||||
tone: "default",
|
||||
href: "/employees?status=Aktiv&status=Karenz",
|
||||
// Mit Komma, nicht zweimal `status=`: die Liste liest den Parameter als
|
||||
// *eine* Zeichenkette und trennt selbst. Zweimal übergeben macht Next
|
||||
// daraus ein Array, und die Seite scheiterte an `.split(",")`.
|
||||
href: "/employees?status=Aktiv,Karenz",
|
||||
},
|
||||
// Geplante Eintritte ohne Zeitgrenze — anders als die Karte „Anstehend"
|
||||
// darunter, die nur den eingestellten Vorschauzeitraum zeigt. Ein
|
||||
|
||||
@@ -6,6 +6,7 @@ import { subtreeOf } from "@/lib/org";
|
||||
import { loadPlacements, loadReportingLineMap } from "@/lib/placement";
|
||||
import { LEERE_CRITERIA, parseCriteria, passtImSpeicher } from "@/lib/report-criteria";
|
||||
import { derivedStatusFilter } from "@/lib/employee-status-filter";
|
||||
import { sortiereWochentage } from "@/lib/wochentage";
|
||||
import { deriveStatusAsOf, parseIsoDateParam, parseStatuses, type OrgLookups } from "@/lib/reports";
|
||||
import { applyCriteria, loadDependentsCounts, loadOrgLookups, type ReportFilters } from "@/lib/reports-data";
|
||||
import { requireHrUser } from "@/lib/auth/require-hr";
|
||||
@@ -114,8 +115,6 @@ export async function GET(request: NextRequest) {
|
||||
return new NextResponse(new Blob([body as BlobPart]), { headers: exportResponseHeaders(filename, format) });
|
||||
}
|
||||
|
||||
const WEEKDAY_ORDER: Weekday[] = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
|
||||
function employeeExportColumns(
|
||||
lookups: OrgLookups,
|
||||
managerName: Map<string, string>,
|
||||
@@ -148,9 +147,11 @@ function employeeExportColumns(
|
||||
{ header: "Leitungsplanstelle", get: (e) => e.is_chief },
|
||||
{ header: "Beschäftigungsausmaß", get: (e) => e.employment_type },
|
||||
{ header: "Wochenstunden", get: (e) => e.weekly_hours },
|
||||
// work_days is stored in click order (see RoleEmploymentFields), not
|
||||
// guaranteed chronological — re-sort Mo→So for the export.
|
||||
{ header: "Arbeitstage", get: (e) => [...e.work_days].sort((a, b) => WEEKDAY_ORDER.indexOf(a as Weekday) - WEEKDAY_ORDER.indexOf(b as Weekday)).join(", ") },
|
||||
// Seit lib/wochentage.ts wird sortiert gespeichert; der Bestand kann aber
|
||||
// noch unsortierte Zeilen aus der Zeit davor tragen. Hier bleibt es
|
||||
// deshalb stehen — im Export kostet es nichts und macht die Spalte
|
||||
// unabhängig davon, wann eine Zeile zuletzt gespeichert wurde.
|
||||
{ header: "Arbeitstage", get: (e) => sortiereWochentage(e.work_days).join(", ") },
|
||||
{ header: "Vertragsart", get: (e) => e.contract_type },
|
||||
{ header: "Befristet bis", get: (e) => e.contract_end_date, kind: "date" },
|
||||
{ header: "Beschäftigtengruppe", get: (e) => e.worker_type },
|
||||
|
||||
Reference in New Issue
Block a user