@bsol-oss/react-datatable5 13.0.1-beta.2 → 13.0.1-beta.4

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.js CHANGED
@@ -3714,7 +3714,7 @@ const TextWithCopy = ({ text, globalFilter, highlightedText, }) => {
3714
3714
  const displayText = highlightedText !== undefined
3715
3715
  ? highlightedText
3716
3716
  : highlightText$1(textValue, globalFilter);
3717
- return (jsxRuntime.jsxs(react.HStack, { gap: 2, alignItems: "center", children: [jsxRuntime.jsx(react.Text, { as: "span", children: displayText }), jsxRuntime.jsx(react.Clipboard.Root, { value: textValue, children: jsxRuntime.jsx(react.Clipboard.Trigger, { asChild: true, children: jsxRuntime.jsx(react.IconButton, { size: "xs", variant: "ghost", "aria-label": "Copy", fontSize: "1em", children: jsxRuntime.jsx(react.Clipboard.Indicator, { copied: jsxRuntime.jsx(lu.LuCheck, {}), children: jsxRuntime.jsx(lu.LuCopy, {}) }) }) }) })] }));
3717
+ return (jsxRuntime.jsxs(react.HStack, { gap: 2, alignItems: "center", children: [jsxRuntime.jsx(react.Text, { as: "span", children: displayText }), jsxRuntime.jsx(react.Clipboard.Root, { value: textValue, children: jsxRuntime.jsx(react.Clipboard.Trigger, { asChild: true, children: jsxRuntime.jsx(react.IconButton, { size: "2xs", variant: "ghost", "aria-label": "Copy", fontSize: "1em", children: jsxRuntime.jsx(react.Clipboard.Indicator, { copied: jsxRuntime.jsx(lu.LuCheck, {}), children: jsxRuntime.jsx(lu.LuCopy, {}) }) }) }) })] }));
3718
3718
  };
3719
3719
 
3720
3720
  // Helper function to highlight matching text
