"use client"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { hireEmployee } from "@/actions/employees"; import { deleteHireDraft, saveHireDraft } from "@/actions/hireDrafts"; import { Button } from "@/components/ui/Button"; import { Modal } from "@/components/ui/Modal"; import { useToast } from "@/components/ui/Toast"; import type { OpenPositionResolved } from "@/lib/positions"; import { isValidSvnr, requiresAustrianSvnr } from "@/lib/svnr"; import { StepPerson } from "./StepPerson"; import { StepPosition } from "./StepPosition"; import { StepSummary } from "./StepSummary"; import { StepVertrag } from "./StepVertrag"; import { EMPTY_HIRE_DRAFT, type HireDraftData } from "./types"; const STEP_LABELS = ["Person", "Position", "Vertrag", "Zusammenfassung"]; type HireWizardProps = { open: boolean; onClose: () => void; openPositions: OpenPositionResolved[]; locations: { id: string; name: string; country: string }[]; resumeDraft: { id: string; step: number; payload: Record } | null; initialPositionId?: string; }; export function HireWizard({ open, onClose, openPositions, locations, resumeDraft, initialPositionId }: HireWizardProps) { const { showToast } = useToast(); const router = useRouter(); // The parent remounts this component (via a changing `key`) each time it's // freshly opened, so these initializers — reading resumeDraft/ // initialPositionId once at mount — are the reset, no effect needed. const [step, setStep] = useState(() => resumeDraft?.step ?? 0); const [draft, setDraft] = useState(() => resumeDraft ? { ...EMPTY_HIRE_DRAFT, ...(resumeDraft.payload as Partial) } : { ...EMPTY_HIRE_DRAFT, positionId: initialPositionId ?? "" } ); const [draftId] = useState(() => resumeDraft?.id); const [submitting, setSubmitting] = useState(false); const selectedPosition = useMemo( () => openPositions.find((p) => p.id === draft.positionId) ?? null, [openPositions, draft.positionId] ); function update(patch: Partial) { setDraft((prev) => ({ ...prev, ...patch })); } // Blocks step 1 rather than letting the hire fail at the RPC: the SVNR // trigger rejects a bad number, and by then the user is three steps on. const svNummerOk = !draft.svNummer.trim() || !requiresAustrianSvnr(locations.find((l) => l.id === draft.locationId)?.country) || isValidSvnr(draft.svNummer, draft.birthDate || null); const stepValid = [ // E-Mail gehört zu den Pflichtfeldern, weil die Spalte NOT NULL ist. Ohne // die Prüfung hier bricht erst die Datenbank ab — am Ende des vierten // Schritts, nach allen Eingaben. Boolean(draft.firstName && draft.lastName && draft.birthDate && draft.locationId && draft.email.trim()) && svNummerOk, Boolean(draft.positionId && draft.besetzung), Boolean(draft.entryDate && draft.workDays.length > 0), true, ][step]; async function handleSaveDraft() { const result = await saveHireDraft({ id: draftId, step, data: draft }); if (result.success) { showToast("Entwurf gespeichert."); router.refresh(); onClose(); } else { showToast(result.error ?? "Fehler beim Speichern.", "error"); } } async function handleSubmit() { if (!selectedPosition || !draft.besetzung) return; setSubmitting(true); const result = await hireEmployee({ first_name: draft.firstName, last_name: draft.lastName, title_prefix: draft.titlePrefix, title_suffix: draft.titleSuffix, gender: draft.gender, birth_date: draft.birthDate, sv_nummer: draft.svNummer || undefined, email: draft.email.trim(), phone: draft.phone || undefined, position_id: draft.positionId, location_id: draft.locationId, entry_date: draft.entryDate, contract_type: draft.contractType, contract_end_date: draft.contractType === "befristet" ? draft.contractEndDate : undefined, employment_type: draft.employmentType, weekly_hours: Number(draft.weeklyHours), paygrade: draft.paygrade, source: draft.besetzung, worker_type: draft.workerType, collective_agreement: draft.collectiveAgreement, work_days: draft.workDays, is_betriebsrat: draft.isBetriebsrat, has_dienstwagen: draft.hasDienstwagen, is_laterale_fuehrung: draft.isLateraleFuehrung, is_c_level: draft.isCLevel, }); setSubmitting(false); if (result.success) { showToast(`${draft.firstName} ${draft.lastName} wurde eingestellt.`); if (draftId) await deleteHireDraft(draftId); router.refresh(); onClose(); } else { showToast(result.error ?? "Fehler beim Anlegen.", "error"); } } return (
{step > 0 && ( )} {step < 3 && ( )} {step === 3 && ( )}
} > {/* A progress trail, not navigation: only completed steps are reachable, so the rest are genuinely disabled rather than inert buttons that silently swallow a click. */}
{STEP_LABELS.map((label, i) => ( ))}
{step === 0 && } {step === 1 && } {step === 2 && } {step === 3 && }
); }