export type ProjectPhase = { key?: string label?: string active?: boolean optional?: boolean activated_at?: string activated_by?: string [key: string]: unknown } type PhaseSelector = { phaseKey?: string | null phaseLabel?: string | null } export const activateProjectPhase = ( value: unknown, selector: PhaseSelector, userId: string, activatedAt = new Date().toISOString(), ) => { if (!Array.isArray(value) || value.length === 0) { throw new Error("Das Projekt hat keine Phasen") } const phases = value as ProjectPhase[] const phaseKey = String(selector.phaseKey || "").trim() const phaseLabel = String(selector.phaseLabel || "").trim() if (Boolean(phaseKey) === Boolean(phaseLabel)) { throw new Error("Genau phaseKey oder phaseLabel ist erforderlich") } const matches = phases .map((phase, index) => ({ phase, index })) .filter(({ phase }) => phaseKey ? phase.key === phaseKey : phase.label === phaseLabel) if (matches.length === 0) throw new Error("Projektphase nicht gefunden") if (matches.length > 1) throw new Error("Projektphase ist nicht eindeutig; bitte phaseKey verwenden") const { phase: target, index: targetIndex } = matches[0] const activeIndex = phases.findIndex((phase) => phase.active === true) const active = activeIndex >= 0 ? phases[activeIndex] : null if (target.active) throw new Error(`Projektphase „${target.label || target.key}“ ist bereits aktiv`) if (target.activated_at) throw new Error(`Projektphase „${target.label || target.key}“ wurde bereits aktiviert`) const completesProject = target.label === "Abgeschlossen" if (!completesProject && activeIndex >= 0) { if (targetIndex <= activeIndex) { throw new Error("Eine bereits durchlaufene Projektphase kann nicht erneut aktiviert werden") } const requiredSkippedPhase = phases .slice(activeIndex + 1, targetIndex) .find((candidate) => !candidate.optional) if (requiredSkippedPhase) { throw new Error(`Die Phase „${requiredSkippedPhase.label || requiredSkippedPhase.key}“ muss zuerst aktiviert werden`) } } else if (!completesProject && activeIndex < 0 && targetIndex !== 0) { throw new Error(`Die Phase „${phases[0].label || phases[0].key}“ muss zuerst aktiviert werden`) } const nextPhases = phases.map((phase, index) => { if (index === targetIndex) { return { ...phase, active: true, activated_at: activatedAt, activated_by: userId, } } if (phase.active) { return { ...phase, active: false, } } return { ...phase } }) return { phases: nextPhases, activePhase: String(target.label || "").trim() || null, previousPhase: active ? String(active.label || "").trim() || null : null, } }