formanitor 0.0.15 → 0.0.16

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
@@ -217,6 +217,52 @@ interface SmartTextareaFieldDef extends BaseFieldDef {
217
217
  /** Debounce delay in ms before NLP + search triggers. Default: 1000. */
218
218
  debounceDelay?: number;
219
219
  }
220
+ interface MedicationFrequentItem {
221
+ id: string;
222
+ name: string;
223
+ short_name?: string;
224
+ /** Optional brand name to prefill into the medication row (falls back to name). */
225
+ brand_name?: string;
226
+ frequency?: string;
227
+ route?: string;
228
+ /**
229
+ * Optional human-readable duration like "5 days" or "2 weeks".
230
+ * If provided, Forminator will parse it unless duration_value/unit are set.
231
+ */
232
+ duration?: string;
233
+ /** Optional structured duration components (preferred over duration when present). */
234
+ duration_value?: string;
235
+ duration_unit?: 'days' | 'weeks' | 'months' | string;
236
+ /** Optional schedule slots; when omitted, Forminator falls back to "0". */
237
+ morning?: string;
238
+ afternoon?: string;
239
+ night?: string;
240
+ /** Optional food-timing hint; when omitted, Forminator defaults to "AFTER FOOD". */
241
+ before_after?: 'AFTER FOOD' | 'BEFORE FOOD' | string;
242
+ /** Optional notes; when omitted, Forminator may derive a note from route. */
243
+ notes?: string;
244
+ usage_count?: number;
245
+ last_used_at?: string;
246
+ }
247
+ interface InvestigationFrequentItem {
248
+ id: string;
249
+ name: string;
250
+ code?: string | null;
251
+ usage_count?: number;
252
+ last_used_at?: string;
253
+ }
254
+ interface ProcedureFrequentItem {
255
+ id: string;
256
+ name: string;
257
+ code?: string | null;
258
+ usage_count?: number;
259
+ last_used_at?: string;
260
+ }
261
+ interface DoctorFrequentItems {
262
+ medications?: MedicationFrequentItem[];
263
+ investigations?: InvestigationFrequentItem[];
264
+ procedures?: ProcedureFrequentItem[];
265
+ }
220
266
  type FieldDef = TextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | BaseFieldDef;