@@ -4146,6 +4146,22 @@ const convertAjvErrorsToFieldErrors = (errors, schema) => {
4146
4146
  // Get the schema node for this field to check for custom error messages
4147
4147
  const fieldSchema = getSchemaNodeForField(schema, fieldName);
4148
4148
  const customMessage = fieldSchema?.errorMessages?.[error.keyword];
4149
+ // Debug log when error message is missing
4150
+ if (!customMessage) {
4151
+ console.debug(`[Form Validation] Missing error message for field '${fieldName}' with keyword '${error.keyword}'. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`, {
4152
+ fieldName,
4153
+ keyword: error.keyword,
4154
+ instancePath: error.instancePath,
4155
+ schemaPath: error.schemaPath,
4156
+ params: error.params,
4157
+ fieldSchema: fieldSchema
4158
+ ? {
4159
+ type: fieldSchema.type,
4160
+ errorMessages: fieldSchema.errorMessages,
4161
+ }
4162
+ : undefined,
4163
+ });
4164
+ }
4149
4165
  // Provide helpful fallback message if no custom message is provided
4150
4166
  const fallbackMessage = customMessage ||
4151
4167
  `Missing error message for ${error.keyword}. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`;
@@ -4388,7 +4404,7 @@ function removeIndex(str) {
4388
4404
  *
4389
4405
  * @param column - The column name
4390
4406
  * @param prefix - The prefix for the field (usually empty string or parent path)
4391
- * @param schema - Optional schema object with title property
4407
+ * @param schema - Required schema object with title property
4392
4408
  * @returns Object with label helper functions
4393
4409
  *
4394
4410
  * @example
@@ -4421,9 +4437,21 @@ const useFormI18n = (column, prefix = '', schema) => {
4421
4437
  * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
4422
4438
  */
4423
4439
  label: (options) => {
4424
- if (schema?.title) {
4440
+ if (schema.title) {
4425
4441
  return schema.title;
4426
4442
  }
4443
+ // Debug log when field title is missing
4444
+ console.debug(`[Form Field Label] Missing title for field '${colLabel}'. Add title property to schema for field '${colLabel}'.`, {
4445
+ fieldName: column,
4446
+ colLabel,
4447
+ prefix,
4448
+ schema: {
4449
+ type: schema.type,
4450
+ errorMessages: schema.errorMessages
4451
+ ? Object.keys(schema.errorMessages)
4452
+ : undefined,
4453
+ },
4454
+ });
4427
4455
  return translate.t(removeIndex(`${colLabel}.field_label`), options);
4428
4456
  },
4429
4457
  /**
@@ -4700,7 +4728,7 @@ dayjs.extend(timezone);
4700
4728
  const DateRangePicker = ({ column, schema, prefix, }) => {
4701
4729
  const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
4702
4730
  const { timezone, insideDialog } = useSchemaContext();
4703
- const formI18n = useFormI18n(column, prefix);
4731
+ const formI18n = useFormI18n(column, prefix, schema);
4704
4732
  const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
4705
4733
  const isRequired = required?.some((columnId) => columnId === column);
4706
4734
  const colLabel = formI18n.colLabel;
@@ -5345,7 +5373,7 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
5345
5373
  }) })) }))] }));
5346
5374
  };
5347
5375
 
5348
- function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, colLabel, }) {
5376
+ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, }) {
5349
5377
  const [selectedFile, setSelectedFile] = React.useState(undefined);
5350
5378
  const [activeTab, setActiveTab] = React.useState('browse');
5351
5379
  const [uploadingFiles, setUploadingFiles] = React.useState(new Set());
@@ -5429,7 +5457,7 @@ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly =
5429
5457
  const FilePicker = ({ column, schema, prefix }) => {
5430
5458
  const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
5431
5459
  const { filePickerLabels } = useSchemaContext();
5432
- const formI18n = useFormI18n(column, prefix);
5460
+ const formI18n = useFormI18n(column, prefix, schema);
5433
5461
  const { required, gridColumn = 'span 12', gridRow = 'span 1', type, } = schema;
5434
5462
  const isRequired = required?.some((columnId) => columnId === column);
5435
5463
  const isSingleSelect = type === 'string';
@@ -5505,7 +5533,7 @@ const FilePicker = ({ column, schema, prefix }) => {
5505
5533
  const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5506
5534
  const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
5507
5535
  const { filePickerLabels } = useSchemaContext();
5508
- const formI18n = useFormI18n(column, prefix);
5536
+ const formI18n = useFormI18n(column, prefix, schema);
5509
5537
  const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
5510
5538
  const isRequired = required?.some((columnId) => columnId === column);
5511
5539
  const isSingleSelect = type === 'string';
@@ -5600,9 +5628,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5600
5628
  return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5601
5629
  gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: jsxRuntime.jsx(react.Button, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
5602
5630
  formI18n.t('browse_library') ??
5603
- 'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
5604
- filePickerLabels?.dialogTitle ??
5605
- 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5631
+ 'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ?? formI18n.label() ?? 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5606
5632
  const file = fileMap.get(fileId);
5607
5633
  const isImage = file
5608
5634
  ? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
@@ -5626,10 +5652,10 @@ const defaultRenderDisplay = (item) => {
5626
5652
  const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5627
5653
  const { watch, getValues, formState: { errors }, setValue, } = reactHookForm.useFormContext();
5628
5654
  const { idMap, setIdMap, idPickerLabels, insideDialog } = useSchemaContext();
5629
- const { renderDisplay, loadInitialValues: schemaLoadInitialValues, foreign_key, variant, } = schema;
5655
+ const { renderDisplay, loadInitialValues, foreign_key, variant } = schema;
5630
5656
  // loadInitialValues must be provided in schema for id-picker fields
5631
5657
  // It's used to load the record of the id so the display is human-readable
5632
- if (variant === 'id-picker' && !schemaLoadInitialValues) {
5658
+ if (variant === 'id-picker' && !loadInitialValues) {
5633
5659
  throw new Error(`loadInitialValues is required in schema for IdPicker field '${column}'. Please provide loadInitialValues function in the schema to load records for human-readable display.`);
5634
5660
  }
5635
5661
  const { table, column: column_ref, customQueryFn, } = foreign_key;
@@ -5676,19 +5702,27 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5676
5702
  const missingIdsKey = React.useMemo(() => {
5677
5703
  return JSON.stringify([...missingIds].sort());
5678
5704
  }, [missingIds]);
5705
+ // Include idMap state in query key to force refetch when idMap is reset (e.g., on remount from another page)
5706
+ // This ensures the query runs even if React Query has cached data for the same missing IDs
5707
+ const idMapStateKey = React.useMemo(() => {
5708
+ // Create a key based on whether the required IDs are in idMap
5709
+ const hasRequiredIds = currentValue.every((id) => idMap[id]);
5710
+ return hasRequiredIds ? 'complete' : 'incomplete';
5711
+ }, [currentValue, idMap]);
5679
5712
  // Query to fetch initial values that are missing from idMap
5680
5713
  // This query runs automatically when missingIds.length > 0 and updates idMap
5681
5714
  const initialValuesQuery = reactQuery.useQuery({
5682
- queryKey: [`idpicker-initial`, column, missingIdsKey],
5715
+ queryKey: [`idpicker-initial`, column, missingIdsKey, idMapStateKey],
5683
5716
  queryFn: async () => {
5684
5717
  if (missingIds.length === 0) {
5685
5718
  return { data: [], count: 0 };
5686
5719
  }
5687
5720
  // Use schema's loadInitialValues (required for id-picker)
5688
- if (!schemaLoadInitialValues) {
5689
- throw new Error(`loadInitialValues is required in schema for IdPicker field '${column}'.`);
5721
+ if (!loadInitialValues) {
5722
+ console.warn(`loadInitialValues is required in schema for IdPicker field '${column}'. Returning empty idMap.`);
5723
+ return { data: [], count: 0 };
5690
5724
  }
5691
- const result = await schemaLoadInitialValues({
5725
+ const result = await loadInitialValues({
5692
5726
  ids: missingIds,
5693
5727
  foreign_key: foreign_key,
5694
5728
  setIdMap,
@@ -5696,7 +5730,9 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5696
5730
  return result.data;
5697
5731
  },
5698
5732
  enabled: missingIds.length > 0, // Only fetch if there are missing IDs
5699
- staleTime: 300000,
5733
+ staleTime: 0, // Always consider data stale to refetch on remount
5734
+ refetchOnMount: true, // Always refetch when component remounts (e.g., from another page)
5735
+ refetchOnWindowFocus: false, // Don't refetch on window focus
5700
5736
  });
5701
5737
  const { isLoading: isLoadingInitialValues, isFetching: isFetchingInitialValues, } = initialValuesQuery;
5702
5738
  // Query for search results (async loading)
@@ -5841,7 +5877,7 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5841
5877
  idPickerLabels,
5842
5878
  insideDialog: insideDialog ?? false,
5843
5879
  renderDisplay,
5844
- loadInitialValues: schemaLoadInitialValues, // Required for id-picker, checked above
5880
+ loadInitialValues: loadInitialValues, // Required for id-picker, checked above
5845
5881
  column_ref,
5846
5882
  errors,
5847
5883
  setValue,
@@ -7767,15 +7803,15 @@ const DateViewer = ({ column, schema, prefix }) => {
7767
7803
 
7768
7804
  const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
7769
7805
  const { watch, formState: { errors }, } = reactHookForm.useFormContext();
7770
- const formI18n = useFormI18n(column, prefix);
7806
+ const formI18n = useFormI18n(column, prefix, schema);
7771
7807
  const { required } = schema;
7772
7808
  const isRequired = required?.some((columnId) => columnId === column);
7773
- const { gridColumn = "span 12", gridRow = "span 1", renderDisplay } = schema;
7809
+ const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
7774
7810
  const colLabel = formI18n.colLabel;
7775
7811
  const watchEnum = watch(colLabel);
7776
7812
  const watchEnums = (watch(colLabel) ?? []);
7777
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: "stretch", gridColumn,
7778
- gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: "wrap", gap: 1, children: watchEnums.map((enumValue) => {
7813
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7814
+ gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
7779
7815
  const item = enumValue;
7780
7816
  if (item === undefined) {
7781
7817
  return jsxRuntime.jsx(jsxRuntime.Fragment, { children: "undefined" });
@@ -7783,7 +7819,7 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
7783
7819
  return (jsxRuntime.jsx(Tag, { size: "lg", children: !!renderDisplay === true
7784
7820
  ? renderDisplay(item)
7785
7821
  : formI18n.t(item) }, item));
7786
- }) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: formI18n.required() }))] }));
7822
+ }) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
7787
7823
  };
7788
7824
 
7789
7825
  const FileViewer = ({ column, schema, prefix }) => {
@@ -8196,6 +8232,17 @@ const FormBody = () => {
8196
8232
 
8197
8233
  const FormTitle = () => {
8198
8234
  const { schema } = useSchemaContext();
8235
+ // Debug log when form title is missing
8236
+ if (!schema.title) {
8237
+ console.debug('[Form Title] Missing title in root schema. Add title property to schema.', {
8238
+ schema: {
8239
+ type: schema.type,
8240
+ properties: schema.properties
8241
+ ? Object.keys(schema.properties)
8242
+ : undefined,
8243
+ },
8244
+ });
8245
+ }
8199
8246
  return jsxRuntime.jsx(react.Heading, { children: schema.title ?? 'Form' });
8200
8247
  };
8201
8248
 
package/dist/index.mjs CHANGED
@@ -3694,7 +3694,7 @@ const TextWithCopy = ({ text, globalFilter, highlightedText, }) => {
3694
3694
  const displayText = highlightedText !== undefined
3695
3695
  ? highlightedText
3696
3696
  : highlightText$1(textValue, globalFilter);
3697
- return (jsxs(HStack, { gap: 2, alignItems: "center", children: [jsx(Text, { as: "span", children: displayText }), jsx(Clipboard.Root, { value: textValue, children: jsx(Clipboard.Trigger, { asChild: true, children: jsx(IconButton, { size: "xs", variant: "ghost", "aria-label": "Copy", fontSize: "1em", children: jsx(Clipboard.Indicator, { copied: jsx(LuCheck, {}), children: jsx(LuCopy, {}) }) }) }) })] }));
3697
+ return (jsxs(HStack, { gap: 2, alignItems: "center", children: [jsx(Text, { as: "span", children: displayText }), jsx(Clipboard.Root, { value: textValue, children: jsx(Clipboard.Trigger, { asChild: true, children: jsx(IconButton, { size: "2xs", variant: "ghost", "aria-label": "Copy", fontSize: "1em", children: jsx(Clipboard.Indicator, { copied: jsx(LuCheck, {}), children: jsx(LuCopy, {}) }) }) }) })] }));
3698
3698
  };
3699
3699
 
3700
3700
  // Helper function to highlight matching text
@@ -4126,6 +4126,22 @@ const convertAjvErrorsToFieldErrors = (errors, schema) => {
4126
4126
  // Get the schema node for this field to check for custom error messages
4127
4127
  const fieldSchema = getSchemaNodeForField(schema, fieldName);
4128
4128
  const customMessage = fieldSchema?.errorMessages?.[error.keyword];
4129
+ // Debug log when error message is missing
4130
+ if (!customMessage) {
4131
+ console.debug(`[Form Validation] Missing error message for field '${fieldName}' with keyword '${error.keyword}'. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`, {
4132
+ fieldName,
4133
+ keyword: error.keyword,
4134
+ instancePath: error.instancePath,
4135
+ schemaPath: error.schemaPath,
4136
+ params: error.params,
4137
+ fieldSchema: fieldSchema
4138
+ ? {
4139
+ type: fieldSchema.type,
4140
+ errorMessages: fieldSchema.errorMessages,
4141
+ }
4142
+ : undefined,
4143
+ });
4144
+ }
4129
4145
  // Provide helpful fallback message if no custom message is provided
4130
4146
  const fallbackMessage = customMessage ||
4131
4147
  `Missing error message for ${error.keyword}. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`;
@@ -4368,7 +4384,7 @@ function removeIndex(str) {
4368
4384
  *
4369
4385
  * @param column - The column name
4370
4386
  * @param prefix - The prefix for the field (usually empty string or parent path)
4371
- * @param schema - Optional schema object with title property
4387
+ * @param schema - Required schema object with title property
4372
4388
  * @returns Object with label helper functions
4373
4389
  *
4374
4390
  * @example
@@ -4401,9 +4417,21 @@ const useFormI18n = (column, prefix = '', schema) => {
4401
4417
  * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
4402
4418
  */
4403
4419
  label: (options) => {
4404
- if (schema?.title) {
4420
+ if (schema.title) {
4405
4421
  return schema.title;
4406
4422
  }
4423
+ // Debug log when field title is missing
4424
+ console.debug(`[Form Field Label] Missing title for field '${colLabel}'. Add title property to schema for field '${colLabel}'.`, {
4425
+ fieldName: column,
4426
+ colLabel,
4427
+ prefix,
4428
+ schema: {
4429
+ type: schema.type,
4430
+ errorMessages: schema.errorMessages
4431
+ ? Object.keys(schema.errorMessages)
4432
+ : undefined,
4433
+ },
4434
+ });
4407
4435
  return translate.t(removeIndex(`${colLabel}.field_label`), options);
4408
4436
  },
4409
4437
  /**
@@ -4680,7 +4708,7 @@ dayjs.extend(timezone);
4680
4708
  const DateRangePicker = ({ column, schema, prefix, }) => {
4681
4709
  const { watch, formState: { errors }, setValue, } = useFormContext();
4682
4710
  const { timezone, insideDialog } = useSchemaContext();
4683
- const formI18n = useFormI18n(column, prefix);
4711
+ const formI18n = useFormI18n(column, prefix, schema);
4684
4712
  const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
4685
4713
  const isRequired = required?.some((columnId) => columnId === column);
4686
4714
  const colLabel = formI18n.colLabel;
@@ -5325,7 +5353,7 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
5325
5353
  }) })) }))] }));
5326
5354
  };
5327
5355
 
5328
- function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, colLabel, }) {
5356
+ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, }) {
5329
5357
  const [selectedFile, setSelectedFile] = useState(undefined);
5330
5358
  const [activeTab, setActiveTab] = useState('browse');
5331
5359
  const [uploadingFiles, setUploadingFiles] = useState(new Set());
@@ -5409,7 +5437,7 @@ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly =
5409
5437
  const FilePicker = ({ column, schema, prefix }) => {
5410
5438
  const { setValue, formState: { errors }, watch, } = useFormContext();
5411
5439
  const { filePickerLabels } = useSchemaContext();
5412
- const formI18n = useFormI18n(column, prefix);
5440
+ const formI18n = useFormI18n(column, prefix, schema);
5413
5441
  const { required, gridColumn = 'span 12', gridRow = 'span 1', type, } = schema;
5414
5442
  const isRequired = required?.some((columnId) => columnId === column);
5415
5443
  const isSingleSelect = type === 'string';
@@ -5485,7 +5513,7 @@ const FilePicker = ({ column, schema, prefix }) => {
5485
5513
  const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5486
5514
  const { setValue, formState: { errors }, watch, } = useFormContext();
5487
5515
  const { filePickerLabels } = useSchemaContext();
5488
- const formI18n = useFormI18n(column, prefix);
5516
+ const formI18n = useFormI18n(column, prefix, schema);
5489
5517
  const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
5490
5518
  const isRequired = required?.some((columnId) => columnId === column);
5491
5519
  const isSingleSelect = type === 'string';
@@ -5580,9 +5608,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5580
5608
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5581
5609
  gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
5582
5610
  formI18n.t('browse_library') ??
5583
- 'Browse from Library' }) }), jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
5584
- filePickerLabels?.dialogTitle ??
5585
- 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5611
+ 'Browse from Library' }) }), jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ?? formI18n.label() ?? 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5586
5612
  const file = fileMap.get(fileId);
5587
5613
  const isImage = file
5588
5614
  ? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
@@ -5606,10 +5632,10 @@ const defaultRenderDisplay = (item) => {
5606
5632
  const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5607
5633
  const { watch, getValues, formState: { errors }, setValue, } = useFormContext();
5608
5634
  const { idMap, setIdMap, idPickerLabels, insideDialog } = useSchemaContext();
5609
- const { renderDisplay, loadInitialValues: schemaLoadInitialValues, foreign_key, variant, } = schema;
5635
+ const { renderDisplay, loadInitialValues, foreign_key, variant } = schema;
5610
5636
  // loadInitialValues must be provided in schema for id-picker fields
5611
5637
  // It's used to load the record of the id so the display is human-readable
5612
- if (variant === 'id-picker' && !schemaLoadInitialValues) {
5638
+ if (variant === 'id-picker' && !loadInitialValues) {
5613
5639
  throw new Error(`loadInitialValues is required in schema for IdPicker field '${column}'. Please provide loadInitialValues function in the schema to load records for human-readable display.`);
5614
5640
  }
5615
5641
  const { table, column: column_ref, customQueryFn, } = foreign_key;
@@ -5656,19 +5682,27 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5656
5682
  const missingIdsKey = useMemo(() => {
5657
5683
  return JSON.stringify([...missingIds].sort());
5658
5684
  }, [missingIds]);
5685
+ // Include idMap state in query key to force refetch when idMap is reset (e.g., on remount from another page)
5686
+ // This ensures the query runs even if React Query has cached data for the same missing IDs
5687
+ const idMapStateKey = useMemo(() => {
5688
+ // Create a key based on whether the required IDs are in idMap
5689
+ const hasRequiredIds = currentValue.every((id) => idMap[id]);
5690
+ return hasRequiredIds ? 'complete' : 'incomplete';
5691
+ }, [currentValue, idMap]);
5659
5692
  // Query to fetch initial values that are missing from idMap
5660
5693
  // This query runs automatically when missingIds.length > 0 and updates idMap
5661
5694
  const initialValuesQuery = useQuery({
5662
- queryKey: [`idpicker-initial`, column, missingIdsKey],
5695
+ queryKey: [`idpicker-initial`, column, missingIdsKey, idMapStateKey],
5663
5696
  queryFn: async () => {
5664
5697
  if (missingIds.length === 0) {
5665
5698
  return { data: [], count: 0 };
5666
5699
  }
5667
5700
  // Use schema's loadInitialValues (required for id-picker)
5668
- if (!schemaLoadInitialValues) {
5669
- throw new Error(`loadInitialValues is required in schema for IdPicker field '${column}'.`);
5701
+ if (!loadInitialValues) {
5702
+ console.warn(`loadInitialValues is required in schema for IdPicker field '${column}'. Returning empty idMap.`);
5703
+ return { data: [], count: 0 };
5670
5704
  }
5671
- const result = await schemaLoadInitialValues({
5705
+ const result = await loadInitialValues({
5672
5706
  ids: missingIds,
5673
5707
  foreign_key: foreign_key,
5674
5708
  setIdMap,
@@ -5676,7 +5710,9 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5676
5710
  return result.data;
5677
5711
  },
5678
5712
  enabled: missingIds.length > 0, // Only fetch if there are missing IDs
5679
- staleTime: 300000,
5713
+ staleTime: 0, // Always consider data stale to refetch on remount
5714
+ refetchOnMount: true, // Always refetch when component remounts (e.g., from another page)
5715
+ refetchOnWindowFocus: false, // Don't refetch on window focus
5680
5716
  });
5681
5717
  const { isLoading: isLoadingInitialValues, isFetching: isFetchingInitialValues, } = initialValuesQuery;
5682
5718
  // Query for search results (async loading)
@@ -5821,7 +5857,7 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
5821
5857
  idPickerLabels,
5822
5858
  insideDialog: insideDialog ?? false,
5823
5859
  renderDisplay,
5824
- loadInitialValues: schemaLoadInitialValues, // Required for id-picker, checked above
5860
+ loadInitialValues: loadInitialValues, // Required for id-picker, checked above
5825
5861
  column_ref,
5826
5862
  errors,
5827
5863
  setValue,
@@ -7747,15 +7783,15 @@ const DateViewer = ({ column, schema, prefix }) => {
7747
7783
 
7748
7784
  const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
7749
7785
  const { watch, formState: { errors }, } = useFormContext();
7750
- const formI18n = useFormI18n(column, prefix);
7786
+ const formI18n = useFormI18n(column, prefix, schema);
7751
7787
  const { required } = schema;
7752
7788
  const isRequired = required?.some((columnId) => columnId === column);
7753
- const { gridColumn = "span 12", gridRow = "span 1", renderDisplay } = schema;
7789
+ const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
7754
7790
  const colLabel = formI18n.colLabel;
7755
7791
  const watchEnum = watch(colLabel);
7756
7792
  const watchEnums = (watch(colLabel) ?? []);
7757
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: "stretch", gridColumn,
7758
- gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: "wrap", gap: 1, children: watchEnums.map((enumValue) => {
7793
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7794
+ gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
7759
7795
  const item = enumValue;
7760
7796
  if (item === undefined) {
7761
7797
  return jsx(Fragment, { children: "undefined" });
@@ -7763,7 +7799,7 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
7763
7799
  return (jsx(Tag, { size: "lg", children: !!renderDisplay === true
7764
7800
  ? renderDisplay(item)
7765
7801
  : formI18n.t(item) }, item));
7766
- }) })), !isMultiple && jsx(Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: formI18n.required() }))] }));
7802
+ }) })), !isMultiple && jsx(Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
7767
7803
  };
7768
7804
 
7769
7805
  const FileViewer = ({ column, schema, prefix }) => {
@@ -8176,6 +8212,17 @@ const FormBody = () => {
8176
8212
 
8177
8213
  const FormTitle = () => {
8178
8214
  const { schema } = useSchemaContext();
8215
+ // Debug log when form title is missing
8216
+ if (!schema.title) {
8217
+ console.debug('[Form Title] Missing title in root schema. Add title property to schema.', {
8218
+ schema: {
8219
+ type: schema.type,
8220
+ properties: schema.properties
8221
+ ? Object.keys(schema.properties)
8222
+ : undefined,
8223
+ },
8224
+ });
8225
+ }
8179
8226
  return jsx(Heading, { children: schema.title ?? 'Form' });
8180
8227
  };
8181
8228
 
@@ -12,6 +12,6 @@ interface MediaBrowserDialogProps {
12
12
  labels?: FilePickerLabels;
13
13
  colLabel: string;
14
14
  }
15
- export declare function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly, onFetchFiles, onUploadFile, enableUpload, labels, colLabel, }: MediaBrowserDialogProps): import("react/jsx-runtime").JSX.Element | null;
15
+ export declare function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly, onFetchFiles, onUploadFile, enableUpload, labels, }: MediaBrowserDialogProps): import("react/jsx-runtime").JSX.Element | null;
16
16
  export declare const FilePicker: ({ column, schema, prefix }: InputDefaultProps) => import("react/jsx-runtime").JSX.Element;
17
17
  export {};
@@ -1,4 +1,4 @@
1
- import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
1
+ import { CustomJSONSchema7 } from '../types/CustomJSONSchema7';
2
2
  export interface EnumViewerProps {
3
3
  column: string;
4
4
  isMultiple?: boolean;
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @param column - The column name
7
7
  * @param prefix - The prefix for the field (usually empty string or parent path)
8
- * @param schema - Optional schema object with title property
8
+ * @param schema - Required schema object with title property
9
9
  * @returns Object with label helper functions
10
10
  *
11
11
  * @example
@@ -25,7 +25,7 @@
25
25
  * const colLabel = formI18n.colLabel;
26
26
  * ```
27
27
  */
28
- export declare const useFormI18n: (column: string, prefix?: string, schema?: {
28
+ export declare const useFormI18n: (column: string, prefix: string | undefined, schema: {
29
29
  title?: string;
30
30
  }) => {
31
31
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "13.0.1-beta.2",
3
+ "version": "13.0.1-beta.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",