feat: implement quote editing for admins and creators

This commit is contained in:
DanielS
2026-07-03 00:22:10 +02:00
parent 023c789baf
commit e3cdf6683a
5 changed files with 359 additions and 42 deletions

View File

@@ -3,7 +3,7 @@
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 { Product, ProductModule, Profile, EndCustomer, Order } 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'
@@ -12,7 +12,7 @@ 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, CreditCard, Calendar, Search } from 'lucide-react'
import { submitOrder } from '@/lib/actions/orders'
import { submitOrder, updateOrder } from '@/lib/actions/orders'
import { createEndCustomer } from '@/lib/actions/end-customers'
import { Category } from '@/lib/types'
import { Badge } from '@/components/ui/badge'
@@ -80,11 +80,13 @@ export function OrderWizard({
categories,
initialProfile,
initialEndCustomers,
initialOrder,
}: {
products: Product[]
categories: Category[]
initialProfile: Profile | null
initialEndCustomers: EndCustomer[]
initialOrder?: Order | null
}) {
const router = useRouter()
const [step, setStep] = useState(1)
@@ -96,7 +98,9 @@ export function OrderWizard({
// Endkunden-State
const [endCustomers, setEndCustomers] = useState<EndCustomer[]>(initialEndCustomers)
const [selectedEndCustomerId, setSelectedEndCustomerId] = useState<string | null>(
initialEndCustomers.length > 0 ? initialEndCustomers[0].id : null
initialOrder
? initialOrder.end_customer_id
: (initialEndCustomers.length > 0 ? initialEndCustomers[0].id : null)
)
const selectedEndCustomer = endCustomers.find(c => c.id === selectedEndCustomerId) ?? null
@@ -134,13 +138,26 @@ export function OrderWizard({
})
// Abrechnungsmodell-Auswahl ('one_time' = Kauf, 'monthly' = Abo)
const [selectedBillingInterval, setSelectedBillingInterval] = useState<'one_time' | 'monthly'>('monthly')
const [selectedBillingInterval, setSelectedBillingInterval] = useState<'one_time' | 'monthly'>(
initialOrder?.order_data?.billing_cycle ?? 'monthly'
)
// Modulmengen (moduleId -> Menge)
const [moduleQuantities, setModuleQuantities] = useState<Record<string, number>>({})
const [moduleQuantities, setModuleQuantities] = useState<Record<string, number>>(() => {
if (!initialOrder) return {}
const quantities: Record<string, number> = {}
initialOrder.order_data?.items?.forEach(item => {
item.selected_modules?.forEach(mod => {
quantities[mod.module_id] = mod.quantity ?? 1
})
})
return quantities
})
// Datum der letzten Lizenz (Bestandskunden-Update-Logik)
const [lastLicenseDate, setLastLicenseDate] = useState<string>("")
const [lastLicenseDate, setLastLicenseDate] = useState<string>(
initialOrder?.order_data?.last_license_date ?? ""
)
const lastLicenseMonths = useMemo(() => {
if (!lastLicenseDate || customerMode !== 'select' || !selectedEndCustomerId) return null
@@ -174,6 +191,16 @@ export function OrderWizard({
const [selections, setSelections] = useState<Record<string, CategorySelection>>(() => {
const init: Record<string, CategorySelection> = {}
categories.forEach(cat => {
if (initialOrder) {
const item = initialOrder.order_data?.items?.find(i => i.category_id === cat.id)
if (item) {
init[cat.id] = {
productId: item.product_id,
moduleIds: item.selected_modules?.map(m => m.module_id) ?? []
}
return
}
}
const first = products.find(p => p.category_id === cat.id && p.show_in_abo !== false)
init[cat.id] = { productId: first?.id ?? null, moduleIds: [] }
})
@@ -335,21 +362,34 @@ export function OrderWizard({
if (isSubmitting) return // Doppelklick-Guard
setIsSubmitting(true)
try {
const order = await submitOrder({
selections,
moduleQuantities,
products,
categories,
customerProfile: customerData,
endCustomerId: selectedEndCustomerId,
endCustomer: selectedEndCustomer,
billingInterval: selectedBillingInterval,
lastLicenseDate: lastLicenseDate || null,
})
let order
if (initialOrder) {
order = await updateOrder(initialOrder.id, {
selections,
moduleQuantities,
customerProfile: customerData,
endCustomerId: selectedEndCustomerId,
endCustomer: selectedEndCustomer,
billingInterval: selectedBillingInterval,
lastLicenseDate: lastLicenseDate || null,
})
} else {
order = await submitOrder({
selections,
moduleQuantities,
products,
categories,
customerProfile: customerData,
endCustomerId: selectedEndCustomerId,
endCustomer: selectedEndCustomer,
billingInterval: selectedBillingInterval,
lastLicenseDate: lastLicenseDate || null,
})
}
router.push(`/order/success?id=${order.id}`)
} catch (error) {
} catch (error: any) {
console.error(error)
alert('Fehler bei der Bestellung. Bitte versuchen Sie es erneut.')
alert(error?.message || (initialOrder ? 'Fehler beim Aktualisieren des Angebots.' : 'Fehler bei der Bestellung. Bitte versuchen Sie es erneut.'))
setIsSubmitting(false) // Nur bei Fehler wieder entsperren
}
}
@@ -1106,9 +1146,13 @@ export function OrderWizard({
disabled={isSubmitting}
>
{isSubmitting ? (
<><Loader2 className="mr-2 h-5 w-5 animate-spin" /> Bestellung wird verarbeitet...</>
initialOrder ? (
<><Loader2 className="mr-2 h-5 w-5 animate-spin" /> Angebot wird aktualisiert...</>
) : (
<><Loader2 className="mr-2 h-5 w-5 animate-spin" /> Bestellung wird verarbeitet...</>
)
) : (
'Kostenpflichtig bestellen'
initialOrder ? 'Angebot aktualisieren' : 'Kostenpflichtig bestellen'
)}
</Button>
<Button variant="ghost" className="w-full text-white" onClick={prevStep}>