'use client' import { useEffect, useRef, useState } from 'react' import { useActionState } from 'react' import { toast } from 'sonner' 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 { PrioritySelect } from '@/components/shared/priority-select' import { PbiStatusSelect } from '@/components/shared/pbi-status-select' 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 { createPbiAction, updatePbiAction } from '@/actions/pbis' import type { PbiStatusApi } from '@/lib/task-status' import { debugProps } from '@/lib/debug' export interface PbiDialogPbi { id: string title: string priority: number description?: string | null code?: string | null status?: PbiStatusApi } type CreateState = { mode: 'create'; productId: string; defaultPriority?: number } type EditState = { mode: 'edit'; pbi: PbiDialogPbi; productId: string } export type PbiDialogState = CreateState | EditState interface PbiDialogProps { state: PbiDialogState | null onClose: () => void isDemo?: boolean } interface ActionResult { success?: boolean error?: string code?: number fieldErrors?: Record pbi?: unknown } export function PbiDialog({ state, onClose, isDemo = false }: PbiDialogProps) { const isEdit = state?.mode === 'edit' const pbi = isEdit ? state.pbi : null const initialPriority = isEdit ? pbi!.priority : (state?.defaultPriority ?? 2) const [priority, setPriority] = useState(initialPriority) const initialStatus: PbiStatusApi = isEdit ? (pbi!.status ?? 'ready') : 'ready' const [status, setStatus] = useState(initialStatus) const [dirty, setDirty] = useState(false) const formRef = useRef(null) // Sync priority + status + reset dirty when dialog opens for a different PBI or switches mode useEffect(() => { if (state) { // eslint-disable-next-line react-hooks/set-state-in-effect setPriority(isEdit ? (state as EditState).pbi.priority : ((state as CreateState).defaultPriority ?? 2)) setStatus(isEdit ? ((state as EditState).pbi.status ?? 'ready') : 'ready') setDirty(false) } }, [state, isEdit]) const [createState, createAction, createPending] = useActionState( async (_prev, fd) => { const result = await createPbiAction(_prev, fd) as ActionResult if (result?.success) { toast.success('PBI aangemaakt'); onClose() } else if (result?.code !== 422 && result?.error) toast.error(result.error) return result }, undefined, ) const [updateState, updateAction, updatePending] = useActionState( async (_prev, fd) => { const result = await updatePbiAction(_prev, fd) as ActionResult if (result?.success) { toast.success('PBI opgeslagen'); onClose() } else if (result?.code !== 422 && result?.error) toast.error(result.error) return result }, undefined, ) const pending = isEdit ? updatePending : createPending const activeState = isEdit ? updateState : createState const fieldError = (field: string) => activeState?.fieldErrors?.[field]?.[0] const titleRef = useRef(null) useEffect(() => { if (state) { setTimeout(() => titleRef.current?.focus(), 50) } }, [state]) const closeGuard = useDirtyCloseGuard(dirty, onClose) const handleKeyDown = useDialogSubmitShortcut(() => formRef.current?.requestSubmit()) return ( <> { if (!open) closeGuard.attemptClose() }}>
{isEdit ? 'PBI bewerken' : 'Nieuw PBI'}
setDirty(true)} className={entityDialogBodyClasses} > {isEdit && } {!isEdit && }
{fieldError('code') &&

{fieldError('code')}

}
{fieldError('title') &&

{fieldError('title')}

}
{ setPriority(v); setDirty(true) }} />
{ setStatus(v); setDirty(true) }} />