formanitor 0.0.27 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- type FieldType = 'text' | 'number' | 'textarea' | 'richtext' | 'select' | 'multiselect' | 'radio' | 'repeatable' | 'checkbox' | 'image_upload' | 'signature' | 'date' | 'datetime' | 'editable_table' | 'static_text' | 'medications' | 'investigations' | 'procedures' | 'vitals' | 'hidden_vitals' | 'referral' | 'followup' | string;
3
+ type FieldType = 'text' | 'number' | 'textarea' | 'diagnosis_textarea' | 'richtext' | 'select' | 'multiselect' | 'radio' | 'repeatable' | 'checkbox' | 'image_upload' | 'signature' | 'date' | 'datetime' | 'editable_table' | 'static_text' | 'medications' | 'investigations' | 'procedures' | 'vitals' | 'hidden_vitals' | 'referral' | 'followup' | string;
4
4
  interface ValidationRule {
5
5
  min?: number;
6
6
  max?: number;
@@ -209,6 +209,20 @@ interface SmartTextareaValue {
209
209
  text: string;
210
210
  extractedKeywords: ExtractedKeywordMatch[];
211
211
  }
212
+ /** Selected diagnosis grade chip (single-selection). */
213
+ interface DiagnosisGradeSelection {
214
+ condition: string;
215
+ grade: string;
216
+ }
217
+ /**
218
+ * Structured value for diagnosis_textarea fields.
219
+ * - `text` is the textarea content (what will be submitted).
220
+ * - `selectedGradesByCondition` stores one selected chip per condition for later metadata/preview use.
221
+ */
222
+ interface DiagnosisTextareaValue {
223
+ text: string;
224
+ selectedGradesByCondition: Record<string, string>;
225
+ }
212
226
  /**
213
227
  * Smart textarea that performs client-side NLP keyword extraction and searches
214
228
  * matched terms against backend master tables via `fieldHandlers[fieldId].onSearch`.
@@ -223,6 +237,19 @@ interface SmartTextareaFieldDef extends BaseFieldDef {
223
237
  /** Debounce delay in ms before NLP + search triggers. Default: 1000. */
224
238
  debounceDelay?: number;
225
239
  }
