Files
alpenwerk-hr/components/employees/tabs/StammdatenTab.tsx
Andrei Laas 57acbfae9d
All checks were successful
CI / Lint, Typen, Tests, Build (push) Successful in 11m29s
CI / Migrationen auf leerer Datenbank (push) Successful in 10m27s
Die Firmen-E-Mail als eigenes, freiwilliges Feld
employees.email ist die private Adresse (20260811140000). Sie taugt nicht
als Dienstadresse und darf auch nicht als solche benutzt werden: der
Honestly-Export geht an einen fremden Anbieter, der damit im Namen des
Arbeitgebers einlaedt. Die Spalte "Email" stand dort deshalb seit jeher
leer, mit einem Vermerk in lib/honestly.ts, dass die Firmenadresse im
Datenmodell fehlt. Jetzt gibt es sie, und die Spalte fuellt sich.

Eindeutig, aber freiwillig -- mehrere Personen ohne Adresse stoeren den
Index nicht, weil null nie gleich null ist. Geschrieben wird ueber
`case when ? then nullif` statt `coalesce`: eine Dienstadresse muss sich
auch wieder entfernen lassen.

Vier SQL-Funktionen mussten mit, weil `create or replace` die ganze
Fassung ersetzt und ein ausgelassenes Feld dort still verschwindet:
hire_employee und rehire_employee (beide teilen sich den Schritt
"Person" -- das Formular haette das Feld gezeigt und den Wert
weggeworfen), change_employee_data (sonst nicht aenderbar),
apply_due_pending_changes (sonst verfiele eine auf spaeter datierte
Aenderung) und die Feldkarte (sonst waere der Eintrag in der Historie
nicht korrigierbar). Die Selbstpruefung am Ende prueft jede einzeln.
2026-09-23 10:52:26 +02:00

87 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AngehoerigeSection } from "@/components/employees/AngehoerigeSection";
import { NotfallkontaktSection } from "@/components/employees/NotfallkontaktSection";
import { brauchtAufenthaltstitel } from "@/lib/countries";
import { fmtAge, fmtDate } from "@/lib/format";
import type { Database } from "@/lib/types";
type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"];
type Location = Database["public"]["Tables"]["locations"]["Row"];
type Dependent = Database["public"]["Tables"]["employee_dependents"]["Row"];
export function StammdatenTab({ employee, location, dependents }: { employee: EmployeeRow; location?: Location; dependents: Dependent[] }) {
// Defensive against a DB that hasn't received the title_prefix/title_suffix
// migration yet — select("*") simply omits unknown columns, so these can
// be undefined rather than the empty array the column default implies.
const prefixe = employee.title_prefix ?? [];
const suffixe = employee.title_suffix ?? [];
// Feld für Feld dieselbe Liste wie im Abschnitt „Person" von „Daten
// ändern", in derselben Reihenfolge.
//
// Vorher fasste die Anzeige zusammen: Titel in einer Zeile, Adresse mit
// Postleitzahl und Ort verschmolzen, Vor- und Nachname gar nicht — die
// standen nur in der Kopfzeile. Wer eine Angabe prüfen wollte, musste den
// Änderungsdialog öffnen, um sie überhaupt zu sehen, und stand dann schon
// in einem Formular. Was sich ändern lässt, soll sich auch ansehen lassen.
const rows: [string, string][] = [
["Personalnummer", String(employee.personnel_number)],
["Vorname", employee.first_name],
["Nachname", employee.last_name],
["Titel (vorangestellt)", prefixe.length > 0 ? prefixe.join(", ") : "–"],
["Titel (nachgestellt)", suffixe.length > 0 ? suffixe.join(", ") : "–"],
["Geschlecht", employee.gender === "m" ? "männlich" : "weiblich"],
["Geburtsdatum", `${fmtDate(employee.birth_date)} (${fmtAge(employee.birth_date)} Jahre)`],
["SV-Nummer", employee.sv_nummer ?? "–"],
["Staatsbürgerschaft", employee.nationality],
// Nur, wo er verlangt ist. Bei einer österreichischen Staatsbürger-
// schaft wäre die Zeile „Aufenthaltstitel: Nein" keine Auskunft,
// sondern eine Frage, die sich nicht stellt.
...(brauchtAufenthaltstitel(employee.nationality)
? ([
[
"Aufenthaltstitel",
employee.hat_aufenthaltstitel
? employee.aufenthaltstitel_bis
? `Ja, bis ${fmtDate(employee.aufenthaltstitel_bis)}`
: "Ja (unbefristet oder nicht erfasst)"
: "Nein",
],
] as [string, string][])
: []),
["Adresse", employee.address ?? "–"],
["Postleitzahl", employee.postal_code ?? "–"],
["Ort", employee.city ?? "–"],
["Land", employee.address_country ?? "–"],
["Private E-Mail", employee.email ?? "–"],
["Firmen-E-Mail", employee.company_email ?? "–"],
["Private Telefonnummer", employee.phone ?? "–"],
// Der Standort ist keine Angabe zur Person, sondern die Betriebsstätte —
// er steht deshalb am Ende und nicht zwischen Adresse und Land, wo man
// ihn für den Wohnort halten könnte.
["Standort", location ? `${location.name} (${location.country})` : "–"],
];
return (
<div className="flex flex-col gap-6">
<dl className="grid grid-cols-1 gap-x-8 gap-y-4 sm:grid-cols-2 lg:grid-cols-3">
{rows.map(([label, value]) => (
<div key={label}>
<dt className="text-xs font-semibold uppercase tracking-wide text-ink-muted">{label}</dt>
<dd className="mt-1 text-sm text-ink">{value}</dd>
</div>
))}
</dl>
<AngehoerigeSection employeeId={employee.id} dependents={dependents} />
<NotfallkontaktSection
employeeId={employee.id}
kontakt={{
name: employee.emergency_contact_name,
telefon: employee.emergency_contact_phone,
verhaeltnis: employee.emergency_contact_relation,
}}
/>
</div>
);
}