221
267
  interface Condition {
222
268
  field?: string;
@@ -493,10 +539,16 @@ type FieldHandler = {
493
539
  };
494
540
  type FieldHandlersMap = Record<string, FieldHandler>;
495
541
  declare function useFieldHandlers(fieldId: string): FieldHandler;
542
+ declare function useFrequentItems(): DoctorFrequentItems;
496
543
  interface FormProviderProps {
497
544
  schema: FormDef;
498
545
  config?: StoreConfig;
499
546
  fieldHandlers?: FieldHandlersMap;
547
+ /**
548
+ * Doctor-scoped frequently used items (e.g. from GET /api/doctor-state/frequent-items/).
549
+ * Consumer fetches and passes this; Forminator flows it to medications, investigations, and procedures widgets.
550
+ */
551
+ frequentItems?: DoctorFrequentItems;
500
552
  children: React.ReactNode;
501
553
  }
502
554
  declare const FormProvider: React.FC<FormProviderProps>;
@@ -658,14 +710,14 @@ type AIMedication = {
658
710
  };
659
711
 
660
712
  type MedicationSearchResult = {
661
- id: number;
713
+ id: string;
662
714
  name: string;
663
715
  generic_name?: string;
664
716
  manufacturer?: string;
665
717
  score?: number;
666
718
  };
667
719
  type Medication = {
668
- id: number;
720
+ id: string;
669
721
  brand_name: string;
670
722
  generic_name: string;
671
723
  frequency: string;
@@ -684,6 +736,8 @@ type AddMedicationFieldProps = {
684
736
  onChange: (items: Medication[]) => void;
685
737
  onSearch: (query: string) => Promise<MedicationSearchResult[]>;
686
738
  recentlyUsed?: MedicationSearchResult[];
739
+ /** Doctor-scoped frequently used medications (from FormProvider frequentItems) */
740
+ frequentItems?: MedicationFrequentItem[];
687
741
  /** AI-suggested medications shown as badges; clicking adds to value */
688
742
  suggestedMedications?: AIMedication[];
689
743
  };
@@ -720,6 +774,8 @@ type AddInvestigationFieldProps = {
720
774
  onChange: (items: Investigation[]) => void;
721
775
  onSearch: (query: string) => Promise<InvestigationSearchResult[]>;
722
776
  recentlyUsed?: InvestigationSearchResult[];
777
+ /** Doctor-scoped frequently used investigations (from FormProvider frequentItems) */
778
+ frequentItems?: InvestigationFrequentItem[];
723
779
  };
724
780
  declare const AddInvestigationField: React.FC<AddInvestigationFieldProps>;
725
781
 
@@ -762,4 +818,4 @@ declare const SmartTextareaWidget: React.FC<{
762
818
  fieldId: string;
763
819
  }>;
764
820
 
765
- 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, 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 InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, 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, useSmartKeywords, validateField, validateForm };
821
+ 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 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
@@ -217,6 +217,52 @@ interface SmartTextareaFieldDef extends BaseFieldDef {
217
217
  /** Debounce delay in ms before NLP + search triggers. Default: 1000. */
218
218
  debounceDelay?: number;
219
219
  }
220
+ interface MedicationFrequentItem {
221
+ id: string;
222
+ name: string;
223
+ short_name?: string;
224
+ /** Optional brand name to prefill into the medication row (falls back to name). */
225
+ brand_name?: string;
226
+ frequency?: string;
227
+ route?: string;
228
+ /**
229
+ * Optional human-readable duration like "5 days" or "2 weeks".
230
+ * If provided, Forminator will parse it unless duration_value/unit are set.
231
+ */
232
+ duration?: string;
233
+ /** Optional structured duration components (preferred over duration when present). */
234
+ duration_value?: string;
235
+ duration_unit?: 'days' | 'weeks' | 'months' | string;
236
+ /** Optional schedule slots; when omitted, Forminator falls back to "0". */
237
+ morning?: string;
238
+ afternoon?: string;
239
+ night?: string;
240
+ /** Optional food-timing hint; when omitted, Forminator defaults to "AFTER FOOD". */
241
+ before_after?: 'AFTER FOOD' | 'BEFORE FOOD' | string;
242
+ /** Optional notes; when omitted, Forminator may derive a note from route. */
243
+ notes?: string;
244
+ usage_count?: number;
245
+ last_used_at?: string;
246
+ }
247
+ interface InvestigationFrequentItem {
248
+ id: string;
249
+ name: string;
250
+ code?: string | null;
251
+ usage_count?: number;
252
+ last_used_at?: string;
253
+ }
254
+ interface ProcedureFrequentItem {
255
+ id: string;
256
+ name: string;
257
+ code?: string | null;
258
+ usage_count?: number;
259
+ last_used_at?: string;
260
+ }
261
+ interface DoctorFrequentItems {
262
+ medications?: MedicationFrequentItem[];
263
+ investigations?: InvestigationFrequentItem[];
264
+ procedures?: ProcedureFrequentItem[];
265
+ }
220
266
  type FieldDef = TextFieldDef | NumberFieldDef | SelectFieldDef | RadioFieldDef | CheckboxFieldDef | RepeatableFieldDef | ImageUploadFieldDef | SignatureFieldDef | EditableTableFieldDef | StaticTextFieldDef | MedicationsFieldDef | InvestigationsFieldDef | ProceduresFieldDef | DifferentialDiagnosisFieldDef | VitalsFieldDef | ReferralFieldDef | FollowupFieldDef | SmartTextareaFieldDef | BaseFieldDef;
221
267
  interface Condition {
222
268
  field?: string;
@@ -493,10 +539,16 @@ type FieldHandler = {
493
539
  };
494
540
  type FieldHandlersMap = Record<string, FieldHandler>;
495
541
  declare function useFieldHandlers(fieldId: string): FieldHandler;
542
+ declare function useFrequentItems(): DoctorFrequentItems;
496
543
  interface FormProviderProps {
497
544
  schema: FormDef;
498
545
  config?: StoreConfig;
499
546
  fieldHandlers?: FieldHandlersMap;
547
+ /**
548
+ * Doctor-scoped frequently used items (e.g. from GET /api/doctor-state/frequent-items/).
549
+ * Consumer fetches and passes this; Forminator flows it to medications, investigations, and procedures widgets.
550
+ */
551
+ frequentItems?: DoctorFrequentItems;
500
552
  children: React.ReactNode;
501
553
  }
502
554
  declare const FormProvider: React.FC<FormProviderProps>;
@@ -658,14 +710,14 @@ type AIMedication = {
658
710
  };
659
711
 
660
712
  type MedicationSearchResult = {
661
- id: number;
713
+ id: string;
662
714
  name: string;
663
715
  generic_name?: string;
664
716
  manufacturer?: string;
665
717
  score?: number;
666
718
  };
667
719
  type Medication = {
668
- id: number;
720
+ id: string;
669
721
  brand_name: string;
670
722
  generic_name: string;
671
723
  frequency: string;
@@ -684,6 +736,8 @@ type AddMedicationFieldProps = {
684
736
  onChange: (items: Medication[]) => void;
685
737
  onSearch: (query: string) => Promise<MedicationSearchResult[]>;
686
738
  recentlyUsed?: MedicationSearchResult[];
739
+ /** Doctor-scoped frequently used medications (from FormProvider frequentItems) */
740
+ frequentItems?: MedicationFrequentItem[];
687
741
  /** AI-suggested medications shown as badges; clicking adds to value */
688
742
  suggestedMedications?: AIMedication[];
689
743
  };
@@ -720,6 +774,8 @@ type AddInvestigationFieldProps = {
720
774
  onChange: (items: Investigation[]) => void;
721
775
  onSearch: (query: string) => Promise<InvestigationSearchResult[]>;
722
776
  recentlyUsed?: InvestigationSearchResult[];
777
+ /** Doctor-scoped frequently used investigations (from FormProvider frequentItems) */
778
+ frequentItems?: InvestigationFrequentItem[];
723
779
  };
724
780
  declare const AddInvestigationField: React.FC<AddInvestigationFieldProps>;
725
781
 
@@ -762,4 +818,4 @@ declare const SmartTextareaWidget: React.FC<{
762
818
  fieldId: string;
763
819
  }>;
764
820
 
765
- 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, 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 InvestigationSearchResult, type InvestigationsFieldDef, type LayoutNode, type MatchedCondition, type Medication, type MedicationSearchResult, type MedicationsFieldDef, type NumberFieldDef, type Permission, type PresignedUploadResponse, type ProceduresFieldDef, type RadioFieldDef, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, type ReferralFieldDef, type ReferralItem, type RepeatableFieldDef, 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, useSmartKeywords, validateField, validateForm };
821
+ 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 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
@@ -634,12 +634,27 @@ var FormStore = class {
634
634
  }
635
635
  };
636
636
  var FormContext = createContext(null);
637
+ var emptyFrequentItems = {
638
+ medications: [],
639
+ investigations: [],
640
+ procedures: []
641
+ };
642
+ var FrequentItemsContext = createContext(emptyFrequentItems);
637
643
  var FieldHandlersContext = createContext({});
638
644
  function useFieldHandlers(fieldId) {
639
645
  const map = useContext(FieldHandlersContext);
640
646
  return map[fieldId] ?? { onSearch: async () => [], recentlyUsed: [] };
641
647
  }
642
- var FormProvider = ({ schema, config, fieldHandlers, children }) => {
648
+ function useFrequentItems() {
649
+ return useContext(FrequentItemsContext);
650
+ }
651
+ var FormProvider = ({
652
+ schema,
653
+ config,
654
+ fieldHandlers,
655
+ frequentItems,
656
+ children
657
+ }) => {
643
658
  const storeRef = useRef(null);
644
659
  if (!storeRef.current) {
645
660
  storeRef.current = new FormStore(schema, config);
@@ -652,7 +667,8 @@ var FormProvider = ({ schema, config, fieldHandlers, children }) => {
652
667
  useEffect(() => {
653
668
  storeRef.current?.load();
654
669
  }, []);
655
- return /* @__PURE__ */ jsx(FormContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx(FieldHandlersContext.Provider, { value: fieldHandlers ?? {}, children }) });
670
+ const frequentItemsValue = frequentItems ?? emptyFrequentItems;
671
+ return /* @__PURE__ */ jsx(FormContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx(FieldHandlersContext.Provider, { value: fieldHandlers ?? {}, children: /* @__PURE__ */ jsx(FrequentItemsContext.Provider, { value: frequentItemsValue, children }) }) });
656
672
  };
657
673
  function useFormStore() {
658
674
  const context = useContext(FormContext);
@@ -2470,6 +2486,7 @@ var AddEntityDialog = ({
2470
2486
  onClose,
2471
2487
  title,
2472
2488
  recentlyUsedSlot,
2489
+ chipSectionLabel = "Recently Used",
2473
2490
  children,
2474
2491
  actionSlot
2475
2492
  }) => {
@@ -2477,7 +2494,7 @@ var AddEntityDialog = ({
2477
2494
  /* @__PURE__ */ jsx(DialogHeader, { className: "px-6 pt-6 pb-4 shrink-0", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-lg font-semibold", children: title }) }),
2478
2495
  /* @__PURE__ */ jsxs("div", { className: "px-6 flex flex-col gap-4 overflow-y-auto flex-1 min-h-0 pb-4", children: [
2479
2496
  recentlyUsedSlot && /* @__PURE__ */ jsxs("div", { children: [
2480
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-2", children: "Recently Used" }),
2497
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-2", children: chipSectionLabel }),
2481
2498
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: recentlyUsedSlot })
2482
2499
  ] }),
2483
2500
  children
@@ -2502,9 +2519,9 @@ function mapToMedication(result) {
2502
2519
  };
2503
2520
  }
2504
2521
  function mapAIMedicationToMedication(ai) {
2505
- const id = typeof ai.id === "string" ? Number.parseInt(ai.id, 10) || 0 : Number(ai.id);
2522
+ const id = ai.id != null ? String(ai.id) : "";
2506
2523
  return {
2507
- id: Number.isNaN(id) ? 0 : id,
2524
+ id,
2508
2525
  brand_name: ai.brand_name ?? "",
2509
2526
  generic_name: ai.generic_name ?? "",
2510
2527
  frequency: "custom",
@@ -2520,7 +2537,7 @@ function mapAIMedicationToMedication(ai) {
2520
2537
  }
2521
2538
  function createCustomMedication(name, customId) {
2522
2539
  return {
2523
- id: customId,
2540
+ id: String(customId),
2524
2541
  brand_name: name.trim(),
2525
2542
  generic_name: "",
2526
2543
  frequency: "custom",
@@ -2534,6 +2551,39 @@ function createCustomMedication(name, customId) {
2534
2551
  notes: ""
2535
2552
  };
2536
2553
  }
2554
+ function parseDuration(duration) {
2555
+ if (!duration || !duration.trim()) return { value: "3", unit: "days" };
2556
+ const lower = duration.trim().toLowerCase();
2557
+ const match = lower.match(/^(\d+)\s*(day|days|week|weeks|month|months)?$/);
2558
+ if (!match) return { value: "3", unit: "days" };
2559
+ const value = match[1] ?? "3";
2560
+ const unitWord = match[2] ?? "days";
2561
+ const unit = unitWord.startsWith("month") ? "months" : unitWord.startsWith("week") ? "weeks" : "days";
2562
+ return { value, unit };
2563
+ }
2564
+ function mapFrequentItemToMedication(item) {
2565
+ const parsed = parseDuration(item.duration);
2566
+ const duration_value = item.duration_value ?? parsed.value;
2567
+ const duration_unit = item.duration_unit ?? parsed.unit;
2568
+ const before_after = item.before_after === "BEFORE FOOD" ? "BEFORE FOOD" : "AFTER FOOD";
2569
+ const routeNote = item.route ? `Route: ${item.route}` : "";
2570
+ const baseNotes = item.notes?.trim() || "";
2571
+ const combinedNotes = [baseNotes, routeNote.trim()].filter((part) => part.length > 0).join(" \u2014 ");
2572
+ return {
2573
+ id: item.id,
2574
+ brand_name: item.brand_name ?? item.name,
2575
+ generic_name: item.short_name ?? "",
2576
+ frequency: item.frequency ?? "custom",
2577
+ is_custom: false,
2578
+ duration_value,
2579
+ duration_unit,
2580
+ before_after,
2581
+ morning: item.morning ?? "0",
2582
+ afternoon: item.afternoon ?? "0",
2583
+ night: item.night ?? "0",
2584
+ notes: combinedNotes
2585
+ };
2586
+ }
2537
2587
  function Spinner({
2538
2588
  value,
2539
2589
  onChange
@@ -2581,11 +2631,14 @@ function MedicationCard({
2581
2631
  onChange: (e) => set("frequency", e.target.value),
2582
2632
  children: [
2583
2633
  /* @__PURE__ */ jsx("option", { value: "custom", children: "select frequency" }),
2584
- /* @__PURE__ */ jsx("option", { value: "OD", children: "OD" }),
2585
- /* @__PURE__ */ jsx("option", { value: "BD", children: "BD" }),
2586
- /* @__PURE__ */ jsx("option", { value: "TDS", children: "TDS" }),
2587
- /* @__PURE__ */ jsx("option", { value: "QID", children: "QID" }),
2588
- /* @__PURE__ */ jsx("option", { value: "SOS", children: "SOS" })
2634
+ /* @__PURE__ */ jsx("option", { value: "qid", children: "4 times a day (QID)" }),
2635
+ /* @__PURE__ */ jsx("option", { value: "alternate_day", children: "Alternate day" }),
2636
+ /* @__PURE__ */ jsx("option", { value: "once_weekly", children: "Once weekly" }),
2637
+ /* @__PURE__ */ jsx("option", { value: "once_monthly", children: "Once monthly" }),
2638
+ /* @__PURE__ */ jsx("option", { value: "as_needed", children: "As needed" }),
2639
+ /* @__PURE__ */ jsx("option", { value: "fort_night", children: "Fort night" }),
2640
+ /* @__PURE__ */ jsx("option", { value: "at_bed_time", children: "At Bed time" }),
2641
+ /* @__PURE__ */ jsx("option", { value: "sos", children: "SOS only" })
2589
2642
  ]
2590
2643
  }
2591
2644
  ),
@@ -2724,6 +2777,7 @@ var AddMedicationField = ({
2724
2777
  onChange,
2725
2778
  onSearch,
2726
2779
  recentlyUsed = [],
2780
+ frequentItems = [],
2727
2781
  suggestedMedications = []
2728
2782
  }) => {
2729
2783
  const [open, setOpen] = useState(false);
@@ -2769,6 +2823,13 @@ var AddMedicationField = ({
2769
2823
  onChange([...safeValue, mapToMedication(result)]);
2770
2824
  }
2771
2825
  }
2826
+ function handleFrequentItemToggle(item) {
2827
+ if (addedIds.has(String(item.id))) {
2828
+ onChange(safeValue.filter((m) => String(m.id) !== String(item.id)));
2829
+ } else {
2830
+ onChange([...safeValue, mapFrequentItemToMedication(item)]);
2831
+ }
2832
+ }
2772
2833
  function handleResultClick(result) {
2773
2834
  if (addedIds.has(String(result.id))) return;
2774
2835
  setAddingResultId(result.id);
@@ -2819,6 +2880,26 @@ var AddMedicationField = ({
2819
2880
  r.id
2820
2881
  );
2821
2882
  });
2883
+ const limitedFrequentItems = frequentItems.slice(0, 25);
2884
+ const frequentItemChips = limitedFrequentItems.length > 0 ? limitedFrequentItems.map((item) => {
2885
+ const isAdded = addedIds.has(String(item.id));
2886
+ return /* @__PURE__ */ jsxs(
2887
+ "button",
2888
+ {
2889
+ type: "button",
2890
+ onClick: () => handleFrequentItemToggle(item),
2891
+ className: cn(
2892
+ "rounded-full border text-[11px] px-2 py-0.5 transition-colors cursor-pointer",
2893
+ isAdded ? "border-green-300 bg-green-50 text-green-700" : "border-border bg-white hover:bg-gray-50"
2894
+ ),
2895
+ children: [
2896
+ isAdded && /* @__PURE__ */ jsx("span", { className: "mr-1", children: "\u2713" }),
2897
+ item.brand_name ?? item.name
2898
+ ]
2899
+ },
2900
+ item.id
2901
+ );
2902
+ }) : [];
2822
2903
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md overflow-hidden flex flex-col border bg-white border-[#D0D0D0] space-y-3 p-3 h-fit", children: [
2823
2904
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2824
2905
  /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label }),
@@ -2832,22 +2913,6 @@ var AddMedicationField = ({
2832
2913
  }
2833
2914
  )
2834
2915
  ] }),
2835
- suggestedMedications.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5 items-center", children: [
2836
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mr-1", children: "Suggestions:" }),
2837
- suggestedMedications.filter((s) => !addedIds.has(String(s.id))).map((s) => /* @__PURE__ */ jsxs(
2838
- "button",
2839
- {
2840
- type: "button",
2841
- onClick: () => handleAddSuggestion(s),
2842
- className: "inline-flex items-center gap-1 rounded-full border border-dashed border-purple-300 bg-purple-50/80 text-purple-800 text-xs px-2.5 py-1 hover:bg-purple-100 transition-colors cursor-pointer",
2843
- children: [
2844
- /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
2845
- s.brand_name || s.generic_name || String(s.id)
2846
- ]
2847
- },
2848
- String(s.id)
2849
- ))
2850
- ] }),
2851
2916
  /* @__PURE__ */ jsx("div", { className: "space-y-2", children: safeValue.map((med, i) => /* @__PURE__ */ jsx(
2852
2917
  MedicationCard,
2853
2918
  {
@@ -2863,7 +2928,8 @@ var AddMedicationField = ({
2863
2928
  open,
2864
2929
  onClose: () => setOpen(false),
2865
2930
  title: "Add Medication",
2866
- recentlyUsedSlot: recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
2931
+ recentlyUsedSlot: frequentItemChips.length > 0 ? frequentItemChips : recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
2932
+ chipSectionLabel: frequentItemChips.length > 0 ? "Frequently used" : void 0,
2867
2933
  actionSlot: /* @__PURE__ */ jsx(
2868
2934
  "button",
2869
2935
  {
@@ -2888,6 +2954,22 @@ var AddMedicationField = ({
2888
2954
  className: "border border-border rounded-md focus-visible:ring-0"
2889
2955
  }
2890
2956
  ),
2957
+ suggestedMedications.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5 items-center", children: [
2958
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mr-1", children: "Suggestions:" }),
2959
+ suggestedMedications.filter((s) => !addedIds.has(String(s.id))).slice(0, 25).map((s) => /* @__PURE__ */ jsxs(
2960
+ "button",
2961
+ {
2962
+ type: "button",
2963
+ onClick: () => handleAddSuggestion(s),
2964
+ className: "inline-flex items-center gap-1 rounded-full border border-dashed border-purple-300 bg-purple-50/80 text-purple-800 text-[11px] px-2 py-0.5 hover:bg-purple-100 transition-colors cursor-pointer",
2965
+ children: [
2966
+ /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
2967
+ s.brand_name || s.generic_name || String(s.id)
2968
+ ]
2969
+ },
2970
+ String(s.id)
2971
+ ))
2972
+ ] }),
2891
2973
  loading && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center py-2", children: "Searching\u2026" }),
2892
2974
  !loading && results.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 max-h-64 overflow-y-auto", children: results.map((r) => /* @__PURE__ */ jsx(
2893
2975
  SearchResultCard,
@@ -2907,6 +2989,7 @@ var AddMedicationWidget = ({ fieldId }) => {
2907
2989
  const { value, setValue } = useField(fieldId);
2908
2990
  const handlers = useFieldHandlers(fieldId);
2909
2991
  const { medications: suggestedMedications } = useAISuggestions();
2992
+ const { medications: frequentMedications } = useFrequentItems();
2910
2993
  const safeValue = Array.isArray(value) ? value : [];
2911
2994
  return /* @__PURE__ */ jsx(
2912
2995
  AddMedicationField,
@@ -2915,6 +2998,7 @@ var AddMedicationWidget = ({ fieldId }) => {
2915
2998
  onChange: setValue,
2916
2999
  onSearch: handlers.onSearch,
2917
3000
  recentlyUsed: handlers.recentlyUsed,
3001
+ frequentItems: frequentMedications,
2918
3002
  suggestedMedications
2919
3003
  }
2920
3004
  );
@@ -2928,12 +3012,23 @@ function mapToInvestigation(result) {
2928
3012
  fromAI: false
2929
3013
  };
2930
3014
  }
3015
+ function mapFrequentItemToInvestigation(item) {
3016
+ return {
3017
+ id: String(item.id),
3018
+ name: item.name,
3019
+ code: item.code ?? void 0,
3020
+ label: item.name,
3021
+ is_custom: false,
3022
+ fromAI: false
3023
+ };
3024
+ }
2931
3025
  var AddInvestigationField = ({
2932
3026
  label = "Investigations",
2933
3027
  value,
2934
3028
  onChange,
2935
3029
  onSearch,
2936
- recentlyUsed = []
3030
+ recentlyUsed = [],
3031
+ frequentItems = []
2937
3032
  }) => {
2938
3033
  const [open, setOpen] = useState(false);
2939
3034
  const [query, setQuery] = useState("");
@@ -2995,6 +3090,14 @@ var AddInvestigationField = ({
2995
3090
  addInvestigation(result);
2996
3091
  }
2997
3092
  }
3093
+ function handleFrequentItemToggle(item) {
3094
+ const id = String(item.id);
3095
+ if (addedIds.has(id)) {
3096
+ removeInvestigation(id);
3097
+ } else {
3098
+ onChange([...valueRef.current, mapFrequentItemToInvestigation(item)]);
3099
+ }
3100
+ }
2998
3101
  const recentlyUsedChips = recentlyUsed.map((r) => {
2999
3102
  const isAdded = addedIds.has(r.id);
3000
3103
  return /* @__PURE__ */ jsxs(
@@ -3014,6 +3117,27 @@ var AddInvestigationField = ({
3014
3117
  r.id
3015
3118
  );
3016
3119
  });
3120
+ const limitedFrequentItems = frequentItems.slice(0, 25);
3121
+ const frequentItemChips = limitedFrequentItems.length > 0 ? limitedFrequentItems.map((item) => {
3122
+ const id = String(item.id);
3123
+ const isAdded = addedIds.has(id);
3124
+ return /* @__PURE__ */ jsxs(
3125
+ "button",
3126
+ {
3127
+ type: "button",
3128
+ onClick: () => handleFrequentItemToggle(item),
3129
+ className: cn(
3130
+ "rounded-full border text-xs px-3 py-1 transition-colors",
3131
+ isAdded ? "border-green-300 bg-green-50 text-green-700" : "border-border bg-white hover:bg-gray-50"
3132
+ ),
3133
+ children: [
3134
+ isAdded && /* @__PURE__ */ jsx("span", { className: "mr-1", children: "\u2713" }),
3135
+ item.name
3136
+ ]
3137
+ },
3138
+ item.id
3139
+ );
3140
+ }) : [];
3017
3141
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md overflow-hidden flex flex-col bg-white border border-[#D0D0D0] space-y-3 p-3 h-fit", children: [
3018
3142
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
3019
3143
  /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label }),
@@ -3052,7 +3176,8 @@ var AddInvestigationField = ({
3052
3176
  open,
3053
3177
  onClose: () => setOpen(false),
3054
3178
  title: "Add Investigation",
3055
- recentlyUsedSlot: recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
3179
+ recentlyUsedSlot: frequentItemChips.length > 0 ? frequentItemChips : recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
3180
+ chipSectionLabel: frequentItemChips.length > 0 ? "Frequently used" : void 0,
3056
3181
  children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3057
3182
  /* @__PURE__ */ jsx(
3058
3183
  Input,
@@ -3092,13 +3217,15 @@ var AddInvestigationField = ({
3092
3217
  var AddInvestigationWidget = ({ fieldId }) => {
3093
3218
  const { value, setValue } = useField(fieldId);
3094
3219
  const handlers = useFieldHandlers(fieldId);
3220
+ const { investigations: frequentInvestigations } = useFrequentItems();
3095
3221
  return /* @__PURE__ */ jsx(
3096
3222
  AddInvestigationField,
3097
3223
  {
3098
3224
  value: value ?? [],
3099
3225
  onChange: setValue,
3100
3226
  onSearch: handlers.onSearch,
3101
- recentlyUsed: handlers.recentlyUsed
3227
+ recentlyUsed: handlers.recentlyUsed,
3228
+ frequentItems: frequentInvestigations
3102
3229
  }
3103
3230
  );
3104
3231
  };
@@ -3111,12 +3238,23 @@ function mapToProcedure(result) {
3111
3238
  fromAI: false
3112
3239
  };
3113
3240
  }
3241
+ function mapFrequentItemToProcedure(item) {
3242
+ return {
3243
+ id: String(item.id),
3244
+ name: item.name,
3245
+ code: item.code ?? void 0,
3246
+ label: item.name,
3247
+ is_custom: false,
3248
+ fromAI: false
3249
+ };
3250
+ }
3114
3251
  var AddProcedureField = ({
3115
3252
  label = "Procedures",
3116
3253
  value,
3117
3254
  onChange,
3118
3255
  onSearch,
3119
- recentlyUsed = []
3256
+ recentlyUsed = [],
3257
+ frequentItems = []
3120
3258
  }) => {
3121
3259
  const [open, setOpen] = useState(false);
3122
3260
  const [query, setQuery] = useState("");
@@ -3178,6 +3316,14 @@ var AddProcedureField = ({
3178
3316
  addProcedure(result);
3179
3317
  }
3180
3318
  }
3319
+ function handleFrequentItemToggle(item) {
3320
+ const id = String(item.id);
3321
+ if (addedIds.has(id)) {
3322
+ removeProcedure(id);
3323
+ } else {
3324
+ onChange([...valueRef.current, mapFrequentItemToProcedure(item)]);
3325
+ }
3326
+ }
3181
3327
  const recentlyUsedChips = recentlyUsed.map((r) => {
3182
3328
  const isAdded = addedIds.has(r.id);
3183
3329
  return /* @__PURE__ */ jsxs(
@@ -3197,6 +3343,27 @@ var AddProcedureField = ({
3197
3343
  r.id
3198
3344
  );
3199
3345
  });
3346
+ const limitedFrequentItems = frequentItems.slice(0, 25);
3347
+ const frequentItemChips = limitedFrequentItems.length > 0 ? limitedFrequentItems.map((item) => {
3348
+ const id = String(item.id);
3349
+ const isAdded = addedIds.has(id);
3350
+ return /* @__PURE__ */ jsxs(
3351
+ "button",
3352
+ {
3353
+ type: "button",
3354
+ onClick: () => handleFrequentItemToggle(item),
3355
+ className: cn(
3356
+ "rounded-full border text-xs px-3 py-1 transition-colors",
3357
+ isAdded ? "border-green-300 bg-green-50 text-green-700" : "border-border bg-white hover:bg-gray-50"
3358
+ ),
3359
+ children: [
3360
+ isAdded && /* @__PURE__ */ jsx("span", { className: "mr-1", children: "\u2713" }),
3361
+ item.name
3362
+ ]
3363
+ },
3364
+ item.id
3365
+ );
3366
+ }) : [];
3200
3367
  return /* @__PURE__ */ jsxs("div", { className: "rounded-md overflow-hidden flex flex-col bg-white border border-[#D0D0D0] space-y-3 p-3 h-fit", children: [
3201
3368
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
3202
3369
  /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: label }),
@@ -3235,7 +3402,8 @@ var AddProcedureField = ({
3235
3402
  open,
3236
3403
  onClose: () => setOpen(false),
3237
3404
  title: "Add Procedure",
3238
- recentlyUsedSlot: recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
3405
+ recentlyUsedSlot: frequentItemChips.length > 0 ? frequentItemChips : recentlyUsedChips.length > 0 ? recentlyUsedChips : void 0,
3406
+ chipSectionLabel: frequentItemChips.length > 0 ? "Frequently used" : void 0,
3239
3407
  children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3240
3408
  /* @__PURE__ */ jsx(
3241
3409
  Input,
@@ -3275,13 +3443,15 @@ var AddProcedureField = ({
3275
3443
  var AddProcedureWidget = ({ fieldId }) => {
3276
3444
  const { value, setValue } = useField(fieldId);
3277
3445
  const handlers = useFieldHandlers(fieldId);
3446
+ const { procedures: frequentProcedures } = useFrequentItems();
3278
3447
  return /* @__PURE__ */ jsx(
3279
3448
  AddProcedureField,
3280
3449
  {
3281
3450
  value: value ?? [],
3282
3451
  onChange: setValue,
3283
3452
  onSearch: handlers.onSearch,
3284
- recentlyUsed: handlers.recentlyUsed
3453
+ recentlyUsed: handlers.recentlyUsed,
3454
+ frequentItems: frequentProcedures
3285
3455
  }
3286
3456
  );
3287
3457
  };
@@ -5594,6 +5764,6 @@ function createUploadHandler(options) {
5594
5764
  };
5595
5765
  }
5596
5766
 
5597
- export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, EMAIL_REGEX, FieldRenderer, FormControls, FormProvider, FormStore, ImageUploadWidget, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, SignatureUploadWidget, SmartForm, SmartTextareaWidget, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useSmartKeywords, validateField, validateForm };
5767
+ export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, EMAIL_REGEX, FieldRenderer, FormControls, FormProvider, FormStore, ImageUploadWidget, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlySignature, ReadOnlyTable, SignatureUploadWidget, SmartForm, SmartTextareaWidget, createUploadHandler, evaluateRules, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, useSmartKeywords, validateField, validateForm };
5598
5768
  //# sourceMappingURL=index.mjs.map
5599
5769
  //# sourceMappingURL=index.mjs.map