'use client' import { useState, useMemo } from 'react' import { useRouter } from 'next/navigation' import { motion, AnimatePresence } from 'framer-motion' import { Product, ProductModule, Profile, EndCustomer } from '@/lib/types' import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Separator } from '@/components/ui/separator' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { Check, ChevronRight, ChevronLeft, ShoppingCart, User, ShieldCheck, Cloud, Utensils, HardDrive, Package, AlertCircle, Loader2, UserPlus, Building2 } from 'lucide-react' import { submitOrder } from '@/lib/actions/orders' import { createEndCustomer } from '@/lib/actions/end-customers' import { Category } from '@/lib/types' import { Badge } from '@/components/ui/badge' // ─── Types ──────────────────────────────────────────────────────────────────── type CategorySelection = { productId: string | null // selected product in this category moduleIds: string[] // selected module IDs for this product } // ─── Billing interval helpers ───────────────────────────────────────────────── function billingLabel(interval?: string) { if (interval === 'one_time') return 'einmalig' return '/ Monat' } function billingBadgeClass(interval?: string) { if (interval === 'one_time') return 'bg-slate-500/20 text-slate-300 border-slate-500/30' return 'bg-blue-500/20 text-blue-400 border-blue-500/30' } // ─── Icon helper ────────────────────────────────────────────────────────────── function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) { const Icon = icon === 'Cloud' ? Cloud : icon === 'Utensils' ? Utensils : icon === 'HardDrive' ? HardDrive : Package return } // ─── Module toggle logic ─────────────────────────────────────────────────────── function toggleModuleInList( modules: ProductModule[], currentIds: string[], toggleId: string ): string[] { const isSelected = currentIds.includes(toggleId) if (isSelected) { const next = currentIds.filter(id => id !== toggleId) // cascade: remove modules that required this one return next.filter(mId => { const m = modules.find(mod => mod.id === mId) return !m?.requirements || m.requirements.every(reqId => next.includes(reqId) || reqId === toggleId) }).filter(id => id !== toggleId) } else { const module = modules.find(m => m.id === toggleId) if (module?.exclusions?.some(exId => currentIds.includes(exId))) return currentIds return [...currentIds, toggleId] } } function isModuleDisabled(module: ProductModule, selectedModules: string[]): boolean { const requirementsMet = !module.requirements?.length || module.requirements.every(reqId => selectedModules.includes(reqId)) const isExcluded = module.exclusions?.some(exId => selectedModules.includes(exId)) return !requirementsMet || !!isExcluded } // ─── Main Wizard ────────────────────────────────────────────────────────────── export function OrderWizard({ products, categories, initialProfile, initialEndCustomers, }: { products: Product[] categories: Category[] initialProfile: Profile | null initialEndCustomers: EndCustomer[] }) { const router = useRouter() const [step, setStep] = useState(1) const [isSubmitting, setIsSubmitting] = useState(false) // Partner-Profil (Fallback) const [customerData] = useState>(initialProfile || {}) // Endkunden-State const [endCustomers, setEndCustomers] = useState(initialEndCustomers) const [selectedEndCustomerId, setSelectedEndCustomerId] = useState( initialEndCustomers.length > 0 ? initialEndCustomers[0].id : null ) const selectedEndCustomer = endCustomers.find(c => c.id === selectedEndCustomerId) ?? null // Modus: 'select' = Bestandskunde wählen | 'create' = Neuen anlegen const [customerMode, setCustomerMode] = useState<'select' | 'create'>('select') const [isCreatingCustomer, setIsCreatingCustomer] = useState(false) const [newCustomerForm, setNewCustomerForm] = useState({ company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', }) // Per-category selection map: categoryId -> { productId, moduleIds } const [selections, setSelections] = useState>(() => { const init: Record = {} categories.forEach(cat => { const first = products.find(p => p.category_id === cat.id) init[cat.id] = { productId: first?.id ?? null, moduleIds: [] } }) return init }) // Only REQUIRED categories must have a product selected const allCategoriesFilled = useMemo( () => categories.filter(cat => cat.is_required).every(cat => !!selections[cat.id]?.productId), [categories, selections] ) // Total price across all selections const totalPrice = useMemo(() => { let total = 0 for (const cat of categories) { const sel = selections[cat.id] if (!sel?.productId) continue const prod = products.find(p => p.id === sel.productId) if (!prod) continue total += Number(prod.base_price) sel.moduleIds.forEach(mId => { const mod = prod.modules?.find(m => m.id === mId) if (mod) total += Number(mod.price) }) } return total }, [selections, products, categories]) // Helper: update product selection for a category (resets modules) function selectProduct(catId: string, productId: string) { setSelections(prev => ({ ...prev, [catId]: { productId, moduleIds: [] }, })) } // Helper: toggle module for a category's selected product function toggleModule(catId: string, moduleId: string) { setSelections(prev => { const sel = prev[catId] if (!sel?.productId) return prev const prod = products.find(p => p.id === sel.productId) const newModuleIds = toggleModuleInList(prod?.modules ?? [], sel.moduleIds, moduleId) return { ...prev, [catId]: { ...sel, moduleIds: newModuleIds } } }) } const nextStep = () => setStep(s => s + 1) const prevStep = () => setStep(s => s - 1) // Neuen Endkunden inline anlegen const handleCreateCustomer = async () => { if (!newCustomerForm.company_name.trim()) return setIsCreatingCustomer(true) try { const created = await createEndCustomer(newCustomerForm) setEndCustomers(prev => [...prev, created]) setSelectedEndCustomerId(created.id) setCustomerMode('select') setNewCustomerForm({ company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '' }) } catch (e) { alert('Fehler beim Anlegen des Kunden.') } finally { setIsCreatingCustomer(false) } } const handleSubmit = async () => { if (isSubmitting) return // Doppelklick-Guard setIsSubmitting(true) try { const order = await submitOrder({ selections, products, categories, customerProfile: customerData, endCustomerId: selectedEndCustomerId, endCustomer: selectedEndCustomer, }) router.push(`/order/success?id=${order.id}`) } catch (error) { console.error(error) alert('Fehler bei der Bestellung. Bitte versuchen Sie es erneut.') setIsSubmitting(false) // Nur bei Fehler wieder entsperren } } return (
{/* Progress Stepper */}
{[1, 2, 3].map(s => (
= s ? 'bg-primary text-white scale-110 shadow-[0_0_15px_rgba(59,130,246,0.5)]' : 'bg-slate-800 text-slate-400' }`} > {step > s ? : s}
))}
{/* ───── Step 1: Product Selection per Category ───── */} {step === 1 && (
{/* Left: Category sections */}
Software wählen Wählen Sie pro Kategorie mindestens einen Artikel aus. {categories.map((cat, idx) => { const catProducts = products.filter(p => p.category_id === cat.id) const sel = selections[cat.id] const selectedProduct = catProducts.find(p => p.id === sel?.productId) ?? null return (
{idx > 0 && } {/* Category header */}

{cat.name}

{cat.description && (

{cat.description}

)}
{sel?.productId ? ( Ausgewählt ) : cat.is_required ? ( Pflichtfeld ) : ( Optional )}
{catProducts.length === 0 ? (

Keine Produkte in dieser Kategorie vorhanden.

) : ( selectProduct(cat.id, id)} className="grid gap-3" > {catProducts.map(product => (
))}
)} {/* Modules for selected product in this category */} {selectedProduct?.modules && selectedProduct.modules.length > 0 && (

Zusatzmodule:

{selectedProduct.modules.map(module => { const disabled = isModuleDisabled(module, sel?.moduleIds ?? []) const checked = sel?.moduleIds.includes(module.id) ?? false return (
toggleModule(cat.id, module.id)} disabled={disabled} />
{module.description && (

{module.description}

)} {disabled && (

{module.requirements?.some( reqId => !sel?.moduleIds.includes(reqId) ) ? 'Benötigt weitere Module' : 'Nicht kombinierbar mit aktueller Auswahl'}

)}
) })}
)}
) })}
{/* Right: Summary */}
Zusammenfassung {categories.map(cat => { const sel = selections[cat.id] const prod = products.find(p => p.id === sel?.productId) if (!prod) return (
{cat.is_required ? ( ) : ( )} {cat.name}: {cat.is_required ? 'nicht gewählt' : 'nicht gewählt (optional)'}
) return (
{cat.name}
{prod.name} {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(prod.base_price)} {' '}{billingLabel(prod.billing_interval)}
{sel.moduleIds.map(mId => { const mod = prod.modules?.find(m => m.id === mId) return mod ? (
+ {mod.name} {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(mod.price)}
) : null })}
) })}
Gesamt: {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalPrice)}
{!allCategoriesFilled && (

Bitte aus jeder Pflichtkategorie einen Artikel wählen.

)}
)} {/* ───── Step 2: Endkunde wählen / anlegen ───── */} {step === 2 && ( Für wen bestellen Sie? Wählen Sie einen Endkunden aus Ihrem CRM oder legen Sie einen neuen an. {/* Modus-Toggle */}
{/* Modus A: Bestandskunde wählen */} {customerMode === 'select' && (
{endCustomers.length === 0 ? (

Noch keine Endkunden angelegt.

) : (
{endCustomers.filter(c => !c.is_anonymized).map(customer => ( ))}
)}
)} {/* Modus B: Neuen Kunden anlegen */} {customerMode === 'create' && (
{([ { label: 'Firmenname *', key: 'company_name', span: true, placeholder: 'GmbH / Einzelunternehmen' }, { label: 'USt-IdNr.', key: 'vat_id', span: false, placeholder: 'DE123456789' }, { label: 'Vorname', key: 'first_name', span: false, placeholder: '' }, { label: 'Nachname', key: 'last_name', span: false, placeholder: '' }, { label: 'Straße & Hausnummer', key: 'street', span: true, placeholder: '' }, { label: 'PLZ', key: 'zip', span: false, placeholder: '' }, { label: 'Ort', key: 'city', span: false, placeholder: '' }, ] as const).map(({ label, key, span, placeholder }) => (
setNewCustomerForm(prev => ({ ...prev, [key]: e.target.value }))} placeholder={placeholder} className="bg-white/5 border-white/10 text-white placeholder:text-slate-500" />
))}
)}
)} {/* ───── Step 3: Review & Submit ───── */} {step === 3 && (
Bestellung prüfen Fast fertig! Bitte überprüfen Sie Ihre Auswahl.
{categories.map(cat => { const sel = selections[cat.id] const prod = products.find(p => p.id === sel?.productId) if (!prod) return null const catTotal = Number(prod.base_price) + sel.moduleIds.reduce((acc, mId) => { const mod = prod.modules?.find(m => m.id === mId) return acc + Number(mod?.price ?? 0) }, 0) return (
{prod.name} {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(catTotal)} {' '}{billingLabel(prod.billing_interval)}
{sel.moduleIds.length > 0 && (

Module: {sel.moduleIds .map(id => prod.modules?.find(m => m.id === id)?.name) .filter(Boolean) .join(', ')}

)}
) })}
{selectedEndCustomer ? ( <>

{selectedEndCustomer.company_name}

{[selectedEndCustomer.first_name, selectedEndCustomer.last_name].filter(Boolean).join(' ')}

{[selectedEndCustomer.street, selectedEndCustomer.zip, selectedEndCustomer.city].filter(Boolean).join(', ')}

Endkunde Ihres Partners

) : ( <>

{customerData.company_name}

{customerData.first_name} {customerData.last_name}

{customerData.address}, {customerData.zip_code} {customerData.city}

)}
Gesamtbetrag: {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(totalPrice)} / Monat

Mit dem Klick auf „Kostenpflichtig bestellen" akzeptieren Sie unsere AGB und die Datenschutzerklärung.

)}
) }