feat(multiselect): support multi-select categories and free items limits in category config and order wizard
All checks were successful
Staging Build / build (push) Successful in 2m38s

This commit is contained in:
DanielS
2026-07-07 16:56:24 +02:00
parent 00d1be3e4f
commit cc7445e4c5
9 changed files with 451 additions and 212 deletions

View File

@@ -7,33 +7,46 @@ export function validateWizardSelections(
): void {
for (const cat of dbCategories) {
const sel = selections[cat.id]
if (cat.is_required && (!sel || !sel.productId)) {
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?.productId) {
const prod = dbProducts.find(p => p.id === sel.productId)
if (!prod) {
throw new Error(`Das ausgewählte Produkt für Kategorie "${cat.name}" wurde nicht gefunden.`)
if (sel) {
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)
}
// Validate selected modules
for (const mId of sel.moduleIds) {
const mod = prod.modules?.find(m => m.id === mId)
if (!mod) {
throw new Error(`Das Modul mit ID "${mId}" gehört nicht zum Produkt "${prod.name}".`)
}
// 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(', ')}.`)
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 // module might belong to another selected product in the same category, so skip error if it's not this one's
// 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.`)
// 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.`)
}
}
}
}
@@ -41,9 +54,16 @@ export function validateWizardSelections(
}
// Product-level constraints validation
const selectedProductIds = Object.values(selections)
.map(s => s.productId)
.filter((id): id is string => !!id)
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)