import { useState } from 'react';
import type { Coding } from 'fhir/r4';
import { ArrowLeft, PlusCircle, Trash2 } from 'lucide-react';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../../../../components/ui/dialog';
import { InputControl } from '../../../../components/ui/input-control';
import { SelectControl } from '../../../../components/ui/select-control';
import { CheckboxControl } from '../../../../components/ui/checkbox-control';
import { LoincSearch } from '../../../../components';
import FieldTypeAssist from '../../../../components/FieldTypeAssist/FieldTypeAssist';
import type { ExtendedQuestionnaireItem, IValueSet } from '../../../../services';
import { ANSWER_TYPE_LABELS, buildNewField, isChoiceFieldType, NAMED_FIELD_TYPES, namedFieldType } from './builderFields';
export interface LoincSelection {
type: 'question' | 'group';
item: ExtendedQuestionnaireItem;
}
interface Props {
formTitle: string;
/** A clinical question is added only with a standard code, so its basics offer no plain Add field. */
clinical?: boolean;
/** The group the field joins, when it is added inside one. */
destination?: string;
itemControls: IValueSet[];
onAdd: (field: ExtendedQuestionnaireItem) => void;
/** `intoDestination` false puts the import at the end of the form, outside every group. */
onImport: (selection: LoincSelection, intoDestination: boolean) => void;
onClose: () => void;
}
const typeLabel = (type: string) => ANSWER_TYPE_LABELS[type] ?? type;
const answerLabel = (value: unknown): string => {
if (!value || typeof value !== 'object') return String(value ?? '');
const { display, code, score } = value as { display?: string; code?: string; score?: number | string | null };
const scored = score !== undefined && score !== null && score !== '' ? 'score ' + score : undefined;
return [display ?? code, display && code ? code : undefined, scored].filter(Boolean).join(' · ');
};
// The imported panel as it will land: groups, each question with its type and required state, and coded answers with any score.
const PanelStructure = ({ item }: { item: ExtendedQuestionnaireItem }) => {
const children = item.item ?? [];
const answers = item.type === 'group' ? [] : item.answerOption ?? [];
const line = item.type === 'group' ? item.text : item.text + ' — ' + typeLabel(item.type) + (item.required ? ', required' : '');
return (
{line}
{(children.length > 0 || answers.length > 0) && (
{children.map((child) => )}
{answers.map((answer, index) => - {answerLabel(answer.value)}
)}
)}
);
};
// One modal for a new field: basics first, then the catalogue when the author chooses manually.
// Nothing reaches the form until Add field or a catalogue import; the X leaves the form as it was.
const AddFieldDialog = ({ formTitle, clinical = false, destination, itemControls, onAdd, onImport, onClose }: Props) => {
const [step, setStep] = useState<'basics' | 'manual' | 'review'>('basics');
const [reviewing, setReviewing] = useState(null);
const [insertInto, setInsertInto] = useState<'destination' | 'end'>(destination ? 'destination' : 'end');
const [label, setLabel] = useState('');
const [fieldTypeKey, setFieldTypeKey] = useState('string');
const [required, setRequired] = useState(false);
const [help, setHelp] = useState('');
const [choices, setChoices] = useState(['', '']);
const [code, setCode] = useState(undefined);
const [unit, setUnit] = useState(undefined);
const [assisting, setAssisting] = useState(false);
const [catalogue, setCatalogue] = useState<'question' | 'panel'>('question');
const type = namedFieldType(fieldTypeKey)?.type ?? 'string';
const isQuestion = type !== 'display' && type !== 'group';
const isChoice = isChoiceFieldType(fieldTypeKey);
const needsCode = clinical && isQuestion && !code;
const complete = label.trim() !== '' && !needsCode && (!isChoice || choices.some((choice) => choice.trim() !== ''));
const add = () => {
if (!complete) return;
onAdd(buildNewField({ label, fieldTypeKey, required, help, choices, code, unit }, itemControls));
};
return (
);
};
export default AddFieldDialog;