feat: complete full CRUD for products with unified edit dialog and robust mapping
This commit is contained in:
@@ -26,7 +26,7 @@ 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 } from '@/lib/actions/products'
|
||||
import { createProduct, updateProduct } from '@/lib/actions/products'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Category } from '@/lib/types'
|
||||
@@ -58,18 +58,25 @@ const productSchema = z.object({
|
||||
|
||||
type ProductFormValues = z.infer<typeof productSchema>
|
||||
|
||||
export function CreateProductDialog({ categories }: { categories: Category[] }) {
|
||||
export function CreateProductDialog({ children, categories, product }: { children?: React.ReactNode, categories: Category[], product?: Product }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const form = useForm<ProductFormValues>({
|
||||
resolver: zodResolver(productSchema) as any,
|
||||
defaultValues: {
|
||||
name: '',
|
||||
description: '',
|
||||
base_price: 0,
|
||||
category_id: '',
|
||||
is_active: true,
|
||||
modules: [],
|
||||
name: product?.name || '',
|
||||
description: product?.description || '',
|
||||
base_price: product?.base_price || 0,
|
||||
category_id: product?.category_id || '',
|
||||
is_active: product?.is_active ?? true,
|
||||
modules: product?.modules?.map(m => ({
|
||||
name: m.name,
|
||||
description: m.description || '',
|
||||
price: m.price,
|
||||
is_required: m.is_required,
|
||||
requirements: m.requirements?.join(', ') || '',
|
||||
exclusions: m.exclusions?.join(', ') || '',
|
||||
})) || [],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -86,24 +93,34 @@ export function CreateProductDialog({ categories }: { categories: Category[] })
|
||||
requirements: m.requirements ? m.requirements.split(',').map(s => s.trim()).filter(Boolean) : [],
|
||||
exclusions: m.exclusions ? m.exclusions.split(',').map(s => s.trim()).filter(Boolean) : [],
|
||||
}))
|
||||
await createProduct(productData as any, formattedModules as any)
|
||||
|
||||
if (product) {
|
||||
await updateProduct(product.id, productData as any, formattedModules as any)
|
||||
} else {
|
||||
await createProduct(productData as any, formattedModules as any)
|
||||
}
|
||||
|
||||
setOpen(false)
|
||||
form.reset()
|
||||
if (!product) form.reset()
|
||||
} catch (error) {
|
||||
console.error('Error creating product:', error)
|
||||
console.error('Error saving product:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="bg-primary hover:bg-primary/90">
|
||||
<Plus className="mr-2 h-4 w-4" /> Produkt hinzufügen
|
||||
</Button>
|
||||
{children || (
|
||||
<Button className="bg-primary hover:bg-primary/90">
|
||||
<Plus className="mr-2 h-4 w-4" /> Produkt hinzufügen
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-hidden flex flex-col glass-dark border-white/10 text-white shadow-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl font-bold">Neues Produkt erstellen</DialogTitle>
|
||||
<DialogTitle className="text-2xl font-bold">
|
||||
{product ? 'Produkt bearbeiten' : 'Neues Produkt erstellen'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
Definieren Sie das Basisprodukt und fügen Sie optionale oder erforderliche Module hinzu.
|
||||
</DialogDescription>
|
||||
@@ -111,7 +128,7 @@ export function CreateProductDialog({ categories }: { categories: Category[] })
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 flex-1 overflow-hidden flex flex-col">
|
||||
<ScrollArea className="flex-1 pr-4">
|
||||
<ScrollArea className="flex-1 max-h-[60vh] pr-4">
|
||||
<div className="space-y-6 py-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormField
|
||||
|
||||
@@ -15,8 +15,10 @@ import { Edit2, Trash2, Layers } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { deleteProduct } from '@/lib/actions/products'
|
||||
import { useState } from 'react'
|
||||
import { CreateProductDialog } from './create-product-dialog'
|
||||
import { Category } from '@/lib/types'
|
||||
|
||||
export function ProductList({ initialProducts }: { initialProducts: Product[] }) {
|
||||
export function ProductList({ initialProducts, categories }: { initialProducts: Product[], categories: Category[] }) {
|
||||
const [products, setProducts] = useState(initialProducts)
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
@@ -75,9 +77,11 @@ export function ProductList({ initialProducts }: { initialProducts: Product[] })
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-white">
|
||||
<Edit2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<CreateProductDialog categories={categories} product={product}>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-white">
|
||||
<Edit2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</CreateProductDialog>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
||||
@@ -107,8 +107,8 @@ export async function updateProduct(id: string, product: Partial<Product>, modul
|
||||
if (modules.length > 0) {
|
||||
const modulesWithId = modules.map(m => ({
|
||||
name: m.name,
|
||||
description: m.description,
|
||||
price: m.additional_price, // Match schema column name 'price'
|
||||
description: m.description || null,
|
||||
price: m.price,
|
||||
is_required: m.is_required,
|
||||
requirements: m.requirements || [],
|
||||
exclusions: m.exclusions || [],
|
||||
|
||||
Reference in New Issue
Block a user