'use client'; import React, { useEffect, useMemo, useState } from 'react'; import PageLayout from '../../components/PageLayout'; import { useRouter } from 'next/navigation'; import { useActiveCoffees } from '../hooks/getActiveCoffees'; import { getStandardVatRate, getVatRates } from './hooks/getTaxRate'; import { subscribeAbo } from './hooks/subscribeAbo'; import useAuthStore from '../../store/authStore' export default function SummaryPage() { const router = useRouter(); const { coffees, loading, error } = useActiveCoffees(); const user = useAuthStore(state => state.user) const [selections, setSelections] = useState>({}); const [selectedPlanCapsules, setSelectedPlanCapsules] = useState<60 | 120>(120); const [isForSelf, setIsForSelf] = useState(true); const [form, setForm] = useState({ firstName: '', lastName: '', email: '', street: '', postalCode: '', city: '', country: 'DE', frequency: 'monatlich', startDate: '', recipientEmail: '', recipientName: '', recipientNotes: '', }); const [showThanks, setShowThanks] = useState(false); const [confetti, setConfetti] = useState<{ left: number; delay: number; color: string }[]>([]); const [taxRate, setTaxRate] = useState(0.07); // minimal fallback only const [vatRates, setVatRates] = useState<{ code: string; rate: number | null }[]>([]); const [submitError, setSubmitError] = useState(null); const [submitLoading, setSubmitLoading] = useState(false); const COLORS = ['#1C2B4A', '#233357', '#2A3B66', '#314475', '#3A4F88', '#5B6C9A']; // dark blue palette useEffect(() => { try { const raw = sessionStorage.getItem('coffeeSelections'); if (raw) setSelections(JSON.parse(raw)); const rawPlan = sessionStorage.getItem('coffeeAboSizeCapsules'); if (rawPlan === '60' || rawPlan === '120') { setSelectedPlanCapsules(Number(rawPlan) as 60 | 120); } } catch {} }, []); useEffect(() => { if (!showThanks) return; const items = Array.from({ length: 40 }).map(() => ({ left: Math.random() * 100, delay: Math.random() * 0.6, color: COLORS[Math.floor(Math.random() * COLORS.length)], })); setConfetti(items); }, [showThanks]); const selectedEntries = useMemo( () => Object.entries(selections) .map(([id, qty]) => { const coffee = coffees.find(c => c.id === id); return coffee ? { coffee, quantity: qty } : null; }) .filter(Boolean) as { coffee: ReturnType['coffees'][number]; quantity: number }[], [selections, coffees] ); // NEW: computed packs/capsules for validation const totalCapsules = useMemo( () => selectedEntries.reduce((sum, e) => sum + e.quantity, 0), [selectedEntries] ) const totalPacks = totalCapsules / 10 const requiredPacks = selectedPlanCapsules / 10 // NEW: capture logged-in user id for referral const rawUserId = user?.id const currentUserId = typeof rawUserId === 'number' ? rawUserId : (typeof rawUserId === 'string' && /^\d+$/.test(rawUserId) ? Number(rawUserId) : undefined) console.info('[SummaryPage] currentUserId:', currentUserId) // Countries list from backend VAT rates (fallback to current country if list empty) const countryOptions = useMemo(() => { const currentCode = (form.country || 'DE').toUpperCase(); const opts = vatRates.length > 0 ? vatRates.map(r => r.code) : [currentCode] if (!opts.includes(currentCode)) opts.unshift(currentCode) console.info('[SummaryPage] countryOptions:', opts) return opts }, [vatRates, form.country]); // Load VAT rates list from backend and set initial taxRate useEffect(() => { let active = true; (async () => { console.info('[SummaryPage] Loading vat rates (mount). country:', form.country) const list = await getVatRates(); if (!active) return; console.info('[SummaryPage] getVatRates result count:', list.length) setVatRates(list); const upper = form.country.toUpperCase(); const match = list.find(r => r.code === upper); if (match?.rate != null) { console.info('[SummaryPage] Initial taxRate from list:', match.rate, 'country:', upper) setTaxRate(match.rate); } else { const rate = await getStandardVatRate(form.country); console.info('[SummaryPage] Fallback taxRate via getStandardVatRate:', rate, 'country:', upper) setTaxRate(rate ?? 0.07); } })(); return () => { active = false; }; }, []); // mount-only // Update taxRate when country changes (from backend only) useEffect(() => { let active = true; (async () => { const upper = form.country.toUpperCase(); console.info('[SummaryPage] Country changed:', upper) const fromList = vatRates.find(r => r.code === upper)?.rate; if (fromList != null) { console.info('[SummaryPage] taxRate from existing list:', fromList) if (active) setTaxRate(fromList); return; } const rate = await getStandardVatRate(form.country); console.info('[SummaryPage] taxRate via getStandardVatRate:', rate) if (active) setTaxRate(rate ?? 0.07); })(); return () => { active = false; }; }, [form.country, vatRates]); const totalPrice = useMemo( () => selectedEntries.reduce((sum, e) => sum + (e.quantity / 10) * e.coffee.pricePer10, 0), [selectedEntries] ); const taxAmount = useMemo(() => totalPrice * taxRate, [totalPrice, taxRate]); const totalWithTax = useMemo(() => totalPrice + taxAmount, [totalPrice, taxRate, taxAmount]); const handleInput = (e: React.ChangeEvent) => { const { name, value } = e.target; setForm(prev => ({ ...prev, [name]: value })); }; const handleRecipientNotes = (e: React.ChangeEvent) => { const { name, value } = e.target; setForm(prev => ({ ...prev, [name]: value })); }; const fillFromLoggedInData = () => { if (!user) { setSubmitError('No logged-in user data found to fill the fields.'); return; } const pick = (...values: any[]) => { for (const value of values) { if (typeof value === 'string' && value.trim() !== '') return value.trim(); } return ''; }; setSubmitError(null); setForm(prev => ({ ...prev, firstName: pick(user.firstName, user.firstname, user.givenName, user.first_name) || prev.firstName, lastName: pick(user.lastName, user.lastname, user.familyName, user.last_name) || prev.lastName, email: pick(user.email, user.mail) || prev.email, street: pick(user.street, user.addressStreet, user.address?.street, user.address_line_1) || prev.street, postalCode: pick(user.postalCode, user.zipCode, user.zip, user.addressPostalCode, user.address?.postalCode) || prev.postalCode, city: pick(user.city, user.addressCity, user.town, user.address?.city) || prev.city, country: (pick(user.country, user.countryCode, user.addressCountry, user.address?.country) || prev.country).toUpperCase(), })); }; const requiredSelfFields: Array = [ 'firstName', 'lastName', 'email', 'street', 'postalCode', 'city', 'country', 'frequency', ] const hasRequiredSelfFields = requiredSelfFields.every(k => form[k].trim() !== '') const hasRequiredGiftFields = isForSelf || form.recipientEmail.trim() !== '' const canSubmit = selectedEntries.length > 0 && totalPacks === requiredPacks && hasRequiredSelfFields && hasRequiredGiftFields; const backToSelection = () => router.push('/coffee-abonnements'); const submit = async () => { if (!canSubmit || submitLoading) return // NEW: guard (defensive) — backend requires selected package size if (totalPacks !== requiredPacks) { setSubmitError(`Order must contain exactly ${requiredPacks} packs (${selectedPlanCapsules} capsules).`) return } if (!isForSelf && !form.recipientEmail.trim()) { setSubmitError('Recipient email is required when the subscription is for someone else.') return } setSubmitError(null) setSubmitLoading(true) try { const payload = { items: selectedEntries.map(entry => ({ coffeeId: entry.coffee.id, quantity: Math.round(entry.quantity / 10), // packs })), billing_interval: 'month', interval_count: 1, is_auto_renew: true, is_for_self: isForSelf, // NEW: pass customer fields firstName: form.firstName.trim(), lastName: form.lastName.trim(), email: form.email.trim(), street: form.street.trim(), postalCode: form.postalCode.trim(), city: form.city.trim(), country: form.country.trim(), frequency: form.frequency.trim(), startDate: form.startDate.trim() || undefined, recipient_email: isForSelf ? undefined : form.recipientEmail.trim(), recipient_name: isForSelf ? undefined : (form.recipientName.trim() || undefined), recipient_notes: isForSelf ? undefined : (form.recipientNotes.trim() || undefined), // NEW: always include referred_by if available referred_by: typeof currentUserId === 'number' ? currentUserId : undefined, } console.info('[SummaryPage] subscribeAbo payload:', payload) // NEW: explicit JSON preview to match request body console.info('[SummaryPage] subscribeAbo payload JSON:', JSON.stringify(payload)) await subscribeAbo(payload) setShowThanks(true); try { sessionStorage.removeItem('coffeeSelections'); } catch {} try { sessionStorage.removeItem('coffeeAboSizeCapsules'); } catch {} } catch (e: any) { setSubmitError(e?.message || 'Subscription could not be created.'); } finally { setSubmitLoading(false); } }; return (

Summary & Details

{/* Stepper */}
1 Selection
2 Summary
{error && (

{error}

)} {/* submit error */} {submitError && (

{submitError}

)} {loading ? (
) : selectedEntries.length === 0 ? (

No selection found.

) : (
{/* Left: Customer data */}

1. Your details

{/* inputs translated */}
{!isForSelf && ( <>