'use client' import { useEffect, useState } from 'react' import { useForm, useWatch } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { toast } from 'sonner' import { cn } from '@/lib/utils' import { Dialog, DialogContent, DialogTitle, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { useDirtyCloseGuard, DirtyCloseGuardDialog, } from '@/components/shared/use-dirty-close-guard' import { useDialogSubmitShortcut } from '@/components/shared/use-dialog-submit-shortcut' import { entityDialogBodyClasses, entityDialogContentClasses, entityDialogFooterClasses, entityDialogHeaderClasses, } from '@/components/shared/entity-dialog-layout' import { productSchema, type ProductInput } from '@/lib/schemas/product' import { createProductAction, updateProductAction } from '@/actions/products' import { useProductsStore } from '@/stores/products-store' import { debugProps } from '@/lib/debug' export interface ProductDialogProduct { id: string name: string code?: string | null description?: string | null repo_url?: string | null definition_of_done?: string | null auto_pr?: boolean } type Props = | { mode: 'create'; open: boolean; onOpenChange: (v: boolean) => void; onSaved?: (id: string) => void; isDemo?: boolean } | { mode: 'edit'; open: boolean; onOpenChange: (v: boolean) => void; product: ProductDialogProduct; onSaved?: (id: string) => void; isDemo?: boolean } export function ProductDialog(props: Props) { const { mode, open, onOpenChange, isDemo = false } = props const product = mode === 'edit' ? props.product : null const addProduct = useProductsStore((s) => s.addProduct) const updateProduct = useProductsStore((s) => s.updateProduct) const [isPending, setIsPending] = useState(false) const form = useForm({ resolver: zodResolver(productSchema), mode: 'onTouched', defaultValues: { name: product?.name ?? '', code: product?.code ?? '', description: product?.description ?? '', repo_url: product?.repo_url ?? '', definition_of_done: product?.definition_of_done ?? '', auto_pr: product?.auto_pr ?? false, }, }) // Reset when opening or switching product useEffect(() => { if (open) { form.reset({ name: product?.name ?? '', code: product?.code ?? '', description: product?.description ?? '', repo_url: product?.repo_url ?? '', definition_of_done: product?.definition_of_done ?? '', auto_pr: product?.auto_pr ?? false, }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, product?.id]) const closeGuard = useDirtyCloseGuard(form.formState.isDirty, () => onOpenChange(false)) const handleKeyDown = useDialogSubmitShortcut(() => form.handleSubmit(onSubmit)()) async function onSubmit(values: ProductInput) { setIsPending(true) try { const payload: ProductInput = { name: values.name, code: values.code || undefined, description: values.description || undefined, repo_url: values.repo_url || null, definition_of_done: values.definition_of_done || undefined, auto_pr: values.auto_pr, } function applyError(result: { error: string; code?: number; fieldErrors?: Partial> }) { if (result.code === 422 && result.fieldErrors) { for (const [field, errors] of Object.entries(result.fieldErrors)) { if (errors && errors.length > 0) { form.setError(field as keyof ProductInput, { message: errors[0] }) } } const firstError = Object.keys(result.fieldErrors)[0] as keyof ProductInput | undefined if (firstError) form.setFocus(firstError) return } toast.error(result.error) } if (mode === 'create') { const result = await createProductAction(payload) if ('error' in result) { applyError(result) return } const productId = result.productId addProduct({ id: productId, name: values.name, code: values.code ?? null, description: values.description ?? null, repo_url: values.repo_url ?? null, definition_of_done: values.definition_of_done ?? '', auto_pr: values.auto_pr, }) toast.success('Product aangemaakt') onOpenChange(false) props.onSaved?.(productId) } else { const result = await updateProductAction(product!.id, payload) if ('error' in result) { applyError(result) return } updateProduct(product!.id, { name: values.name, code: values.code ?? null, description: values.description ?? null, repo_url: values.repo_url ?? null, definition_of_done: values.definition_of_done ?? '', auto_pr: values.auto_pr, }) toast.success('Product opgeslagen') onOpenChange(false) props.onSaved?.(product!.id) } } finally { setIsPending(false) } } const autoPr = useWatch({ control: form.control, name: 'auto_pr' }) return ( <> { if (!v) closeGuard.attemptClose(); else onOpenChange(v) }}>
{mode === 'edit' ? 'Product bewerken' : 'Nieuw product'}
{form.formState.errors.name && (

{form.formState.errors.name.message}

)}
{form.formState.errors.code && (

{form.formState.errors.code.message}

)}