Eine Organisationseinheit aus dem Organigramm heraus anlegen
Bisher gab es dafuer nur den Import. Jetzt sitzt auf jeder Einheit im Organigramm -- in der Grafik wie in der Liste -- ein Plus, das eine untergeordnete Einheit anlegt, wahlweise gleich mit Leitungsplanstelle. Bewusst nur dieser eine Fall. Umbenennen, verschieben und schliessen fehlen nicht aus Zeitmangel: org_units.parent_id traegt kein Datum, ein Verschieben aenderte damit auch jede Auswertung auf einen vergangenen Stichtag, und der fruehere Stand waere danach nirgends mehr ablesbar. Anlegen stellt diese Frage nicht, weil vorher nichts da war -- und es kann dabei auch kein Kreis entstehen, was hier mehr wiegt als es klingt: die rekursive Abfrage in om_reporting_lines hat weder Tiefenbegrenzung noch Kreiserkennung. Die Leitungsplanstelle entsteht ueber create_position statt durch eine zweite Fassung derselben Logik -- dort haengen Jobkatalog, Nummernvergabe und die Pruefung "je Einheit genau eine Leitung". Die Orgnummer wird eingetragen, nicht vergeben. Vorgeschlagen wird die naechste freie Nummer der bestehenden Reihe, und nur dann, wenn sich im Bestand genau eine Systematik ablesen laesst -- eine plausibel aussehende, aber erfundene Nummer prueft niemand nach. Das Datum kommt nicht aus dem Stichtag der Ansicht. Wer sich die Struktur zum letzten Jahresende ansieht und auf Plus drueckt, will in aller Regel eine Einheit von heute anlegen.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { ChevronDown, ChevronRight, Plus } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { SegmentedControl } from "@/components/ui/SegmentedControl";
|
||||
import { CreateOrgUnitModal } from "./CreateOrgUnitModal";
|
||||
import { LazyGraphOrgChart } from "./LazyGraphOrgChart";
|
||||
import type { ChartNode, OrgEmployee, OrgUnitNode, OrgVacancy } from "./types";
|
||||
import { fmtName } from "@/lib/format";
|
||||
@@ -41,6 +42,13 @@ export function PositionTree({ employees, units, vacancies }: PositionTreeProps)
|
||||
}, []);
|
||||
const isExpanded = useCallback((id: string) => expanded.has(id), [expanded]);
|
||||
|
||||
// Die Einheit, unter der gerade eine neue entstehen soll. Als Kennung und
|
||||
// nicht als Objekt: nach dem Anlegen lädt die Seite neu, und ein
|
||||
// festgehaltenes Objekt wäre dann der Stand von vorhin.
|
||||
const [anlegenUnter, setAnlegenUnter] = useState<string | null>(null);
|
||||
const onAddUnit = useCallback((unitId: string) => setAnlegenUnter(unitId), []);
|
||||
const eltern = anlegenUnter ? (units.find((u) => u.id === anlegenUnter) ?? null) : null;
|
||||
|
||||
const tree = useMemo(() => buildUnitTree(units, employees, vacancies), [units, employees, vacancies]);
|
||||
|
||||
return (
|
||||
@@ -56,14 +64,16 @@ export function PositionTree({ employees, units, vacancies }: PositionTreeProps)
|
||||
/>
|
||||
</div>
|
||||
{mode === "graph" ? (
|
||||
<LazyGraphOrgChart tree={tree} isExpanded={isExpanded} onToggle={toggle} />
|
||||
<LazyGraphOrgChart tree={tree} isExpanded={isExpanded} onToggle={toggle} onAddUnit={onAddUnit} />
|
||||
) : (
|
||||
<div className="rounded border border-border bg-white p-4">
|
||||
{tree.map((node) => (
|
||||
<ListNode key={node.id} node={node} depth={0} expanded={expanded} onToggle={toggle} />
|
||||
<ListNode key={node.id} node={node} depth={0} expanded={expanded} onToggle={toggle} onAddUnit={onAddUnit} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{eltern && <CreateOrgUnitModal parent={eltern} units={units} onClose={() => setAnlegenUnter(null)} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -73,11 +83,13 @@ function ListNode({
|
||||
depth,
|
||||
expanded,
|
||||
onToggle,
|
||||
onAddUnit,
|
||||
}: {
|
||||
node: ChartNode;
|
||||
depth: number;
|
||||
expanded: Set<string>;
|
||||
onToggle: (id: string) => void;
|
||||
onAddUnit: (unitId: string) => void;
|
||||
}) {
|
||||
const expandable = node.children.length > 0;
|
||||
const open = expanded.has(node.id);
|
||||
@@ -85,7 +97,7 @@ function ListNode({
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="flex items-center gap-2 rounded px-2 py-1.5 hover:bg-surface"
|
||||
className="group/zeile flex items-center gap-2 rounded px-2 py-1.5 hover:bg-surface"
|
||||
style={{ paddingLeft: depth * 24 + 8 }}
|
||||
>
|
||||
{expandable ? (
|
||||
@@ -114,8 +126,27 @@ function ListNode({
|
||||
{node.sublabel && (
|
||||
<span className={node.vacant ? "text-xs font-semibold text-warning-text" : "text-xs text-ink-muted"}>{node.sublabel}</span>
|
||||
)}
|
||||
{/* Derselbe Knopf wie auf der Karte in der Grafik — beide Ansichten
|
||||
kommen aus einem Baum, und eine Handlung, die es nur in einer von
|
||||
beiden gibt, findet man in der anderen nie. */}
|
||||
{node.unitId && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onAddUnit(node.unitId!)}
|
||||
aria-label={`Einheit unter ${node.label} anlegen`}
|
||||
title="Untergeordnete Einheit anlegen"
|
||||
className="ml-1 shrink-0 rounded p-0.5 text-ink-muted opacity-0 transition-opacity hover:text-brand-700
|
||||
focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-brand-500
|
||||
group-hover/zeile:opacity-100 [@media(hover:none)]:opacity-100"
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{open && node.children.map((child) => <ListNode key={child.id} node={child} depth={depth + 1} expanded={expanded} onToggle={onToggle} />)}
|
||||
{open &&
|
||||
node.children.map((child) => (
|
||||
<ListNode key={child.id} node={child} depth={depth + 1} expanded={expanded} onToggle={onToggle} onAddUnit={onAddUnit} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -203,6 +234,7 @@ export function buildUnitTree(units: OrgUnitNode[], employees: OrgEmployee[], va
|
||||
return {
|
||||
id: key,
|
||||
kind: "role",
|
||||
unitId: unit.id,
|
||||
label: `${unit.org_number} · ${unit.name}`,
|
||||
// Die Gesellschaft bleibt ohne Vermerk: sie wird nicht geführt, sondern
|
||||
// ist das Ganze. „Leitung vakant" las sich dort wie eine offene Stelle,
|
||||
|
||||
Reference in New Issue
Block a user