feat: Add product dependencies (requirements and exclusions) to database, admin UI and wizard validation
All checks were successful
Staging Build / build (push) Successful in 2m39s
All checks were successful
Staging Build / build (push) Successful in 2m39s
This commit is contained in:
@@ -139,6 +139,45 @@ export function OrderWizard({
|
||||
})
|
||||
}, [categories, selections, products, selectedBillingInterval])
|
||||
|
||||
// Product validation errors (requirements and exclusions)
|
||||
const productValidationErrors = useMemo(() => {
|
||||
const errors: string[] = []
|
||||
const selectedProductIds = Object.values(selections)
|
||||
.map(s => s.productId)
|
||||
.filter((id): id is string => !!id)
|
||||
|
||||
selectedProductIds.forEach(prodId => {
|
||||
const prod = products.find(p => p.id === prodId)
|
||||
if (!prod) return
|
||||
|
||||
// Requirements check
|
||||
if (prod.requirements && prod.requirements.length > 0) {
|
||||
const missing = prod.requirements.filter(reqId => !selectedProductIds.includes(reqId))
|
||||
if (missing.length > 0) {
|
||||
const names = missing
|
||||
.map(reqId => products.find(p => p.id === reqId)?.name || reqId)
|
||||
.join(', ')
|
||||
errors.push(`"${prod.name}" benötigt das Produkt: ${names}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Exclusions check
|
||||
if (prod.exclusions && prod.exclusions.length > 0) {
|
||||
const conflicting = prod.exclusions.filter(exId => selectedProductIds.includes(exId))
|
||||
if (conflicting.length > 0) {
|
||||
const names = conflicting
|
||||
.map(exId => products.find(p => p.id === exId)?.name || exId)
|
||||
.join(', ')
|
||||
errors.push(`"${prod.name}" schließt aus: ${names}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return errors
|
||||
}, [selections, products])
|
||||
|
||||
const isNextStepDisabled = !allCategoriesFilled || productValidationErrors.length > 0
|
||||
|
||||
// Total price calculations (split by billing interval)
|
||||
const { monthlyTotal, oneTimeTotal } = useMemo(() => {
|
||||
let monthly = 0
|
||||
@@ -786,12 +825,18 @@ export function OrderWizard({
|
||||
Bitte aus jeder Pflichtkategorie einen Artikel wählen.
|
||||
</p>
|
||||
)}
|
||||
{productValidationErrors.map((err, errIdx) => (
|
||||
<p key={errIdx} className="text-xs text-destructive flex items-center gap-1">
|
||||
<AlertCircle className="w-3 h-3 text-destructive" />
|
||||
{err}
|
||||
</p>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardFooter className="flex flex-col gap-2">
|
||||
<Button
|
||||
className="w-full h-12 text-lg"
|
||||
onClick={nextStep}
|
||||
disabled={!allCategoriesFilled}
|
||||
disabled={isNextStepDisabled}
|
||||
>
|
||||
Weiter <ChevronRight className="ml-2 w-5 h-5" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user