All checks were successful
Build and Push Docker Images / build-website (push) Successful in 22s
Build and Push Docker Images / build-central-services-api (push) Successful in 22s
Build and Push Docker Images / build-central-services-admin (push) Successful in 22s
Build and Push Docker Images / build-docs (push) Successful in 22s
Build and Push Docker Images / build-backend (push) Successful in 45s
Build and Push Docker Images / build-frontend (push) Successful in 22s
89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
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,
|
|
}
|
|
}
|