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