Aus dem Gespraech vom 17.09.2026.
Der Kunde suchte den Knopf "Wiedereintritt" an einer ausgetretenen Person
und hielt ihn fuer verschwunden — "ich dachte eigentlich, das ist schon
implementiert, das war mal drin". Er war drin. Er hing nur an
employee.status, und diese Spalte haengt nach: an einer Person, deren
Austritt erfasst und inzwischen vollzogen war, stand dort weiter "Aktiv".
Das ist mein Fehler beim Statusfix. Umgestellt waren dort `isActive` und
`canEditData` — die fuenf einzelnen Abfragen in derselben Datei blieben
stehen, dazu je eine in KarenzPanel und TerminatePanel. Jetzt liest keine
mehr die Spalte; die beiden Panels bekommen den abgeleiteten Status
uebergeben, statt ihn sich selbst aus der Zeile zu holen.
Betroffen war ausser dem Wiedereintritt auch: welcher Austritts-Knopf
erscheint ("Nicht angetreten" statt "Austritt"), die Beschriftung der
Abwesenheit, die Vorbelegung der Beendigungsart und ob die Zugehoerigkeit
angezeigt wird.
Dazu: die Niederlassung ist aus dem Reiter Organisation wieder raus. Sie
stand dort als unsere Auslegung von Anforderung 8; der Kunde hat sie im
Gespraech gestrichen ("nimm's mal hier raus"). Was mit der Anforderung
gemeint war, bleibt offen.
116 lines
4.8 KiB
TypeScript
116 lines
4.8 KiB
TypeScript
import { Network } from "lucide-react";
|
|
import Link from "next/link";
|
|
import { Avatar } from "@/components/ui/Avatar";
|
|
import { LINK_BUTTON_CLASS } from "@/components/ui/Button";
|
|
import { fmtName } from "@/lib/format";
|
|
|
|
type MiniEmployee = { id: string; first_name: string; last_name: string; job_title: string; status?: string };
|
|
|
|
type OrganisationTabProps = {
|
|
employeeId: string;
|
|
manager: MiniEmployee | null;
|
|
/** Nur gesetzt, wenn die zuständige Leitung abwesend ist und vertreten wird. */
|
|
formalManager: MiniEmployee | null;
|
|
directReports: MiniEmployee[];
|
|
breadcrumb: string;
|
|
/**
|
|
* Die Kostenstelle der Planstelle, auf der die Person heute sitzt — nicht
|
|
* ihre eigene: sie kontiert dorthin, wo ihr Sitz kontiert ist. Fehlt sie,
|
|
* hat die Person keine laufende Besetzung (geplanter Eintritt, Austritt).
|
|
*/
|
|
kostenstelle: { code: string; name: string } | null;
|
|
};
|
|
|
|
// Die Niederlassung stand hier kurzzeitig neben Einheit und Kostenstelle —
|
|
// unsere Auslegung von „Niederlassungen auch in Zuordnung einfügen"
|
|
// (Anforderung 8). Im Gespräch am 17.09.2026 hat der Kunde sie hier wieder
|
|
// gestrichen: „nimm's mal hier raus, ich glaub, da brauchen wir's nicht
|
|
// drin." Sie steht weiterhin im Vertragsblatt.
|
|
//
|
|
// Was mit der Anforderung gemeint war, ist damit weiterhin offen — siehe
|
|
// docs/rueckfragen-workshop-2026-09.md.
|
|
|
|
export function OrganisationTab({
|
|
employeeId,
|
|
manager,
|
|
formalManager,
|
|
directReports,
|
|
breadcrumb,
|
|
kostenstelle,
|
|
}: OrganisationTabProps) {
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
<div>
|
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-ink-muted">Organisationseinheit</h3>
|
|
<p className="mt-1 text-sm text-ink">{breadcrumb}</p>
|
|
<h3 className="mt-4 text-xs font-semibold uppercase tracking-wide text-ink-muted">Kostenstelle</h3>
|
|
<p className="mt-1 text-sm text-ink">
|
|
{kostenstelle ? (
|
|
<>
|
|
<span className="font-semibold tabular-nums">{kostenstelle.code}</span>
|
|
<span className="text-ink-body"> · {kostenstelle.name}</span>
|
|
</>
|
|
) : (
|
|
<span className="text-ink-muted">Keine laufende Planstellenbesetzung</span>
|
|
)}
|
|
</p>
|
|
</div>
|
|
{/* ?focus= drives the same highlight/auto-expand path the org chart
|
|
search already uses, so the person is unfolded and centred on
|
|
arrival instead of the user hunting for them. */}
|
|
<Link href={`/orgchart?focus=${employeeId}`} className={LINK_BUTTON_CLASS}>
|
|
<Network className="h-4 w-4" />
|
|
Im Organigramm anzeigen
|
|
</Link>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wide text-ink-muted">
|
|
{formalManager ? "Führungskraft (Vertretung)" : "Führungskraft"}
|
|
</h3>
|
|
{formalManager && (
|
|
<p className="mb-2 text-xs text-ink-muted">
|
|
Zuständig ist {fmtName(formalManager.first_name, formalManager.last_name)}; während der Abwesenheit übernimmt die nächste
|
|
besetzte Ebene.
|
|
</p>
|
|
)}
|
|
{manager ? (
|
|
<Link href={`/employees/${manager.id}`} className="flex w-fit items-center gap-3 rounded border border-border p-3 hover:bg-surface">
|
|
<Avatar firstName={manager.first_name} lastName={manager.last_name} />
|
|
<div>
|
|
<div className="text-sm font-semibold text-ink">
|
|
{fmtName(manager.first_name, manager.last_name)}
|
|
</div>
|
|
<div className="text-xs text-ink-muted">{manager.job_title}</div>
|
|
</div>
|
|
</Link>
|
|
) : (
|
|
<p className="text-sm text-ink-muted">Keine Führungskraft (Geschäftsführung)</p>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wide text-ink-muted">Direkte Berichte ({directReports.length})</h3>
|
|
{directReports.length > 0 ? (
|
|
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
|
|
{directReports.map((r) => (
|
|
<Link key={r.id} href={`/employees/${r.id}`} className="flex items-center gap-3 rounded border border-border p-3 hover:bg-surface">
|
|
<Avatar firstName={r.first_name} lastName={r.last_name} />
|
|
<div>
|
|
<div className="text-sm font-semibold text-ink">
|
|
{fmtName(r.first_name, r.last_name)}
|
|
</div>
|
|
<div className="text-xs text-ink-muted">{r.job_title}</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-ink-muted">Keine direkten Berichte.</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|