'use client' // ST-1105: Modal waar de gebruiker een Claude-vraag beantwoordt (M11). // // Free-text Textarea (max ANSWER_MAX_CHARS) of multiple-choice via knoppen // wanneer de vraag `options` heeft. Submit roept answerQuestion-Server-Action // aan via useTransition; bij succes wordt de vraag uit de store verwijderd // (optimistisch) en sluit de modal. Demo-modus: textarea readOnly + submit // disabled met DemoTooltip. import { useState, useTransition } from 'react' import Link from 'next/link' import { ExternalLink } from 'lucide-react' import { toast } from 'sonner' import { Dialog, DialogContent, DialogDescription, DialogTitle, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' 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 { entityDialogContentClasses, entityDialogFooterClasses, entityDialogHeaderClasses, } from '@/components/shared/entity-dialog-layout' import { ANSWER_MAX_CHARS } from '@/lib/schemas/question-answer' import { answerQuestion } from '@/actions/questions' import { useNotificationsStore, type NotificationQuestion } from '@/stores/notifications-store' import { debugProps } from '@/lib/debug' interface AnswerModalProps { question: NotificationQuestion | null isDemo: boolean onClose: () => void } export function AnswerModal({ question, isDemo, onClose }: AnswerModalProps) { const [answer, setAnswer] = useState('') const [pending, startTransition] = useTransition() const closeGuard = useDirtyCloseGuard(answer.trim().length > 0, () => { setAnswer('') onClose() }) const charsLeft = ANSWER_MAX_CHARS - answer.length const tooLong = charsLeft < 0 const submitDisabled = isDemo || pending || answer.trim().length === 0 || tooLong function submit(text: string) { if (!question) return startTransition(async () => { const res = await answerQuestion(question.id, text) if (!res.ok) { toast.error(res.error) return } useNotificationsStore.getState().remove(question.id) toast.success('Antwoord verstuurd') setAnswer('') onClose() }) } const handleKeyDown = useDialogSubmitShortcut(() => { if (!submitDisabled) submit(answer) }) if (!question) return null return ( <> { if (!open) closeGuard.attemptClose() }}> Beantwoord Claude {question.kind === 'idea' ? question.idea_code : (question.story_code ?? 'story')} {' — '} {question.kind === 'idea' ? question.idea_title : question.story_title} {question.kind === 'idea' ? 'Open idee' : 'Open in Sprint'} {question.question} {question.options?.length ? ( Kies een van de opties: {question.options.map((opt) => ( submit(opt)} > {opt} ))} ) : null} {question.options?.length ? ( Of typ een eigen antwoord ) : null} setAnswer(e.target.value)} placeholder="Typ je antwoord…" rows={5} maxLength={ANSWER_MAX_CHARS} disabled={isDemo} aria-label="Antwoord op Claude's vraag" /> {charsLeft} tekens over Annuleren submit(answer)} disabled={submitDisabled} > {pending ? 'Bezig…' : 'Verstuur'} > ) }
Kies een van de opties:
Of typ een eigen antwoord