Bei den Angehoerigen steht ein "+ Hinzufuegen", beim Notfallkontakt stand nichts: der einzige Weg zu einer Telefonnummer fuehrte ueber ein Formular ueber saemtliche Stammdaten. Jetzt steht der Knopf an derselben Stelle, mit einem Fenster fuer Name, Telefonnummer und Verhaeltnis; ist ein Kontakt hinterlegt, heisst er "Aendern" und daneben laesst er sich entfernen. Geschrieben wird ueber dieselbe Funktion wie in "Daten aendern" -- change_employee_data mit nur den drei Feldern. Der Eintrag landet damit in der Historie und im Protokoll wie jede andere Stammdatenaenderung. Die Datenbank fasst nur die Felder an, die im Aufruf stehen: sie prueft auf das Vorhandensein des Schluessels, nicht auf seinen Wert (Migration 20260917120000, Zeilen 209-211). Adresse, E-Mail und der Rest bleiben unberuehrt.
86 lines
4.0 KiB
TypeScript
86 lines
4.0 KiB
TypeScript
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 ?? "–"],
|
||
["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>
|
||
);
|
||
}
|