feat: add multi-device basket loop and add-another-device button to wizard
All checks were successful
Staging Build / build (push) Successful in 2m46s
All checks were successful
Staging Build / build (push) Successful in 2m46s
This commit is contained in:
@@ -98,6 +98,8 @@ export function OrderWizard({
|
||||
const [selectedCompanyId, setSelectedCompanyId] = useState<string | null>(
|
||||
initialOrder?.company_id ?? null
|
||||
)
|
||||
const [basketItems, setBasketItems] = useState<any[]>([])
|
||||
const [deviceName, setDeviceName] = useState<string>('')
|
||||
|
||||
// Partner-Profil (Fallback)
|
||||
const [customerData] = useState<Partial<Profile>>(initialProfile || {})
|
||||
@@ -549,40 +551,73 @@ export function OrderWizard({
|
||||
}
|
||||
}
|
||||
|
||||
const addToBasket = () => {
|
||||
const currentItem = {
|
||||
deviceName: deviceName || `Kasse ${basketItems.length + 1}`,
|
||||
selections: JSON.parse(JSON.stringify(selections)),
|
||||
moduleQuantities: { ...moduleQuantities },
|
||||
billingInterval: selectedBillingInterval,
|
||||
}
|
||||
setBasketItems(prev => [...prev, currentItem])
|
||||
setModuleQuantities({})
|
||||
setDeviceName('')
|
||||
|
||||
// Reset selections
|
||||
const resetSels: Record<string, CategorySelection> = {}
|
||||
categories.forEach(cat => {
|
||||
const isVisible = selectedBillingInterval === 'one_time' ? cat.show_in_kauf !== false : cat.show_in_abo !== false
|
||||
if (!isVisible || cat.allow_multiselect || cat.preselect === false) {
|
||||
resetSels[cat.id] = { productId: null, productIds: [], moduleIds: [] }
|
||||
} else {
|
||||
const first = products.find(p => p.category_id === cat.id && (selectedBillingInterval === 'one_time' ? p.show_in_kauf !== false : p.show_in_abo !== false))
|
||||
resetSels[cat.id] = { productId: first?.id ?? null, productIds: first ? [first.id] : [], moduleIds: [] }
|
||||
}
|
||||
})
|
||||
setSelections(resetSels)
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting) return // Doppelklick-Guard
|
||||
if (isSubmitting) return
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
let order
|
||||
if (initialOrder) {
|
||||
order = await updateOrder(initialOrder.id, {
|
||||
let finalItems = [...basketItems]
|
||||
if (finalItems.length === 0 && allCategoriesFilled && productValidationErrors.length === 0) {
|
||||
finalItems.push({
|
||||
deviceName: deviceName || 'Kasse 1',
|
||||
selections,
|
||||
moduleQuantities,
|
||||
customerProfile: customerData,
|
||||
endCustomerId: selectedEndCustomerId,
|
||||
endCustomer: selectedEndCustomer,
|
||||
billingInterval: selectedBillingInterval,
|
||||
lastLicenseDate: lastLicenseDate || null,
|
||||
companyId: selectedCompanyId,
|
||||
})
|
||||
} else {
|
||||
order = await submitOrder({
|
||||
selections,
|
||||
moduleQuantities,
|
||||
products,
|
||||
categories,
|
||||
customerProfile: customerData,
|
||||
endCustomerId: selectedEndCustomerId,
|
||||
endCustomer: selectedEndCustomer,
|
||||
billingInterval: selectedBillingInterval,
|
||||
lastLicenseDate: lastLicenseDate || null,
|
||||
billingInterval: selectedBillingInterval
|
||||
})
|
||||
}
|
||||
router.push(`/order/success?id=${order.id}`)
|
||||
|
||||
if (finalItems.length === 0) {
|
||||
throw new Error('Bitte konfigurieren Sie mindestens eine Kasse.')
|
||||
}
|
||||
|
||||
const res = await fetch('/api/orders/checkout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
items: finalItems,
|
||||
customerProfile: customerData,
|
||||
endCustomerId: selectedEndCustomerId,
|
||||
endCustomer: selectedEndCustomer,
|
||||
})
|
||||
})
|
||||
|
||||
const result = await res.json()
|
||||
if (result.error) throw new Error(result.error)
|
||||
|
||||
const firstOrderId = result.orders?.[0]?.id
|
||||
if (firstOrderId) {
|
||||
router.push(`/order/success?id=${firstOrderId}`)
|
||||
} else {
|
||||
router.push('/my-orders')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
alert(error?.message || (initialOrder ? 'Fehler beim Aktualisieren der Anfrage.' : 'Fehler bei der Bestellung. Bitte versuchen Sie es erneut.'))
|
||||
setIsSubmitting(false) // Nur bei Fehler wieder entsperren
|
||||
alert(error?.message || 'Fehler bei der Bestellung. Bitte versuchen Sie es erneut.')
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1323,12 +1358,43 @@ export function OrderWizard({
|
||||
{err}
|
||||
</p>
|
||||
))}
|
||||
{basketItems.length > 0 && (
|
||||
<div className="pt-4 border-t border-white/10 space-y-2">
|
||||
<p className="text-xs font-semibold text-slate-500 uppercase tracking-wider">Warenkorb ({basketItems.length}):</p>
|
||||
{basketItems.map((item, idx) => (
|
||||
<div key={idx} className="flex justify-between text-xs bg-white/5 p-2 rounded border border-white/10">
|
||||
<span className="text-white font-medium">{item.deviceName}</span>
|
||||
<span className="text-slate-400 capitalize">{item.billingInterval === 'one_time' ? 'Kauf' : 'Abo'}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardFooter className="flex flex-col gap-2">
|
||||
<CardFooter className="flex flex-col gap-3">
|
||||
<div className="w-full space-y-2">
|
||||
<Label htmlFor="device-name" className="text-xs text-slate-400">Gerätename (optional)</Label>
|
||||
<Input
|
||||
id="device-name"
|
||||
placeholder="z.B. Hauptkasse, Theke"
|
||||
value={deviceName}
|
||||
onChange={e => setDeviceName(e.target.value)}
|
||||
className="bg-white/5 border-white/10 text-white text-xs h-9 rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full border-primary/30 text-white hover:bg-primary/10 gap-2 h-10 text-sm"
|
||||
onClick={addToBasket}
|
||||
disabled={isNextStepDisabled}
|
||||
>
|
||||
<UserPlus className="w-4 h-4" /> Noch eine Kasse hinzufügen
|
||||
</Button>
|
||||
<Separator className="bg-white/10 my-1" />
|
||||
<Button
|
||||
className="w-full h-12 text-lg"
|
||||
onClick={nextStep}
|
||||
disabled={isNextStepDisabled}
|
||||
disabled={basketItems.length === 0 && isNextStepDisabled}
|
||||
>
|
||||
Weiter <ChevronRight className="ml-2 w-5 h-5" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user