Compare commits

...

2 Commits

Author SHA1 Message Date
DanielS
387ba19802 fix: loop and render all basket items in wizard summary screen
Some checks failed
Staging Build / build (push) Failing after 29s
2026-07-09 23:33:16 +02:00
DanielS
2c572c5bd4 fix: add missing payment_method column to orders table 2026-07-09 23:33:13 +02:00
2 changed files with 84 additions and 65 deletions

View File

@@ -622,6 +622,12 @@ export function OrderWizard({
} }
const finalItemsToShow = basketItems.length > 0
? basketItems
: (allCategoriesFilled && productValidationErrors.length === 0
? [{ deviceName: deviceName || 'Kasse 1', selections, moduleQuantities, billingInterval: selectedBillingInterval }]
: []);
return ( return (
<div className="max-w-5xl mx-auto py-12 px-4"> <div className="max-w-5xl mx-auto py-12 px-4">
{/* Progress Stepper */} {/* Progress Stepper */}
@@ -1426,72 +1432,79 @@ export function OrderWizard({
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-6 text-left"> <CardContent className="space-y-6 text-left">
<div className="p-4 rounded-lg bg-white/5 space-y-4"> <div className="p-4 rounded-lg bg-white/5 space-y-6">
{visibleCategories.map(cat => { {finalItemsToShow.map((item, itemIdx) => (
const sel = selections[cat.id] <div key={itemIdx} className="space-y-3 border-b border-white/10 pb-4 last:border-0 last:pb-0">
const selectedProds: Product[] = [] <div className="flex justify-between items-center bg-white/5 p-2 rounded">
if (cat.allow_multiselect && sel?.productIds) { <span className="text-white font-bold text-sm">Gerät: {item.deviceName}</span>
sel.productIds.forEach(pId => { <span className="text-xs text-slate-400 capitalize">{item.billingInterval === 'one_time' ? 'Kauf' : 'Abo'}</span>
const p = products.find(prod => prod.id === pId)
if (p) selectedProds.push(p)
})
} else if (sel?.productId) {
const p = products.find(prod => prod.id === sel.productId)
if (p) selectedProds.push(p)
}
if (selectedProds.length === 0) return null
// Sort selected products by price to determine which ones are free
const sortedProds = [...selectedProds].sort((a, b) => a.base_price - b.base_price)
const freeLimit = cat.allow_multiselect ? cat.free_items_limit : 0
return (
<div key={cat.id} className="space-y-2 border-b border-white/5 pb-3 last:border-0 last:pb-0">
<div className="flex items-center gap-2 text-slate-400 font-semibold text-sm">
<CategoryIcon icon={cat.icon} className="w-4 h-4 text-primary" />
<span>{cat.name}</span>
</div>
{sortedProds.map((prod, idx) => {
const isFree = idx < freeLimit
const actualPrice = isFree ? 0 : prod.base_price
const catTotal =
Number(actualPrice) +
(sel?.moduleIds?.reduce((acc, mId) => {
const mod = prod.modules?.find(m => m.id === mId)
const qty = moduleQuantities[mId] || 1
return acc + (Number(mod?.price ?? 0) * qty)
}, 0) || 0)
return (
<div key={prod.id} className="pl-4">
<div className="flex justify-between font-bold text-base text-white">
<span>
{prod.name} {isFree && <span className="text-[10px] text-green-400 font-semibold">(Frei)</span>}
</span>
<span>
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(catTotal)}
{' '}<span className="text-sm font-normal text-slate-400">{billingLabel(prod.billing_interval)}</span>
</span>
</div>
{sel?.moduleIds && sel.moduleIds.length > 0 && (
<p className="text-sm text-slate-300 mt-1">
Module: {sel.moduleIds
.map(id => {
const mod = prod.modules?.find(m => m.id === id)
const qty = moduleQuantities[id] || 1
return mod ? `${mod.name}${mod.has_quantity ? ` (x${qty})` : ''}` : ''
})
.filter(Boolean)
.join(', ')}
</p>
)}
</div>
)
})}
</div> </div>
)
})} {visibleCategories.map(cat => {
const sel = item.selections[cat.id]
const selectedProds: Product[] = []
if (cat.allow_multiselect && sel?.productIds) {
sel.productIds.forEach(pId => {
const p = products.find(prod => prod.id === pId)
if (p) selectedProds.push(p)
})
} else if (sel?.productId) {
const p = products.find(prod => prod.id === sel.productId)
if (p) selectedProds.push(p)
}
if (selectedProds.length === 0) return null
const sortedProds = [...selectedProds].sort((a, b) => a.base_price - b.base_price)
const freeLimit = cat.allow_multiselect ? cat.free_items_limit : 0
return (
<div key={cat.id} className="space-y-1 pl-2">
<div className="text-slate-400 font-semibold text-xs flex items-center gap-1">
<CategoryIcon icon={cat.icon} className="w-3.5 h-3.5 text-primary" />
<span>{cat.name}</span>
</div>
{sortedProds.map((prod, idx) => {
const isFree = idx < freeLimit
const actualPrice = isFree ? 0 : prod.base_price
const catTotal =
Number(actualPrice) +
(sel?.moduleIds?.reduce((acc, mId) => {
const mod = prod.modules?.find(m => m.id === mId)
const qty = item.moduleQuantities[mId] || 1
return acc + (Number(mod?.price ?? 0) * qty)
}, 0) || 0)
return (
<div key={prod.id} className="pl-3">
<div className="flex justify-between font-semibold text-sm text-white">
<span>
{prod.name} {isFree && <span className="text-[10px] text-green-400 font-semibold">(Frei)</span>}
</span>
<span>
{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(catTotal)}
</span>
</div>
{sel?.moduleIds && sel.moduleIds.length > 0 && (
<p className="text-xs text-slate-400 mt-0.5">
Module: {sel.moduleIds
.map(id => {
const mod = prod.modules?.find(m => m.id === id)
const qty = item.moduleQuantities[id] || 1
return mod ? `${mod.name}${mod.has_quantity ? ` (x${qty})` : ''}` : ''
})
.filter(Boolean)
.join(', ')}
</p>
)}
</div>
)
})}
</div>
)
})}
</div>
))}
<Separator className="bg-white/10" /> <Separator className="bg-white/10" />
<div className="text-sm text-slate-300 space-y-1"> <div className="text-sm text-slate-300 space-y-1">
{selectedEndCustomer ? ( {selectedEndCustomer ? (

View File

@@ -0,0 +1,6 @@
-- Migration: Add missing payment_method column to orders table
-- Purpose: Add payment_method column to public.orders and reload Schema Cache.
ALTER TABLE public.orders ADD COLUMN IF NOT EXISTS payment_method TEXT;
NOTIFY pgrst, 'reload schema';