Put the onboarding checklist where the file is
The list existed on paper: one printed sheet per entry, twenty-five
boxes. What is on it is known only to whoever holds the sheet — it
cannot be searched, cannot be covered for while someone is away, and
says nothing about who ticked what.
Not every box on the sheet is a checkbox, and the differences carry
meaning, so the field kind is derived from the thing rather than
flattened:
Haken — the normal case. The Meldezettel is there or it is not.
Ja/Nein — Prämienanspruch had *two* boxes on the sheet, and that is
not decoration: "nein" is a finding, "not asked yet" is not.
One checkbox cannot say both.
Text — shoe, shirt and trouser size. The value is the point;
ticked off it would be worthless.
Every item takes a comment, and every item records who last touched it
and when — the part the sheet could never do.
Saved on click, not on submit. A checklist is worked through over days,
between other things; a save button at the end is where half a morning
goes missing.
The items live in lib/onboarding.ts, not in a table: a checklist is a
company process, not a master record. Stored per person is only the
answer, under the item's key — so an item dropped later leaves its old
answers standing instead of taking them along, and a file from back then
stays readable.
A list is created by hire and rehire, in the same transaction as the
hire itself: a hire without a checklist would be a half-recorded hire.
Rehire only adds what is missing and never clears an old tick — what
genuinely has to be redone is HR's call, and a program deciding it would
be guessing. People hired before this feature have no list and get a
button to start one.
Checked against the real database end to end: hire creates 25 open
items; checkbox, ja/nein, size and comment all land; a comment-only edit
leaves the tick alone; rehire tops the list up and keeps what was done.
The probe employee was removed afterwards — audit rows first, since the
log has no delete policy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { revalidatePath } from "next/cache";
|
||||
import { currentUserId } from "@/lib/auth/session";
|
||||
import { withUser } from "@/lib/db";
|
||||
import { callFunction, runMutation, type ActionResult, type MutationFn } from "@/lib/db/rpc";
|
||||
import { ONBOARDING_PUNKTE } from "@/lib/onboarding";
|
||||
import type { CollectiveAgreement, DienstwagenArt, NoteCategory, RelationshipType, Weekday, WorkerType } from "@/lib/supabase/types";
|
||||
|
||||
async function callRpc(fn: MutationFn, payload: Record<string, unknown>, revalidate: string[]): Promise<ActionResult> {
|
||||
@@ -67,9 +68,14 @@ export async function hireEmployee(payload: {
|
||||
// Einzige Mutation, deren Rückgabewert gebraucht wird: die neue
|
||||
// Personen-Kennung, damit die Oberfläche direkt auf die Akte springen kann.
|
||||
try {
|
||||
const employeeId = await withUser(await currentUserId(), (tx) =>
|
||||
callFunction(tx, "hire_employee", payload as Record<string, unknown>)
|
||||
);
|
||||
const employeeId = await withUser(await currentUserId(), async (tx) => {
|
||||
const id = (await callFunction(tx, "hire_employee", payload as Record<string, unknown>)) as string;
|
||||
// In derselben Transaktion: eine Einstellung ohne Checkliste wäre eine
|
||||
// halb erfasste Einstellung, und sie später nachzureichen hiesse, dass
|
||||
// jemand daran denken muss.
|
||||
await callFunction(tx, "start_onboarding", { employee_id: id, item_keys: ONBOARDING_PUNKTE.map((x) => x.key) });
|
||||
return id;
|
||||
});
|
||||
revalidatePath("/employees");
|
||||
revalidatePath("/");
|
||||
revalidatePath("/positions");
|
||||
@@ -160,7 +166,44 @@ export async function rehireEmployee(payload: {
|
||||
rehire_date: string;
|
||||
position_id: string;
|
||||
}): Promise<ActionResult> {
|
||||
return callRpc("rehire_employee", payload, [`/employees/${payload.employee_id}`, "/employees", "/"]);
|
||||
try {
|
||||
await withUser(await currentUserId(), async (tx) => {
|
||||
await callFunction(tx, "rehire_employee", payload as unknown as Record<string, unknown>);
|
||||
// Auch bei der Wiedereinstellung: Dienstzettel, Bankverbindung und
|
||||
// E-Card sind wieder zu erledigen. Die Punkte von damals stehen noch da
|
||||
// und bleiben stehen — start_onboarding legt nur an, was fehlt, statt
|
||||
// einen alten Haken zu löschen. Was wirklich neu zu tun ist, entscheidet
|
||||
// HR an der Liste; ein Programm an ihrer Stelle würde raten.
|
||||
await callFunction(tx, "start_onboarding", {
|
||||
employee_id: payload.employee_id,
|
||||
item_keys: ONBOARDING_PUNKTE.map((x) => x.key),
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
return { success: false, error: err instanceof Error ? err.message : "Unbekannter Fehler." };
|
||||
}
|
||||
for (const path of [`/employees/${payload.employee_id}`, "/employees", "/"]) revalidatePath(path);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/** Einen Punkt der Checkliste festhalten — Haken, Wert oder Kommentar. */
|
||||
export async function setOnboardingTask(payload: {
|
||||
employee_id: string;
|
||||
item_key: string;
|
||||
erledigt?: boolean;
|
||||
wert?: string | null;
|
||||
kommentar?: string | null;
|
||||
}): Promise<ActionResult> {
|
||||
return callRpc("set_onboarding_task", payload, [`/employees/${payload.employee_id}`]);
|
||||
}
|
||||
|
||||
/** Legt die Checkliste nachträglich an — für Personen von vor dieser Liste. */
|
||||
export async function startOnboarding(employeeId: string): Promise<ActionResult> {
|
||||
return callRpc(
|
||||
"start_onboarding",
|
||||
{ employee_id: employeeId, item_keys: ONBOARDING_PUNKTE.map((x) => x.key) },
|
||||
[`/employees/${employeeId}`]
|
||||
);
|
||||
}
|
||||
|
||||
export async function addEmployeeDependent(payload: {
|
||||
|
||||
Reference in New Issue
Block a user