'use client' import { useState } from 'react' import { useRouter } from 'next/navigation' import { useForm, useFieldArray } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import * as z from 'zod' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { Plus, Trash2, X, PlusCircle } from 'lucide-react' import { createProduct, updateProduct } from '@/lib/actions/products' import { ScrollArea } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' import { Category, Product } from '@/lib/types' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" const moduleSchema = z.object({ id: z.string().optional(), name: z.string().min(2, 'Name muss mindestens 2 Zeichen lang sein'), description: z.string().optional(), price: z.coerce.number().min(0, 'Preis darf nicht negativ sein'), is_required: z.boolean().default(false), has_quantity: z.boolean().default(false), requirements: z.array(z.string()).default([]), exclusions: z.array(z.string()).default([]), }) const productSchema = z.object({ name: z.string().min(2, 'Name muss mindestens 2 Zeichen lang sein'), description: z.string().optional(), base_price: z.coerce.number().min(0, 'Preis darf nicht negativ sein'), category_id: z.string().min(1, 'Bitte wählen Sie eine Kategorie'), billing_interval: z.enum(['one_time', 'monthly']).default('monthly'), show_in_kauf: z.boolean().default(true), show_in_abo: z.boolean().default(true), requirements: z.array(z.string()).default([]), exclusions: z.array(z.string()).default([]), modules: z.array(moduleSchema).default([]), allow_update_discount: z.boolean().default(true), }) type ProductFormValues = z.infer export function CreateProductDialog({ children, categories, product, products = [] }: { children?: React.ReactNode categories: Category[] product?: Product products?: Product[] }) { const [open, setOpen] = useState(false) const [step, setStep] = useState(1) const router = useRouter() function handleOpenChange(isOpen: boolean) { setOpen(isOpen) if (isOpen) { setStep(1) } } async function nextStep() { let isValid = false if (step === 1) { isValid = await form.trigger(['name', 'category_id', 'base_price']) } else if (step === 2) { isValid = await form.trigger('billing_interval') } else if (step === 3) { isValid = await form.trigger(['show_in_kauf', 'show_in_abo']) } else if (step === 4) { isValid = await form.trigger(['requirements', 'exclusions']) } if (isValid || step === 5) { setStep(prev => Math.min(prev + 1, 5)) } } const otherProducts = products.filter(p => p.id !== product?.id) const form = useForm({ resolver: zodResolver(productSchema) as any, defaultValues: { name: product?.name || '', description: product?.description || '', base_price: product?.base_price || 0, category_id: product?.category_id || '', billing_interval: product?.billing_interval || 'monthly', show_in_kauf: product ? (product.show_in_kauf ?? true) : true, show_in_abo: product ? (product.show_in_abo ?? true) : true, requirements: product?.requirements || [], exclusions: product?.exclusions || [], allow_update_discount: product ? (product.allow_update_discount ?? true) : true, modules: product?.modules?.map(m => ({ id: m.id, name: m.name, description: m.description || '', price: m.price, is_required: false, has_quantity: m.has_quantity ?? false, requirements: m.requirements || [], exclusions: m.exclusions || [], })) || [], }, }) const { fields, append, remove } = useFieldArray({ name: 'modules', control: form.control, }) async function onSubmit(values: ProductFormValues) { try { const { modules, ...productData } = values const formattedModules = modules.map(m => ({ ...m, id: m.id || (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2, 15)), })) if (product) { await updateProduct(product.id, productData as any, formattedModules as any) } else { await createProduct(productData as any, formattedModules as any) } setOpen(false) if (!product) form.reset() router.refresh() } catch (error) { console.error('Error saving product:', error) } } return ( {children || ( )} {product ? 'Produkt bearbeiten' : 'Neues Produkt erstellen'} Definieren Sie das Basisprodukt und fügen Sie optionale oder erforderliche Module hinzu.
{/* Step Indicator */}
{[ { nr: 1, label: 'Allgemein' }, { nr: 2, label: 'Abrechnung' }, { nr: 3, label: 'Sichtbarkeit' }, { nr: 4, label: 'Abhängigkeiten' }, { nr: 5, label: 'Module' } ].map(s => (
s.nr ? 'bg-green-500 text-white' : 'bg-slate-200 dark:bg-slate-800 text-slate-500' }`}> {s.nr}
{s.label}
))}
{step === 1 && (
( Produktname )} /> ( Basispreis (€) )} />
( Kategorie )} /> ( Beschreibung )} />
)} {step === 2 && (
( Abrechnungsmodell
{[ { value: 'one_time', label: 'Einmalig', icon: '💳' }, { value: 'monthly', label: 'Monatlich', icon: '🔄' }, ].map(opt => ( ))}
)} />
)} {step === 3 && (
(
In Kauf-Auswahl anzeigen Produkt im Kauf-Zweig anzeigen
)} /> (
In Abo-Auswahl anzeigen Produkt im Abo-Zweig anzeigen
)} />
{form.watch('billing_interval') === 'one_time' && ( (
Update-Rabatt erlauben Kunden erhalten für dieses Produkt den selektiven Update-Rabatt.
)} /> )}
)} {step === 4 && (
{/* Product Requirements */} ( Benötigte Artikel {otherProducts.length === 0 ? (

Keine anderen Artikel vorhanden.

) : (
{otherProducts.map(other => { const checked = (reqField.value || []).includes(other.id) return (
{ const currentVal = reqField.value || [] if (checkedState) { reqField.onChange([...currentVal, other.id]) } else { reqField.onChange(currentVal.filter(id => id !== other.id)) } }} />
) })}
)}
)} /> {/* Product Exclusions */} ( Schließt aus {otherProducts.length === 0 ? (

Keine anderen Artikel vorhanden.

) : (
{otherProducts.map(other => { const checked = (exclField.value || []).includes(other.id) return (
{ const currentVal = exclField.value || [] if (checkedState) { exclField.onChange([...currentVal, other.id]) } else { exclField.onChange(currentVal.filter(id => id !== other.id)) } }} />
) })}
)}
)} />
)} {step === 5 && (

Module

{fields.map((field, index) => { const allModules = form.watch('modules') || [] const otherModules = allModules .map((m, idx) => ({ id: m.id || field.id, name: m.name || `Modul ${idx + 1}`, })) .filter((_, idx) => idx !== index) return (
( Modulname )} /> ( Preis (€/Monat) )} />
( Beschreibung )} /> {otherModules.length > 0 && (
( Erfordert Module
{otherModules.map(other => { const checked = (mReqField.value || []).includes(other.id) return (
{ const currentVal = mReqField.value || [] if (checkedState) { mReqField.onChange([...currentVal, other.id]) } else { mReqField.onChange(currentVal.filter(id => id !== other.id)) } }} />
) })}
)} /> ( Schließt Module aus
{otherModules.map(other => { const checked = (mExclField.value || []).includes(other.id) return (
{ const currentVal = mExclField.value || [] if (checkedState) { mExclField.onChange([...currentVal, other.id]) } else { mExclField.onChange(currentVal.filter(id => id !== other.id)) } }} />
) })}
)} />
)}
( Erforderlich )} /> ( Mengeneingabe erlauben )} />
) })} {fields.length === 0 && (
Noch keine Module hinzugefügt.
)}
)}
{step > 1 ? ( ) : ( )}
{step < 5 ? ( ) : ( )}
) }