240
+ /**
241
+ * Diagnosis textarea that can display condition-wise grade chips based on
242
+ * asynchronous API response.
243
+ *
244
+ * Value shape in form state can be plain text (legacy) or `DiagnosisTextareaValue`.
245
+ */
246
+ interface DiagnosisTextareaFieldDef extends BaseFieldDef {
247
+ type: 'diagnosis_textarea';
248
+ /** Height in pixels. If omitted, uses default min height (80px). */
249
+ height?: number;
250
+ /** Debounce delay in ms before API call triggers. Default: 700. */
251
+ debounceDelay?: number;
252
+ }
226
253
  interface MedicationFrequentItem {
227
254
  id: string;
228
255
  name: string;
@@ -269,7 +296,7 @@ interface DoctorFrequentItems {
269
296
  investigations?: InvestigationFrequentItem[];
270
297
  procedures?: ProcedureFrequentItem[];
271
298
  }
272
- type FieldDef = TextFieldDef | RichTextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | BaseFieldDef;
299
+ type FieldDef = TextFieldDef | RichTextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | DiagnosisTextareaFieldDef | BaseFieldDef;
273
300
  interface Condition {
274
301
  field?: string;
275
302
  op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'in' | 'contains';
@@ -542,6 +569,11 @@ type FieldHandler = {
542
569
  label: string;
543
570
  value: any;
544
571
  }[]>;
572
+ /**
573
+ * Called by diagnosis_textarea widgets to fetch condition-wise grades
574
+ * from diagnosis text input.
575
+ */
576
+ onFetchDiagnosisGrades?: (query: string) => Promise<any>;
545
577
  };
546
578
  type FieldHandlersMap = Record<string, FieldHandler>;
547
579
  declare function useFieldHandlers(fieldId: string): FieldHandler;
@@ -860,4 +892,4 @@ declare const SmartTextareaWidget: React.FC<{
860
892
  fieldId: string;
861
893
  }>;
862
894
 
863
- export { type AIDifferentialDiagnosisItem, AddInvestigationField, type AddInvestigationFieldProps, AddMedicationField, type AddMedicationFieldProps, type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DifferentialDiagnosis, type DifferentialDiagnosisFieldDef, type DifferentialDiagnosisItem, type DifferentialDiagnosisProps, type DoctorFrequentItems, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type ExtractedKeywordMatch, type FieldDef, type FieldHandler, type FieldHandlersMap, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, type FollowupFieldDef, type FollowupValue, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type FormulaComputation, type FormulaExpr, type ImageUploadFieldDef, ImageUploadWidget, type Investigation, type InvestigationFrequentItem, type InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationFrequentItem, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProcedureFrequentItem, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, type RichTextFieldDef, RichTextWidget, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, SmartForm, type SmartFormProps, type SmartKeywordsResult, type SmartTextareaFieldDef, type SmartTextareaValue, SmartTextareaWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type VitalsFieldDef, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, useSmartKeywords, validateField, validateForm };
895
+ export { type AIDifferentialDiagnosisItem, AddInvestigationField, type AddInvestigationFieldProps, AddMedicationField, type AddMedicationFieldProps, type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, type DiagnosisGradeSelection, type DiagnosisTextareaFieldDef, type DiagnosisTextareaValue, DifferentialDiagnosis, type DifferentialDiagnosisFieldDef, type DifferentialDiagnosisItem, type DifferentialDiagnosisProps, type DoctorFrequentItems, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type ExtractedKeywordMatch, type FieldDef, type FieldHandler, type FieldHandlersMap, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, type FollowupFieldDef, type FollowupValue, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type FormulaComputation, type FormulaExpr, type ImageUploadFieldDef, ImageUploadWidget, type Investigation, type InvestigationFrequentItem, type InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationFrequentItem, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProcedureFrequentItem, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, type RichTextFieldDef, RichTextWidget, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, SmartForm, type SmartFormProps, type SmartKeywordsResult, type SmartTextareaFieldDef, type SmartTextareaValue, SmartTextareaWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type VitalsFieldDef, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, useSmartKeywords, validateField, validateForm };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- type FieldType = 'text' | 'number' | 'textarea' | 'richtext' | 'select' | 'multiselect' | 'radio' | 'repeatable' | 'checkbox' | 'image_upload' | 'signature' | 'date' | 'datetime' | 'editable_table' | 'static_text' | 'medications' | 'investigations' | 'procedures' | 'vitals' | 'hidden_vitals' | 'referral' | 'followup' | string;
3
+ type FieldType = 'text' | 'number' | 'textarea' | 'diagnosis_textarea' | 'richtext' | 'select' | 'multiselect' | 'radio' | 'repeatable' | 'checkbox' | 'image_upload' | 'signature' | 'date' | 'datetime' | 'editable_table' | 'static_text' | 'medications' | 'investigations' | 'procedures' | 'vitals' | 'hidden_vitals' | 'referral' | 'followup' | string;
4
4
  interface ValidationRule {
5
5
  min?: number;
6
6
  max?: number;
@@ -209,6 +209,20 @@ interface SmartTextareaValue {
209
209
  text: string;
210
210
  extractedKeywords: ExtractedKeywordMatch[];
211
211
  }
212
+ /** Selected diagnosis grade chip (single-selection). */
213
+ interface DiagnosisGradeSelection {
214
+ condition: string;
215
+ grade: string;
216
+ }
217
+ /**
218
+ * Structured value for diagnosis_textarea fields.
219
+ * - `text` is the textarea content (what will be submitted).
220
+ * - `selectedGradesByCondition` stores one selected chip per condition for later metadata/preview use.
221
+ */
222
+ interface DiagnosisTextareaValue {
223
+ text: string;
224
+ selectedGradesByCondition: Record<string, string>;
225
+ }
212
226
  /**
213
227
  * Smart textarea that performs client-side NLP keyword extraction and searches
214
228
  * matched terms against backend master tables via `fieldHandlers[fieldId].onSearch`.
@@ -223,6 +237,19 @@ interface SmartTextareaFieldDef extends BaseFieldDef {
223
237
  /** Debounce delay in ms before NLP + search triggers. Default: 1000. */
224
238
  debounceDelay?: number;
225
239
  }
240
+ /**
241
+ * Diagnosis textarea that can display condition-wise grade chips based on
242
+ * asynchronous API response.
243
+ *
244
+ * Value shape in form state can be plain text (legacy) or `DiagnosisTextareaValue`.
245
+ */
246
+ interface DiagnosisTextareaFieldDef extends BaseFieldDef {
247
+ type: 'diagnosis_textarea';
248
+ /** Height in pixels. If omitted, uses default min height (80px). */
249
+ height?: number;
250
+ /** Debounce delay in ms before API call triggers. Default: 700. */
251
+ debounceDelay?: number;
252
+ }
226
253
  interface MedicationFrequentItem {
227
254
  id: string;
228
255
  name: string;
@@ -269,7 +296,7 @@ interface DoctorFrequentItems {
269
296
  investigations?: InvestigationFrequentItem[];
270
297
  procedures?: ProcedureFrequentItem[];
271
298
  }
272
- type FieldDef = TextFieldDef | RichTextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | BaseFieldDef;
299
+ type FieldDef = TextFieldDef | RichTextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | DiagnosisTextareaFieldDef | BaseFieldDef;
273
300
  interface Condition {
274
301
  field?: string;
275
302
  op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'in' | 'contains';
@@ -542,6 +569,11 @@ type FieldHandler = {
542
569
  label: string;
543
570
  value: any;
544
571
  }[]>;
572
+ /**
573
+ * Called by diagnosis_textarea widgets to fetch condition-wise grades
574
+ * from diagnosis text input.
575
+ */
576
+ onFetchDiagnosisGrades?: (query: string) => Promise<any>;
545
577
  };
546
578
  type FieldHandlersMap = Record<string, FieldHandler>;
547
579
  declare function useFieldHandlers(fieldId: string): FieldHandler;
@@ -860,4 +892,4 @@ declare const SmartTextareaWidget: React.FC<{
860
892
  fieldId: string;
861
893
  }>;
862
894
 
863
- export { type AIDifferentialDiagnosisItem, AddInvestigationField, type AddInvestigationFieldProps, AddMedicationField, type AddMedicationFieldProps, type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, DifferentialDiagnosis, type DifferentialDiagnosisFieldDef, type DifferentialDiagnosisItem, type DifferentialDiagnosisProps, type DoctorFrequentItems, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type ExtractedKeywordMatch, type FieldDef, type FieldHandler, type FieldHandlersMap, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, type FollowupFieldDef, type FollowupValue, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type FormulaComputation, type FormulaExpr, type ImageUploadFieldDef, ImageUploadWidget, type Investigation, type InvestigationFrequentItem, type InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationFrequentItem, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProcedureFrequentItem, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, type RichTextFieldDef, RichTextWidget, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, SmartForm, type SmartFormProps, type SmartKeywordsResult, type SmartTextareaFieldDef, type SmartTextareaValue, SmartTextareaWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type VitalsFieldDef, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, useSmartKeywords, validateField, validateForm };
895
+ export { type AIDifferentialDiagnosisItem, AddInvestigationField, type AddInvestigationFieldProps, AddMedicationField, type AddMedicationFieldProps, type BaseFieldDef, type CheckboxFieldDef, type ColumnLayout, type Computation, type Condition, type CreateUploadHandlerOptions, type DataSource, type DiagnosisGradeSelection, type DiagnosisTextareaFieldDef, type DiagnosisTextareaValue, DifferentialDiagnosis, type DifferentialDiagnosisFieldDef, type DifferentialDiagnosisItem, type DifferentialDiagnosisProps, type DoctorFrequentItems, DynamicForm, EMAIL_REGEX, type EditableTableColumnDef, type EditableTableFieldDef, type EventDefinition, type ExtractedKeywordMatch, type FieldDef, type FieldHandler, type FieldHandlersMap, type FieldPrefill, FieldRenderer, type FieldState, type FieldType, type FollowupFieldDef, type FollowupValue, FormControls, type FormControlsProps, type FormDef, type FormErrors, FormProvider, type FormState, FormStore, type FormValues, type FormulaComputation, type FormulaExpr, type ImageUploadFieldDef, ImageUploadWidget, type Investigation, type InvestigationFrequentItem, type InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationFrequentItem, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProcedureFrequentItem, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, type RichTextFieldDef, RichTextWidget, type Rule, type RuleAction, type RuleResult, type ScoreComputation, type ScoreRange, type Section, type SectionChild, type SelectFieldDef, type SelectOption, type SignatureFieldDef, SignatureUploadWidget, SmartForm, type SmartFormProps, type SmartKeywordsResult, type SmartTextareaFieldDef, type SmartTextareaValue, SmartTextareaWidget, type StaticTextFieldDef, type StoreConfig, type SubmissionData, type SumComputation, type TextFieldDef, type ValidationRule, type VitalsFieldDef, type WebhookDef, type Workflow, type WorkflowTransition, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, useSmartKeywords, validateField, validateForm };
package/dist/index.mjs CHANGED
@@ -395,6 +395,11 @@ var FormStore = class {
395
395
  result[field.id] = raw?.text ?? null;
396
396
  return;
397
397
  }
398
+ if (field.type === "diagnosis_textarea") {
399
+ const raw = values[field.id];
400
+ result[field.id] = typeof raw === "string" ? raw : raw && typeof raw === "object" ? raw.text ?? null : null;
401
+ return;
402
+ }
398
403
  if (field.type === "ophthal_diagnosis") {
399
404
  const raw = values[field.id];
400
405
  const formatEye = (eyeData, eyeLabel) => {
@@ -4759,6 +4764,174 @@ var SmartTextareaWidget = ({ fieldId }) => {
4759
4764
  showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
4760
4765
  ] });
4761
4766
  };
4767
+ function normalizeDiagnosisGrades(payload) {
4768
+ const result = [];
4769
+ const pushGroup = (conditionRaw, gradesRaw) => {
4770
+ const condition = typeof conditionRaw === "string" ? conditionRaw.trim() : "";
4771
+ if (!condition) return;
4772
+ if (!Array.isArray(gradesRaw)) return;
4773
+ const grades = Array.from(
4774
+ new Set(
4775
+ gradesRaw.map((grade) => typeof grade === "string" ? grade.trim() : "").filter(Boolean)
4776
+ )
4777
+ );
4778
+ if (grades.length === 0) return;
4779
+ result.push({ condition, grades });
4780
+ };
4781
+ const asArrayResponse = payload;
4782
+ if (Array.isArray(asArrayResponse?.diagnosisGrades)) {
4783
+ asArrayResponse.diagnosisGrades.forEach((item) => {
4784
+ pushGroup(item?.condition, item?.grades);
4785
+ });
4786
+ return result;
4787
+ }
4788
+ const asMapResponse = payload;
4789
+ const mapLike = asMapResponse?.field === "diagnosisgrade" && asMapResponse?.value && typeof asMapResponse.value === "object" ? asMapResponse.value : null;
4790
+ if (mapLike) {
4791
+ Object.entries(mapLike).forEach(([condition, grades]) => {
4792
+ pushGroup(condition, grades);
4793
+ });
4794
+ }
4795
+ return result;
4796
+ }
4797
+ var DiagnosisTextareaWidget = ({ fieldId }) => {
4798
+ const { fieldDef, value, setValue, setTouched, error, disabled, touched } = useField(fieldId);
4799
+ const { state } = useForm();
4800
+ const handler = useFieldHandlers(fieldId);
4801
+ const def = fieldDef;
4802
+ const [gradeGroups, setGradeGroups] = React13__default.useState([]);
4803
+ const [isLoadingGrades, setIsLoadingGrades] = React13__default.useState(false);
4804
+ const normalized = React13__default.useMemo(() => {
4805
+ const raw = value;
4806
+ if (typeof raw === "string") {
4807
+ return { text: raw, selectedGradesByCondition: {} };
4808
+ }
4809
+ if (!raw || typeof raw !== "object") {
4810
+ return { text: "", selectedGradesByCondition: {} };
4811
+ }
4812
+ const text = typeof raw.text === "string" ? raw.text : "";
4813
+ const mapRaw = raw.selectedGradesByCondition;
4814
+ let selectedGradesByCondition = {};
4815
+ if (mapRaw && typeof mapRaw === "object" && !Array.isArray(mapRaw)) {
4816
+ const entries = Object.entries(mapRaw);
4817
+ for (const [condition, gradeRaw] of entries) {
4818
+ if (typeof condition !== "string") continue;
4819
+ if (typeof gradeRaw === "string" && gradeRaw.trim()) {
4820
+ selectedGradesByCondition[condition] = gradeRaw;
4821
+ }
4822
+ }
4823
+ }
4824
+ const sg = raw.selectedGrade;
4825
+ if (sg && typeof sg === "object" && typeof sg.condition === "string" && typeof sg.grade === "string") {
4826
+ const condition = sg.condition.trim();
4827
+ const grade = sg.grade.trim();
4828
+ if (condition && grade && !selectedGradesByCondition[condition]) {
4829
+ selectedGradesByCondition = { ...selectedGradesByCondition, [condition]: grade };
4830
+ }
4831
+ }
4832
+ return { text, selectedGradesByCondition };
4833
+ }, [value]);
4834
+ const textValue = normalized.text;
4835
+ const showError = !!error && (touched || state.submitAttempted);
4836
+ React13__default.useEffect(() => {
4837
+ const query = textValue.trim();
4838
+ const fetchGrades = handler.onFetchDiagnosisGrades;
4839
+ const debounceMs = def?.debounceDelay ?? 1e3;
4840
+ if (!query || !fetchGrades) {
4841
+ setGradeGroups([]);
4842
+ setIsLoadingGrades(false);
4843
+ return;
4844
+ }
4845
+ let isActive = true;
4846
+ const timer = window.setTimeout(async () => {
4847
+ setIsLoadingGrades(true);
4848
+ try {
4849
+ const response = await fetchGrades(query);
4850
+ if (!isActive) return;
4851
+ setGradeGroups(normalizeDiagnosisGrades(response));
4852
+ } catch {
4853
+ if (!isActive) return;
4854
+ setGradeGroups([]);
4855
+ } finally {
4856
+ if (isActive) setIsLoadingGrades(false);
4857
+ }
4858
+ }, debounceMs);
4859
+ return () => {
4860
+ isActive = false;
4861
+ window.clearTimeout(timer);
4862
+ };
4863
+ }, [textValue, handler.onFetchDiagnosisGrades, def?.debounceDelay]);
4864
+ if (!def) return null;
4865
+ const theme = getThemeConfig("textarea", def.theme);
4866
+ const wrapperClass = theme?.wrapperClassName ?? "space-y-2";
4867
+ const labelClass = theme?.labelClassName;
4868
+ const inputClass = cn(theme?.inputClassName, showError && "border-red-500");
4869
+ const toggleSelectedGrade = (condition, grade) => {
4870
+ if (disabled) return;
4871
+ const current = normalized.selectedGradesByCondition[condition];
4872
+ const nextForCondition = current === grade ? "" : grade;
4873
+ const nextMap = { ...normalized.selectedGradesByCondition };
4874
+ if (!nextForCondition) {
4875
+ delete nextMap[condition];
4876
+ } else {
4877
+ nextMap[condition] = nextForCondition;
4878
+ }
4879
+ setValue({ text: normalized.text, selectedGradesByCondition: nextMap });
4880
+ setTouched();
4881
+ };
4882
+ const labelContent = /* @__PURE__ */ jsxs(Fragment, { children: [
4883
+ def.label,
4884
+ " ",
4885
+ def.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
4886
+ ] });
4887
+ return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
4888
+ theme ? /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, className: labelClass, children: labelContent }) : /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: labelContent }),
4889
+ /* @__PURE__ */ jsx(
4890
+ Textarea,
4891
+ {
4892
+ id: fieldId,
4893
+ value: textValue,
4894
+ onChange: (e) => setValue({ text: e.target.value, selectedGradesByCondition: normalized.selectedGradesByCondition }),
4895
+ onBlur: setTouched,
4896
+ disabled,
4897
+ placeholder: def.placeholder,
4898
+ className: inputClass,
4899
+ ...def.height != null ? { height: def.height } : {}
4900
+ }
4901
+ ),
4902
+ isLoadingGrades && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Checking diagnosis grades..." }),
4903
+ !isLoadingGrades && gradeGroups.length > 0 && /* @__PURE__ */ jsx(
4904
+ "div",
4905
+ {
4906
+ className: "mt-2 space-y-3 rounded-lg border border-border bg-muted/30 p-3",
4907
+ role: "group",
4908
+ "aria-label": "Diagnosis grade options",
4909
+ children: gradeGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4910
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium leading-none text-muted-foreground", children: [
4911
+ group.condition,
4912
+ ":"
4913
+ ] }),
4914
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: group.grades.map((grade) => /* @__PURE__ */ jsx(
4915
+ "button",
4916
+ {
4917
+ type: "button",
4918
+ onClick: () => toggleSelectedGrade(group.condition, grade),
4919
+ disabled,
4920
+ className: cn(
4921
+ "inline-flex min-h-8 items-center rounded-full border px-3 py-1 text-xs font-medium leading-none transition-colors",
4922
+ normalized.selectedGradesByCondition[group.condition] === grade ? "border-primary bg-primary/15 text-primary shadow-sm" : "border-border/80 bg-background text-foreground hover:bg-muted/80",
4923
+ disabled && "cursor-not-allowed opacity-60"
4924
+ ),
4925
+ children: grade
4926
+ },
4927
+ `${group.condition}-${grade}`
4928
+ )) })
4929
+ ] }, group.condition))
4930
+ }
4931
+ ),
4932
+ showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
4933
+ ] });
4934
+ };
4762
4935
  var DEFAULT_DRAFT = {
4763
4936
  gpal: { G: 0, P: 0, A: 0, L: 0 },
4764
4937
  lmp: "",
@@ -7112,6 +7285,8 @@ var FieldRenderer = ({ fieldId }) => {
7112
7285
  return /* @__PURE__ */ jsx(FollowupWidget, { fieldId });
7113
7286
  case "smart_textarea":
7114
7287
  return /* @__PURE__ */ jsx(SmartTextareaWidget, { fieldId });
7288
+ case "diagnosis_textarea":
7289
+ return /* @__PURE__ */ jsx(DiagnosisTextareaWidget, { fieldId });
7115
7290
  case "obstetric_history":
7116
7291
  return /* @__PURE__ */ jsx(ObstetricHistoryWidget, { fieldId });
7117
7292
  case "eye_prescription":