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.
87 lines
4.0 KiB
TypeScript
87 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 ?? "–"],
|
||
["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>
|
||
);
|
||
}
|