105 lines
3.8 KiB
TypeScript
105 lines
3.8 KiB
TypeScript
import { WizardSelections, Product, Category } from '@/lib/types'
|
|
|
|
export function validateWizardSelections(
|
|
selections: WizardSelections,
|
|
dbProducts: Product[],
|
|
dbCategories: Category[]
|
|
): void {
|
|
for (const cat of dbCategories) {
|
|
const sel = selections[cat.id]
|
|
|
|
// --- is_required: Pflichtauswahl prüfen ---
|
|
const hasSelection = sel && (
|
|
(cat.allow_multiselect && sel.productIds && sel.productIds.length > 0) ||
|
|
(!cat.allow_multiselect && sel.productId)
|
|
)
|
|
|
|
if (cat.is_required && !hasSelection) {
|
|
throw new Error(`Die Kategorie "${cat.name}" ist ein Pflichtfeld, wurde aber nicht ausgewählt.`)
|
|
}
|
|
|
|
if (sel) {
|
|
// --- allow_multiselect = false: keine doppelten IDs erlaubt ---
|
|
if (!cat.allow_multiselect && sel.productIds && sel.productIds.length > 1) {
|
|
throw new Error(
|
|
`Die Kategorie "${cat.name}" erlaubt keine Mehrfachauswahl. Bitte nur ein Produkt wählen.`
|
|
)
|
|
}
|
|
|
|
const selectedProds: Product[] = []
|
|
if (cat.allow_multiselect && sel.productIds) {
|
|
sel.productIds.forEach(pId => {
|
|
const p = dbProducts.find(prod => prod.id === pId)
|
|
if (p) selectedProds.push(p)
|
|
})
|
|
} else if (sel.productId) {
|
|
const p = dbProducts.find(prod => prod.id === sel.productId)
|
|
if (p) selectedProds.push(p)
|
|
}
|
|
|
|
for (const prod of selectedProds) {
|
|
// Validate selected modules
|
|
for (const mId of sel.moduleIds) {
|
|
const mod = prod.modules?.find(m => m.id === mId)
|
|
if (!mod) continue
|
|
|
|
// Requirements check
|
|
if (mod.requirements && mod.requirements.length > 0) {
|
|
const hasMetRequirement = mod.requirements.some(reqId => sel.moduleIds.includes(reqId))
|
|
if (!hasMetRequirement) {
|
|
throw new Error(`Das Modul "${mod.name}" setzt die Aktivierung mindestens eines der folgenden Module voraus: ${mod.requirements.join(', ')}.`)
|
|
}
|
|
}
|
|
// Exclusions check
|
|
if (mod.exclusions && mod.exclusions.length > 0) {
|
|
const conflicting = mod.exclusions.filter(exId => sel.moduleIds.includes(exId))
|
|
if (conflicting.length > 0) {
|
|
throw new Error(`Das Modul "${mod.name}" schließt die Kombination mit anderen gewählten Modulen aus.`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Product-level constraints validation ---
|
|
const selectedProductIds: string[] = []
|
|
Object.values(selections).forEach(s => {
|
|
if (s.productIds && s.productIds.length > 0) {
|
|
s.productIds.forEach(id => {
|
|
if (id) selectedProductIds.push(id)
|
|
})
|
|
} else if (s.productId) {
|
|
selectedProductIds.push(s.productId)
|
|
}
|
|
})
|
|
|
|
for (const prodId of selectedProductIds) {
|
|
const prod = dbProducts.find(p => p.id === prodId)
|
|
if (!prod) continue
|
|
|
|
// Product requirements check
|
|
if (prod.requirements && prod.requirements.length > 0) {
|
|
const hasMetRequirement = prod.requirements.some(reqId => selectedProductIds.includes(reqId))
|
|
if (!hasMetRequirement) {
|
|
const reqNames = prod.requirements
|
|
.map(reqId => dbProducts.find(p => p.id === reqId)?.name || reqId)
|
|
.join(' oder ')
|
|
throw new Error(`Das Produkt "${prod.name}" erfordert die Auswahl von mindestens einem dieser Produkte: ${reqNames}.`)
|
|
}
|
|
}
|
|
|
|
// Product exclusions check
|
|
if (prod.exclusions && prod.exclusions.length > 0) {
|
|
const conflicting = prod.exclusions.filter(exId => selectedProductIds.includes(exId))
|
|
if (conflicting.length > 0) {
|
|
const conflictingNames = conflicting
|
|
.map(exId => dbProducts.find(p => p.id === exId)?.name || exId)
|
|
.join(', ')
|
|
throw new Error(`Das Produkt "${prod.name}" schließt die gleichzeitige Auswahl von folgenden Produkten aus: ${conflictingNames}.`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|