223 lines
8.5 KiB
TypeScript
223 lines
8.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { useForm } 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,
|
|
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 { createCategory, updateCategory } from '@/lib/actions/products'
|
|
import { Category } from '@/lib/types'
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue
|
|
} from '@/components/ui/select'
|
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
|
import { Cloud, Utensils, HardDrive, Package } from 'lucide-react'
|
|
|
|
const categorySchema = z.object({
|
|
name: z.string().min(2, 'Name muss mindestens 2 Zeichen lang sein'),
|
|
description: z.string().optional(),
|
|
icon: z.string().default('Package'),
|
|
sort_order: z.coerce.number().int().min(0).default(0),
|
|
is_required: z.boolean().default(true),
|
|
})
|
|
|
|
type CategoryFormValues = z.infer<typeof categorySchema>
|
|
|
|
export function CategoryDialog({
|
|
children,
|
|
category
|
|
}: {
|
|
children?: React.ReactNode,
|
|
category?: Category
|
|
}) {
|
|
const [open, setOpen] = useState(false)
|
|
const router = useRouter()
|
|
|
|
const form = useForm<CategoryFormValues>({
|
|
resolver: zodResolver(categorySchema) as any,
|
|
defaultValues: {
|
|
name: category?.name || '',
|
|
description: category?.description || '',
|
|
icon: category?.icon || 'Package',
|
|
sort_order: category?.sort_order ?? 0,
|
|
is_required: category?.is_required ?? true,
|
|
},
|
|
})
|
|
|
|
async function onSubmit(values: CategoryFormValues) {
|
|
try {
|
|
if (category) {
|
|
await updateCategory(category.id, values)
|
|
} else {
|
|
await createCategory(values)
|
|
}
|
|
setOpen(false)
|
|
form.reset()
|
|
router.refresh()
|
|
} catch (error) {
|
|
console.error('Error saving category:', error)
|
|
}
|
|
}
|
|
|
|
const icons = [
|
|
{ name: 'Cloud', Icon: Cloud },
|
|
{ name: 'Utensils', Icon: Utensils },
|
|
{ name: 'HardDrive', Icon: HardDrive },
|
|
{ name: 'Package', Icon: Package },
|
|
]
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
{children || (
|
|
<Button className="bg-primary hover:bg-primary/90">
|
|
Kategorie hinzufügen
|
|
</Button>
|
|
)}
|
|
</DialogTrigger>
|
|
<DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>{category ? 'Kategorie bearbeiten' : 'Neue Kategorie erstellen'}</DialogTitle>
|
|
<DialogDescription className="text-slate-500 dark:text-slate-400">
|
|
Definieren Sie eine Kategorie für Ihre Produkte.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 flex-1 overflow-hidden flex flex-col min-h-0">
|
|
<ScrollArea className="max-h-[60vh] pr-4">
|
|
<div className="space-y-4 py-2">
|
|
<FormField
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel className="text-slate-900 dark:text-white">Name</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="z.B. Software" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="description"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel className="text-slate-900 dark:text-white">Beschreibung</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="Kurze Beschreibung" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="icon"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel className="text-slate-900 dark:text-white">Icon</FormLabel>
|
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
<FormControl>
|
|
<SelectTrigger className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
|
|
<SelectValue placeholder="Icon wählen" />
|
|
</SelectTrigger>
|
|
</FormControl>
|
|
<SelectContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
|
|
{icons.map(({ name, Icon }) => (
|
|
<SelectItem key={name} value={name}>
|
|
<div className="flex items-center gap-2">
|
|
<Icon className="w-4 h-4" />
|
|
{name}
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="sort_order"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel className="text-slate-900 dark:text-white">Reihenfolge im Bestellvorgang</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="number"
|
|
min={0}
|
|
placeholder="0 = zuerst"
|
|
className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500"
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
<p className="text-xs text-slate-500 dark:text-slate-400">Kleinere Zahl = weiter oben im Bestellformular angezeigt.</p>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="is_required"
|
|
render={({ field }) => (
|
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-lg border border-slate-200 dark:border-white/10 bg-slate-50 dark:bg-white/5 p-4">
|
|
<FormControl>
|
|
<Checkbox
|
|
checked={field.value}
|
|
onCheckedChange={field.onChange}
|
|
/>
|
|
</FormControl>
|
|
<div className="space-y-1 leading-none">
|
|
<FormLabel className="text-slate-900 dark:text-white font-semibold cursor-pointer">
|
|
Pflichtauswahl im Bestellvorgang
|
|
</FormLabel>
|
|
<p className="text-xs text-slate-500 dark:text-slate-400">
|
|
Wenn aktiv, muss der Kunde aus dieser Kategorie einen Artikel wählen.
|
|
Wenn deaktiviert, ist die Kategorie optional.
|
|
</p>
|
|
</div>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</div>
|
|
</ScrollArea>
|
|
<DialogFooter className="pt-4">
|
|
<Button type="submit" className="w-full bg-primary hover:bg-primary/90 text-white">
|
|
{category ? 'Aktualisieren' : 'Erstellen'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</Form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|