Fix CartDrawer build error: move grouping logic out of JSX
The IIFE with an inline type declaration inside a JSX ternary didn't compile. Pulled renderEntry and cartGroups into plain variables above the cartBody const so the JSX stays clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c72699da16
commit
7b7c3efb65
@ -405,121 +405,118 @@ export default function CartDrawer() {
|
|||||||
|
|
||||||
// ── Step content ───────────────────────────────────────────────────────────
|
// ── Step content ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const renderEntry = (entry: CartEntry, inBouquet = false) => (
|
||||||
|
<div key={entry.cartId} style={inBouquet ? { paddingBottom: '0.5rem', marginBottom: '0.5rem', borderBottom: '1px dashed #e0d8cc' } : { borderBottom: '1px solid #e6dfc8', paddingBottom: '0.75rem', marginBottom: '0.75rem' }}>
|
||||||
|
<div style={{ display: 'flex', gap: '10px', alignItems: 'flex-start' }}>
|
||||||
|
{entry.product.imageUrls[0] && (
|
||||||
|
<img
|
||||||
|
src={entry.product.imageUrls[0]}
|
||||||
|
alt={entry.product.name}
|
||||||
|
style={{ width: inBouquet ? 40 : 52, height: inBouquet ? 40 : 52, borderRadius: 6, objectFit: 'cover', flexShrink: 0, border: '1px solid #e6dfc8' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||||||
|
<strong style={{ fontSize: inBouquet ? '0.88rem' : '0.95rem' }}>{entry.product.name}</strong>
|
||||||
|
<div style={{ display: 'flex', gap: '6px', alignItems: 'center', flexShrink: 0 }}>
|
||||||
|
{!inBouquet && (
|
||||||
|
<button
|
||||||
|
onClick={() => setEditingEntry(entry)}
|
||||||
|
aria-label="Edit"
|
||||||
|
style={{ background: 'none', border: 'none', color: '#11b3be', cursor: 'pointer', fontSize: '0.75rem', lineHeight: 1, padding: '2px 4px' }}
|
||||||
|
>Edit</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => removeEntry(entry.cartId)}
|
||||||
|
aria-label="Remove"
|
||||||
|
style={{ background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '1.1rem', lineHeight: 1 }}
|
||||||
|
>×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginTop: '0.35rem' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => updateQuantity(entry.cartId, entry.quantity - 1)}
|
||||||
|
style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||||
|
>−</button>
|
||||||
|
<span style={{ fontSize: '0.85rem', minWidth: '18px', textAlign: 'center' }}>{entry.quantity}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => updateQuantity(entry.cartId, entry.quantity + 1)}
|
||||||
|
style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||||
|
>+</button>
|
||||||
|
{entry.product.price && (
|
||||||
|
<span style={{ fontSize: '0.82rem', color: '#666', marginLeft: '4px' }}>{fmt(entryUnitPrice(entry) * entry.quantity)}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{entry.selectedColors.length > 0 && (
|
||||||
|
<div style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
|
||||||
|
Colors: {entry.selectedColors.join(', ')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{Object.entries(entry.modifierChoices).map(([listId, optIds]) => {
|
||||||
|
if (!optIds.length) return null
|
||||||
|
const ml = entry.product.modifiers?.find((m) => m.id === listId)
|
||||||
|
if (!ml) return null
|
||||||
|
const labels = optIds.map((id) => {
|
||||||
|
const opt = ml.options.find((o) => o.id === id)
|
||||||
|
if (!opt) return id
|
||||||
|
return opt.priceDelta ? `${opt.name} (+${fmt(opt.priceDelta)})` : opt.name
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<div key={listId} style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
|
||||||
|
{ml.name}: {labels.join(', ')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
{entry.notes && (
|
||||||
|
<div style={{ fontSize: '0.8rem', color: '#888', marginTop: '0.2rem', fontStyle: 'italic' }}>
|
||||||
|
“{entry.notes}”
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const cartGroups: Array<
|
||||||
|
| { kind: 'single'; entry: CartEntry }
|
||||||
|
| { kind: 'bouquet'; groupId: string; items: CartEntry[] }
|
||||||
|
> = []
|
||||||
|
const seenGroups = new Set<string>()
|
||||||
|
entries.forEach((e) => {
|
||||||
|
if (!e.bouquetGroupId) {
|
||||||
|
cartGroups.push({ kind: 'single', entry: e })
|
||||||
|
} else if (!seenGroups.has(e.bouquetGroupId)) {
|
||||||
|
seenGroups.add(e.bouquetGroupId)
|
||||||
|
cartGroups.push({ kind: 'bouquet', groupId: e.bouquetGroupId, items: entries.filter((x) => x.bouquetGroupId === e.bouquetGroupId) })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const cartBody = (
|
const cartBody = (
|
||||||
<>
|
<>
|
||||||
{entries.length === 0 ? (
|
{entries.length === 0 ? (
|
||||||
<p className="has-text-grey has-text-centered" style={{ marginTop: '3rem' }}>
|
<p className="has-text-grey has-text-centered" style={{ marginTop: '3rem' }}>
|
||||||
Your order is empty.<br />Pick something from the shop!
|
Your order is empty.<br />Pick something from the shop!
|
||||||
</p>
|
</p>
|
||||||
) : (() => {
|
) : (
|
||||||
// Build render groups: bouquet entries grouped, regular entries standalone
|
cartGroups.map((group) => {
|
||||||
type RenderGroup =
|
if (group.kind === 'single') return renderEntry(group.entry)
|
||||||
| { kind: 'single'; entry: CartEntry }
|
const bouquetTotal = group.items.reduce((sum, e) => sum + entryUnitPrice(e) * e.quantity, 0)
|
||||||
| { kind: 'bouquet'; groupId: string; items: CartEntry[] }
|
return (
|
||||||
const groups: RenderGroup[] = []
|
<div key={group.groupId} style={{ border: '1px solid #b2e0e4', borderRadius: 10, padding: '0.75rem', marginBottom: '0.75rem', background: '#f7fdfd' }}>
|
||||||
const seen = new Set<string>()
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.6rem' }}>
|
||||||
entries.forEach((e) => {
|
<strong style={{ fontSize: '0.88rem', color: '#11b3be' }}>Your Bouquet</strong>
|
||||||
if (!e.bouquetGroupId) {
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||||
groups.push({ kind: 'single', entry: e })
|
{bouquetTotal > 0 && <span style={{ fontSize: '0.82rem', color: '#666' }}>{fmt(bouquetTotal)}</span>}
|
||||||
} else if (!seen.has(e.bouquetGroupId)) {
|
<button
|
||||||
seen.add(e.bouquetGroupId)
|
onClick={() => group.items.forEach((e) => removeEntry(e.cartId))}
|
||||||
groups.push({ kind: 'bouquet', groupId: e.bouquetGroupId, items: entries.filter((x) => x.bouquetGroupId === e.bouquetGroupId) })
|
style={{ background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '0.78rem' }}
|
||||||
}
|
>Remove all</button>
|
||||||
})
|
|
||||||
|
|
||||||
const renderEntry = (entry: CartEntry, inBouquet = false) => (
|
|
||||||
<div key={entry.cartId} style={inBouquet ? { paddingBottom: '0.5rem', marginBottom: '0.5rem', borderBottom: '1px dashed #e0d8cc' } : { borderBottom: '1px solid #e6dfc8', paddingBottom: '0.75rem', marginBottom: '0.75rem' }}>
|
|
||||||
<div style={{ display: 'flex', gap: '10px', alignItems: 'flex-start' }}>
|
|
||||||
{entry.product.imageUrls[0] && (
|
|
||||||
<img
|
|
||||||
src={entry.product.imageUrls[0]}
|
|
||||||
alt={entry.product.name}
|
|
||||||
style={{ width: inBouquet ? 40 : 52, height: inBouquet ? 40 : 52, borderRadius: 6, objectFit: 'cover', flexShrink: 0, border: '1px solid #e6dfc8' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
|
||||||
<strong style={{ fontSize: inBouquet ? '0.88rem' : '0.95rem' }}>{entry.product.name}</strong>
|
|
||||||
<div style={{ display: 'flex', gap: '6px', alignItems: 'center', flexShrink: 0 }}>
|
|
||||||
{!inBouquet && (
|
|
||||||
<button
|
|
||||||
onClick={() => setEditingEntry(entry)}
|
|
||||||
aria-label="Edit"
|
|
||||||
style={{ background: 'none', border: 'none', color: '#11b3be', cursor: 'pointer', fontSize: '0.75rem', lineHeight: 1, padding: '2px 4px' }}
|
|
||||||
>Edit</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => removeEntry(entry.cartId)}
|
|
||||||
aria-label="Remove"
|
|
||||||
style={{ background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '1.1rem', lineHeight: 1 }}
|
|
||||||
>×</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginTop: '0.35rem' }}>
|
|
||||||
<button
|
|
||||||
onClick={() => updateQuantity(entry.cartId, entry.quantity - 1)}
|
|
||||||
style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
|
||||||
>−</button>
|
|
||||||
<span style={{ fontSize: '0.85rem', minWidth: '18px', textAlign: 'center' }}>{entry.quantity}</span>
|
|
||||||
<button
|
|
||||||
onClick={() => updateQuantity(entry.cartId, entry.quantity + 1)}
|
|
||||||
style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
|
||||||
>+</button>
|
|
||||||
{entry.product.price && (
|
|
||||||
<span style={{ fontSize: '0.82rem', color: '#666', marginLeft: '4px' }}>{fmt(entryUnitPrice(entry) * entry.quantity)}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{entry.selectedColors.length > 0 && (
|
|
||||||
<div style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
|
|
||||||
Colors: {entry.selectedColors.join(', ')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{Object.entries(entry.modifierChoices).map(([listId, optIds]) => {
|
|
||||||
if (!optIds.length) return null
|
|
||||||
const ml = entry.product.modifiers?.find((m) => m.id === listId)
|
|
||||||
if (!ml) return null
|
|
||||||
const labels = optIds.map((id) => {
|
|
||||||
const opt = ml.options.find((o) => o.id === id)
|
|
||||||
if (!opt) return id
|
|
||||||
return opt.priceDelta ? `${opt.name} (+${fmt(opt.priceDelta)})` : opt.name
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<div key={listId} style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
|
|
||||||
{ml.name}: {labels.join(', ')}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{entry.notes && (
|
|
||||||
<div style={{ fontSize: '0.8rem', color: '#888', marginTop: '0.2rem', fontStyle: 'italic' }}>
|
|
||||||
“{entry.notes}”
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{group.items.map((e) => renderEntry(e, true))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
})
|
||||||
return groups.map((group) => {
|
|
||||||
if (group.kind === 'single') return renderEntry(group.entry)
|
|
||||||
|
|
||||||
const bouquetTotal = group.items.reduce((sum, e) => sum + entryUnitPrice(e) * e.quantity, 0)
|
|
||||||
return (
|
|
||||||
<div key={group.groupId} style={{ border: '1px solid #b2e0e4', borderRadius: 10, padding: '0.75rem', marginBottom: '0.75rem', background: '#f7fdfd' }}>
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.6rem' }}>
|
|
||||||
<strong style={{ fontSize: '0.88rem', color: '#11b3be' }}>Your Bouquet</strong>
|
|
||||||
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
||||||
{bouquetTotal > 0 && <span style={{ fontSize: '0.82rem', color: '#666' }}>{fmt(bouquetTotal)}</span>}
|
|
||||||
<button
|
|
||||||
onClick={() => group.items.forEach((e) => removeEntry(e.cartId))}
|
|
||||||
style={{ background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '0.78rem' }}
|
|
||||||
>Remove all</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{group.items.map((e) => renderEntry(e, true))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})()
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user