import { useMemo, useState } from 'react'; import { ArrowLeft } from 'lucide-react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../../../../components/ui/dialog'; import { SelectControl } from '../../../../components/ui/select-control'; import type { ExtendedQuestionnaireItem } from '../../../../services'; import { currentPlacement, fieldName, moveDestinations, movePositions } from './builderFields'; // Radix Select cannot carry null or an empty string, so the top level travels as a sentinel. const ROOT = '__outside_all_groups__'; interface Props { tree: ExtendedQuestionnaireItem[]; field: ExtendedQuestionnaireItem; /** A draft has no answers yet, so leaving or joining a group needs no warning. */ isDraft: boolean; onMove: (targetParentLinkId: string | null, targetIndex: number) => void; onClose: () => void; } // The keyboard route for what dragging does: pick a destination group and a position within it. const MoveFieldDialog = ({ tree, field, isDraft, onMove, onClose }: Props) => { const placement = useMemo(() => currentPlacement(tree, field.linkId), [tree, field.linkId]); const destinations = useMemo(() => moveDestinations(tree, field.linkId), [tree, field.linkId]); const [destination, setDestination] = useState(placement?.parentLinkId ?? null); const positions = useMemo(() => movePositions(tree, destination, field.linkId), [tree, destination, field.linkId]); const [position, setPosition] = useState(Math.max(0, placement?.index ?? 0)); const currentGroup = destinations.find((entry) => entry.parentLinkId === placement?.parentLinkId); const currentPosition = movePositions(tree, placement?.parentLinkId ?? null, field.linkId) .find((entry) => entry.index === placement?.index); const changesGroup = destination !== (placement?.parentLinkId ?? null); const unchanged = !changesGroup && position === placement?.index; return ( { if (!open) onClose(); }}> Move field {fieldName(field)}

Currently {placement?.parentLinkId ? `inside ${currentGroup?.label}` : 'outside all groups'} {currentPosition ? `, ${currentPosition.label.charAt(0).toLowerCase()}${currentPosition.label.slice(1)}` : ''}.

({ label: entry.label, value: entry.parentLinkId ?? ROOT }))} onChange={(value) => { const next = value === ROOT ? null : value; setDestination(next); setPosition(movePositions(tree, next, field.linkId).length - 1); }}/> ({ label: entry.label, value: String(entry.index) }))} onChange={(value) => setPosition(Number(value))}/>

The field keeps its ID, answer type, coding and conditions. Only its place in the form changes.

{!isDraft && changesGroup && (

If patients have already answered this form, their answers to this question will stop appearing. The data is not deleted; it is recorded at the old position and will no longer show.

)}
); }; export default MoveFieldDialog;