@openg2p/registry-widgets 1.1.6-dev.2 → 1.1.6-dev.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
@@ -1688,12 +1688,13 @@ function useWidgetTheme() {
1688
1688
  const WidgetContext = React.createContext({
1689
1689
  dataSourceRequestHandler: undefined,
1690
1690
  schemaData: undefined,
1691
+ hostContext: undefined,
1691
1692
  t: undefined,
1692
1693
  });
1693
1694
  const useWidgetContext = () => {
1694
1695
  return React.useContext(WidgetContext);
1695
1696
  };
1696
- const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, t, theme, children, }) => {
1697
+ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, hostContext, t, theme, children, }) => {
1697
1698
  const widgetStore = React.useMemo(() => store || createWidgetStore(), [store]);
1698
1699
  const eventBus = React.useMemo(() => new WidgetEventBus(), []);
1699
1700
  const resolvedTheme = React.useMemo(() => resolveTheme(theme), [theme]);
@@ -1701,8 +1702,9 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, t, theme,
1701
1702
  const contextValue = React.useMemo(() => ({
1702
1703
  dataSourceRequestHandler,
1703
1704
  schemaData,
1705
+ hostContext,
1704
1706
  t,
1705
- }), [dataSourceRequestHandler, schemaData, t]);
1707
+ }), [dataSourceRequestHandler, schemaData, hostContext, t]);
1706
1708
  React.useEffect(() => {
1707
1709
  if (!dataSourceRequestHandler) {
1708
1710
  console.warn('[WidgetProvider] dataSourceRequestHandler is not provided. ' +
@@ -1850,7 +1852,7 @@ const useBaseWidget = (options) => {
1850
1852
  // This prevents data disappearance when switching to Edit mode and components
1851
1853
  // incorrectly clear values before options load or if handler is temporarily missing.
1852
1854
  if (newValue === '' || newValue === null || newValue === undefined) {
1853
- const allowEmptyClear = config.widget === 'register-lookup';
1855
+ const allowEmptyClear = config.widget === 'register-lookup' || config.widget === 'parent-lookup';
1854
1856
  if (!allowEmptyClear) {
1855
1857
  if (loadingRef.current) {
1856
1858
  console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
@@ -3051,11 +3053,15 @@ const useCrViewData = (mode, currentSchemaData, storeValues) => React.useMemo(()
3051
3053
  return null;
3052
3054
  const dataSource = { ...storeValues, ...currentSchemaData };
3053
3055
  const recordPath = Object.keys(dataSource)[0];
3056
+ const records = dataSource[recordPath]?.records;
3057
+ const auditPath = Array.isArray(records) && records.length > 0
3058
+ ? `${recordPath}.records.${records.length - 1}`
3059
+ : recordPath;
3054
3060
  return {
3055
- createdBy: getValueByPath(dataSource, `${recordPath}.created_by`),
3056
- createdDate: getValueByPath(dataSource, `${recordPath}.created_at`),
3057
- approvedBy: getValueByPath(dataSource, `${recordPath}.last_approved_by`),
3058
- approvedDate: getValueByPath(dataSource, `${recordPath}.last_approved_at`),
3061
+ createdBy: getValueByPath(dataSource, `${auditPath}.created_by`),
3062
+ createdDate: getValueByPath(dataSource, `${auditPath}.created_at`),
3063
+ approvedBy: getValueByPath(dataSource, `${auditPath}.last_approved_by`),
3064
+ approvedDate: getValueByPath(dataSource, `${auditPath}.last_approved_at`),
3059
3065
  };
3060
3066
  }, [mode, currentSchemaData, storeValues]);
3061
3067
 
@@ -4273,6 +4279,7 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
4273
4279
  }
4274
4280
  return results;
4275
4281
  },
4282
+ hasUnsavedChanges: () => Object.values(sectionDirtyMapRef.current).some(Boolean),
4276
4283
  };
4277
4284
  }, [store, dispatch, safeSections]);
4278
4285
  React.useEffect(() => {
@@ -4384,6 +4391,8 @@ const WIDGET_TYPES = [
4384
4391
  'display',
4385
4392
  'profile',
4386
4393
  'geo-hierarchy',
4394
+ 'register-lookup',
4395
+ 'parent-lookup',
4387
4396
  ];
4388
4397
  const ORIENTATIONS = ['horizontal', 'vertical'];
4389
4398
  const CONDITION_OPERATORS = [
@@ -7007,7 +7016,18 @@ const SelectWidget = ({ config }) => {
7007
7016
  : (value != null && value !== '' ? tSchema(t, String(value)) : '-');
7008
7017
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] SelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
7009
7018
  }
7010
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: tSchema(t, widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
7019
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: tSchema(t, widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", {
7020
+ // value={value || ''}
7021
+ // onChange={(e) => onChange(e.target.value === '' ? undefined : e.target.value)}
7022
+ value: value === undefined || value === null ? '' : String(value), onChange: (e) => {
7023
+ const rawValue = e.target.value;
7024
+ if (rawValue === '') {
7025
+ onChange(undefined);
7026
+ return;
7027
+ }
7028
+ const selectedOption = dataSourceOptions.find((option) => String(option.value) === rawValue);
7029
+ onChange(selectedOption ? selectedOption.value : rawValue);
7030
+ }, onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
7011
7031
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7012
7032
  : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: tSchema(t, widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: t?.('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: tSchema(t, option.label) }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: t?.('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7013
7033
  };
@@ -7394,18 +7414,46 @@ const TableWidget = ({ config }) => {
7394
7414
  const [originalRows, setOriginalRows] = React.useState(null);
7395
7415
  const isSectionEditMode = !isReadonly && operations.edit;
7396
7416
  const isAnyRowEditing = editingState !== null || isAdding;
7417
+ const resolveEditingRowData = React.useCallback(() => {
7418
+ if (!editingState)
7419
+ return null;
7420
+ const row = { ...editingState.currentValue };
7421
+ columns.forEach((col) => {
7422
+ const key = col['column-key'];
7423
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${editingState.rowIndex}-col-${key}`;
7424
+ if (storeValues[cellWidgetId] !== undefined) {
7425
+ row[key] = storeValues[cellWidgetId];
7426
+ }
7427
+ });
7428
+ return row;
7429
+ }, [editingState, columns, widgetConfig, storeValues]);
7430
+ const resolveNewRowData = React.useCallback(() => {
7431
+ if (!isAdding || !newRowData)
7432
+ return null;
7433
+ const row = { ...newRowData };
7434
+ columns.forEach((col) => {
7435
+ const key = col['column-key'];
7436
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rows.length}-col-${key}`;
7437
+ if (storeValues[cellWidgetId] !== undefined) {
7438
+ row[key] = storeValues[cellWidgetId];
7439
+ }
7440
+ });
7441
+ return row;
7442
+ }, [isAdding, newRowData, columns, widgetConfig, rows.length, storeValues]);
7397
7443
  const canSaveEditingRow = React.useMemo(() => {
7398
- if (!editingState) {
7444
+ const rowData = resolveEditingRowData();
7445
+ if (!rowData) {
7399
7446
  return false;
7400
7447
  }
7401
- return isTableRowDataValid(editingState.currentValue, columns, isReadonly, resolveSchemaLabel);
7402
- }, [editingState, columns, isReadonly, resolveSchemaLabel]);
7448
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7449
+ }, [resolveEditingRowData, columns, isReadonly, resolveSchemaLabel]);
7403
7450
  const canSaveNewRow = React.useMemo(() => {
7404
- if (!isAdding || !newRowData) {
7451
+ const rowData = resolveNewRowData();
7452
+ if (!rowData) {
7405
7453
  return false;
7406
7454
  }
7407
- return isTableRowDataValid(newRowData, columns, isReadonly, resolveSchemaLabel);
7408
- }, [isAdding, newRowData, columns, isReadonly, resolveSchemaLabel]);
7455
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7456
+ }, [resolveNewRowData, columns, isReadonly, resolveSchemaLabel]);
7409
7457
  const showConfirmation = React.useCallback((message, onConfirm, onCancel) => {
7410
7458
  setConfirmationState({
7411
7459
  show: true,
@@ -7464,29 +7512,41 @@ const TableWidget = ({ config }) => {
7464
7512
  }, [isAnyRowEditing, rows, showConfirmation, cancelEdit, t]);
7465
7513
  const updateCellValue = React.useCallback((columnKey, newValue, rowIndex) => {
7466
7514
  if (editingState && rowIndex !== undefined) {
7467
- setEditingState({
7468
- ...editingState,
7469
- currentValue: {
7470
- ...editingState.currentValue,
7471
- [columnKey]: newValue,
7472
- },
7515
+ setEditingState((prev) => {
7516
+ if (!prev || prev.rowIndex !== rowIndex)
7517
+ return prev;
7518
+ return {
7519
+ ...prev,
7520
+ currentValue: {
7521
+ ...prev.currentValue,
7522
+ [columnKey]: newValue,
7523
+ },
7524
+ };
7473
7525
  });
7474
7526
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7475
7527
  dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7476
7528
  }
7477
- else if (isAdding && newRowData) {
7478
- setNewRowData({
7479
- ...newRowData,
7480
- [columnKey]: newValue,
7529
+ else if (isAdding) {
7530
+ setNewRowData((prev) => {
7531
+ if (!prev)
7532
+ return prev;
7533
+ return {
7534
+ ...prev,
7535
+ [columnKey]: newValue,
7536
+ };
7481
7537
  });
7538
+ if (rowIndex !== undefined) {
7539
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7540
+ dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7541
+ }
7482
7542
  }
7483
- }, [editingState, isAdding, newRowData, widgetConfig, dispatch]);
7543
+ }, [editingState, isAdding, widgetConfig, dispatch]);
7484
7544
  const saveEdit = React.useCallback(async () => {
7485
- if (!editingState)
7545
+ const rowData = resolveEditingRowData();
7546
+ if (!editingState || !rowData)
7486
7547
  return;
7487
7548
  if (!canSaveEditingRow)
7488
7549
  return;
7489
- const rowData = editingState.currentValue;
7490
7550
  const rowIndex = editingState.rowIndex;
7491
7551
  setLoadingRowIndex(rowIndex);
7492
7552
  try {
@@ -7545,7 +7605,7 @@ const TableWidget = ({ config }) => {
7545
7605
  finally {
7546
7606
  setLoadingRowIndex(null);
7547
7607
  }
7548
- }, [editingState, canSaveEditingRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
7608
+ }, [editingState, canSaveEditingRow, resolveEditingRowData, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
7549
7609
  const startAdd = React.useCallback(() => {
7550
7610
  if (isAnyRowEditing) {
7551
7611
  cancelEdit();
@@ -7558,23 +7618,26 @@ const TableWidget = ({ config }) => {
7558
7618
  setNewRowData(emptyRow);
7559
7619
  }, [isAnyRowEditing, columns, cancelEdit]);
7560
7620
  const saveAdd = React.useCallback(async () => {
7561
- if (!isAdding || !newRowData)
7621
+ const rowData = resolveNewRowData();
7622
+ if (!isAdding || !rowData)
7562
7623
  return;
7563
7624
  if (!canSaveNewRow)
7564
7625
  return;
7565
- setLoadingRowIndex(-1); // Use -1 to indicate new row
7626
+ setLoadingRowIndex(-1);
7566
7627
  try {
7567
- let savedRow = { ...newRowData };
7568
- // TODO: Update to use dataSourceRequestHandler pattern
7628
+ let savedRow = { ...rowData };
7569
7629
  if (dataSourceRequestHandler && apiConfig.add) {
7570
7630
  console.warn('[TableWidget] API add operations require migration to dataSourceRequestHandler pattern');
7571
- const response = newRowData;
7572
- if (response && typeof response === 'object') {
7573
- savedRow = { ...savedRow, ...response };
7631
+ if (rowData && typeof rowData === 'object') {
7632
+ savedRow = { ...savedRow, ...rowData };
7574
7633
  }
7575
7634
  }
7576
7635
  savedRow = { ...savedRow, edit_action: 'ADD' };
7577
7636
  onChange([...rows, savedRow]);
7637
+ columns.forEach((col) => {
7638
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rows.length}-col-${col['column-key']}`;
7639
+ dispatch(resetWidget(cellWidgetId));
7640
+ });
7578
7641
  setIsAdding(false);
7579
7642
  setNewRowData(null);
7580
7643
  }
@@ -7585,7 +7648,7 @@ const TableWidget = ({ config }) => {
7585
7648
  finally {
7586
7649
  setLoadingRowIndex(null);
7587
7650
  }
7588
- }, [isAdding, newRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode]);
7651
+ }, [isAdding, resolveNewRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, columns, widgetConfig, dispatch]);
7589
7652
  const deleteRow = React.useCallback(async (rowIndex) => {
7590
7653
  if (isAnyRowEditing) {
7591
7654
  showConfirmation(t?.('table.unsavedChanges') || 'You have unsaved changes. Do you want to discard them?', () => {
@@ -7636,6 +7699,11 @@ const TableWidget = ({ config }) => {
7636
7699
  return editingState.currentValue[columnKey];
7637
7700
  }
7638
7701
  if (isAdding && rowIndex === rows.length) {
7702
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7703
+ const storeValue = storeValues[cellWidgetId];
7704
+ if (storeValue !== undefined) {
7705
+ return storeValue;
7706
+ }
7639
7707
  return newRowData?.[columnKey];
7640
7708
  }
7641
7709
  return rows[rowIndex]?.[columnKey];
@@ -7650,6 +7718,9 @@ const TableWidget = ({ config }) => {
7650
7718
  if (widgetType === 'select') {
7651
7719
  return null; // Will be handled by SelectDisplayValue component
7652
7720
  }
7721
+ if (widgetType === 'parent-lookup') {
7722
+ return null;
7723
+ }
7653
7724
  if (column['widget-data-format']) {
7654
7725
  return formatValue(cellValue, column['widget-data-format'], column.widget);
7655
7726
  }
@@ -7666,7 +7737,14 @@ const TableWidget = ({ config }) => {
7666
7737
  setOriginalRows(null);
7667
7738
  }
7668
7739
  }, [isSectionEditMode, rows, originalRows]);
7740
+ const editSessionKey = editingState
7741
+ ? `edit-${editingState.rowIndex}`
7742
+ : isAdding
7743
+ ? `add-${rows.length}`
7744
+ : null;
7669
7745
  React.useEffect(() => {
7746
+ if (!editSessionKey)
7747
+ return;
7670
7748
  if (editingState) {
7671
7749
  columns.forEach((col) => {
7672
7750
  const columnKey = col['column-key'];
@@ -7675,9 +7753,8 @@ const TableWidget = ({ config }) => {
7675
7753
  const defaultValue = cellValue !== undefined ? cellValue : (col['widget-data-default'] ?? '');
7676
7754
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7677
7755
  });
7756
+ return;
7678
7757
  }
7679
- }, [editingState, columns, widgetConfig, dispatch]);
7680
- React.useEffect(() => {
7681
7758
  if (isAdding && newRowData) {
7682
7759
  columns.forEach((col) => {
7683
7760
  const columnKey = col['column-key'];
@@ -7687,7 +7764,9 @@ const TableWidget = ({ config }) => {
7687
7764
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7688
7765
  });
7689
7766
  }
7690
- }, [isAdding, newRowData, columns, widgetConfig, rows.length, dispatch]);
7767
+ // Seed only when add/edit session starts — not on every cell change.
7768
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7769
+ }, [editSessionKey]);
7691
7770
  const getRowValuesForEdit = React.useCallback((rowIndex) => {
7692
7771
  if (editingState && editingState.rowIndex === rowIndex) {
7693
7772
  return editingState.currentValue ?? {};
@@ -7767,6 +7846,20 @@ const TableWidget = ({ config }) => {
7767
7846
  };
7768
7847
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue$1, { config: cellConfig, value: cellValue }) }));
7769
7848
  }
7849
+ if (widgetType === 'parent-lookup' && displayValue === null) {
7850
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7851
+ const cellConfig = {
7852
+ ...column,
7853
+ 'widget-id': cellWidgetId,
7854
+ 'widget-label': '',
7855
+ 'widget-readonly': true,
7856
+ 'widget-data-path': undefined,
7857
+ 'widget-data-default': cellValue !== undefined ? cellValue : '',
7858
+ };
7859
+ return (jsxRuntimeExports.jsx("div", { className: "text-sm table-cell-widget", style: getCellStyle(), children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
7860
+ [cellWidgetId]: cellValue !== undefined ? cellValue : '',
7861
+ } }) }));
7862
+ }
7770
7863
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: displayValue }));
7771
7864
  }
7772
7865
  }, [isRowEditing, getCellValue, getDisplayValue, renderTableCell]);
@@ -7862,7 +7955,7 @@ const TableWidget = ({ config }) => {
7862
7955
  border: '1px solid var(--owt-btn-primary-border, #F07B1A)',
7863
7956
  backgroundColor: 'var(--owt-color-primary, #F5BB1A)',
7864
7957
  color: 'var(--owt-color-bg, #FFFFFF)',
7865
- }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: { borderRadius: 'var(--owt-widget-table-border-radius, 15px)', borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && !isAdding && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: columns.length + (((operations.edit || operations.remove) && !isReadonly) || isSectionEditMode ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
7958
+ }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: { borderRadius: 'var(--owt-widget-table-border-radius, 15px)', borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['column-label'] || col['widget-label'] || col['column-key']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && !isAdding && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: columns.length + (((operations.edit || operations.remove) && !isReadonly) || isSectionEditMode ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
7866
7959
  const isEditing = isRowEditing(rowIndex);
7867
7960
  const isLoading = loadingRowIndex === rowIndex;
7868
7961
  return (jsxRuntimeExports.jsxs("tr", { className: `${isLoading ? 'opacity-50' : ''}${isEditing ? ' table-row-editing' : ''}`, style: {
@@ -7984,7 +8077,7 @@ const DialogTableField = React.memo(function DialogTableField({ col, cellWidgetI
7984
8077
  widget: widgetType,
7985
8078
  'widget-type': col['widget-type'] || 'input',
7986
8079
  'widget-id': cellWidgetId,
7987
- 'widget-label': col['widget-label'],
8080
+ 'widget-label': col['widget-label'] || col['column-label'] || col['column-key'] || '',
7988
8081
  'widget-readonly': isReadonly || col['widget-readonly'] === true,
7989
8082
  'widget-data-path': undefined,
7990
8083
  'widget-data-default': col['widget-data-default'],
@@ -8229,6 +8322,8 @@ const DialogTableWidget = ({ config }) => {
8229
8322
  return '-';
8230
8323
  if (widgetType === 'select')
8231
8324
  return null;
8325
+ if (widgetType === 'parent-lookup')
8326
+ return null;
8232
8327
  if (column['widget-data-format'])
8233
8328
  return formatValue(cellValue, column['widget-data-format'], column.widget);
8234
8329
  return String(cellValue);
@@ -8282,7 +8377,7 @@ const DialogTableWidget = ({ config }) => {
8282
8377
  }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
8283
8378
  borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
8284
8379
  borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
8285
- }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
8380
+ }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['column-label'] || col['widget-label'] || col['column-key']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
8286
8381
  const cellStyle = getRowCellStyle(row?.edit_action);
8287
8382
  return (jsxRuntimeExports.jsxs("tr", { style: {
8288
8383
  borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
@@ -8303,6 +8398,18 @@ const DialogTableWidget = ({ config }) => {
8303
8398
  };
8304
8399
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: displayConfig, value: row?.[key] }) }) }, key));
8305
8400
  }
8401
+ if (widgetType === 'parent-lookup' && displayValue === null) {
8402
+ const cellWidgetId = `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`;
8403
+ const displayConfig = {
8404
+ ...col,
8405
+ 'widget-id': cellWidgetId,
8406
+ 'widget-label': '',
8407
+ 'widget-readonly': true,
8408
+ 'widget-data-path': undefined,
8409
+ 'widget-data-default': row?.[key] ?? '',
8410
+ };
8411
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm table-cell-widget", style: cellStyle, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: displayConfig, schemaData: { [cellWidgetId]: row?.[key] ?? '' } }) }) }, key));
8412
+ }
8306
8413
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: displayValue }) }, key));
8307
8414
  }), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [operations.edit && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => openEditDialog(rowIndex), disabled: !isEnabled, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
8308
8415
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
@@ -9877,7 +9984,7 @@ const normalizeDisplayFields = (row) => {
9877
9984
  value: f.value !== null && f.value !== undefined ? String(f.value) : '-',
9878
9985
  }));
9879
9986
  };
9880
- const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
9987
+ const parsePagination$1 = (pagination, rowCount, size, fallbackPage = 1) => {
9881
9988
  const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
9882
9989
  const totalPages = typeof pagination.number_of_pages === 'number'
9883
9990
  ? Math.max(1, pagination.number_of_pages)
@@ -10028,7 +10135,7 @@ const RegisterLookupWidget = ({ config }) => {
10028
10135
  const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
10029
10136
  if (match)
10030
10137
  return match;
10031
- totalPages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
10138
+ totalPages = parsePagination$1(pagination, rows.length, hydratePageSize).totalPages;
10032
10139
  if (page >= totalPages)
10033
10140
  break;
10034
10141
  page += 1;
@@ -10038,7 +10145,7 @@ const RegisterLookupWidget = ({ config }) => {
10038
10145
  const runSearch = React.useCallback(async (text, page = 1) => {
10039
10146
  try {
10040
10147
  const { rows, pagination } = await fetchRecords(text, page, pageSize);
10041
- const parsed = parsePagination(pagination, rows.length, pageSize, page);
10148
+ const parsed = parsePagination$1(pagination, rows.length, pageSize, page);
10042
10149
  setSearchResults(rows);
10043
10150
  setTotalCount(parsed.totalItems);
10044
10151
  setTotalPages(parsed.totalPages);
@@ -10171,6 +10278,361 @@ const RegisterLookupWidget = ({ config }) => {
10171
10278
  : t?.('common.searchHint', { defaultValue: 'Type and press Enter or click search' }) })) : (jsxRuntimeExports.jsx(ResultsTable, { rows: searchResults, selectedRowKey: pendingRow?.internal_record_id ?? null, onRowClick: setPendingRow, onRowDoubleClick: applySelection })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsx(PaginationFooter, { embedded: true, currentPage: currentPage, totalPages: totalPages, totalCount: totalCount, pageSize: pageSize, onPageChange: (page) => runSearch(searchText, page), onPrev: () => currentPage > 1 && runSearch(searchText, currentPage - 1), onNext: () => currentPage < totalPages && runSearch(searchText, currentPage + 1) })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] }))] }));
10172
10279
  };
10173
10280
 
10281
+ const parentLookupPageCache = new Map();
10282
+ const parentLookupPageInflight = new Map();
10283
+ const parentLookupRecordCache = new Map();
10284
+ const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
10285
+ const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
10286
+ const totalPages = typeof pagination.number_of_pages === 'number'
10287
+ ? Math.max(1, pagination.number_of_pages)
10288
+ : totalItems > 0
10289
+ ? Math.max(1, Math.ceil(totalItems / size))
10290
+ : 1;
10291
+ return { totalItems, totalPages, currentPage: pagination.current_page ?? fallbackPage };
10292
+ };
10293
+ const parentLabel = (row) => {
10294
+ if (!row)
10295
+ return '';
10296
+ if (row.record_name != null && String(row.record_name).trim() !== '') {
10297
+ return String(row.record_name);
10298
+ }
10299
+ return String(row.internal_record_id ?? '');
10300
+ };
10301
+ const indexParentRecords = (rows) => {
10302
+ for (const row of rows) {
10303
+ const id = String(row.internal_record_id ?? '').trim();
10304
+ if (id) {
10305
+ parentLookupRecordCache.set(id, row);
10306
+ }
10307
+ }
10308
+ };
10309
+ const buildParentLookupCacheKey = (service, endpoint, method, params) => `${service}|${endpoint}|${method}|${JSON.stringify(params)}`;
10310
+ const fetchParentLookupPage = async (handler, service, endpoint, method, params, headers) => {
10311
+ const resolvedMethod = method || 'POST';
10312
+ const cacheKey = buildParentLookupCacheKey(service, endpoint, resolvedMethod, params);
10313
+ const cached = parentLookupPageCache.get(cacheKey);
10314
+ if (cached) {
10315
+ return cached;
10316
+ }
10317
+ const inflight = parentLookupPageInflight.get(cacheKey);
10318
+ if (inflight) {
10319
+ return inflight;
10320
+ }
10321
+ const fetchPromise = (async () => {
10322
+ const result = await handler(service, endpoint, resolvedMethod, params, { headers });
10323
+ const parsed = {
10324
+ rows: (result?.records ?? []),
10325
+ pagination: (result?.pagination ?? {}),
10326
+ };
10327
+ indexParentRecords(parsed.rows);
10328
+ parentLookupPageCache.set(cacheKey, parsed);
10329
+ return parsed;
10330
+ })();
10331
+ parentLookupPageInflight.set(cacheKey, fetchPromise);
10332
+ try {
10333
+ return await fetchPromise;
10334
+ }
10335
+ finally {
10336
+ parentLookupPageInflight.delete(cacheKey);
10337
+ }
10338
+ };
10339
+ const ParentLookupWidget = ({ config, value: valueProp, error: errorProp, touched: touchedProp, isEnabled: isEnabledProp, isRequired: isRequiredProp, onChange: onChangeProp, onBlur: onBlurProp, }) => {
10340
+ const hook = useBaseWidget({ config });
10341
+ // WidgetRenderer spreads its hook result as props (includes table onValueChange).
10342
+ // Prefer those so parent selection updates the table row / form state.
10343
+ const useInjected = typeof onChangeProp === 'function';
10344
+ const value = useInjected ? valueProp : hook.value;
10345
+ const error = useInjected ? (errorProp ?? hook.error) : hook.error;
10346
+ const touched = useInjected ? !!touchedProp : hook.touched;
10347
+ const isEnabled = useInjected ? (isEnabledProp ?? hook.isEnabled) : hook.isEnabled;
10348
+ const isRequired = useInjected ? !!isRequiredProp : hook.isRequired;
10349
+ const onChange = useInjected ? onChangeProp : hook.onChange;
10350
+ const onBlur = useInjected ? (onBlurProp ?? hook.onBlur) : hook.onBlur;
10351
+ const widgetConfig = config;
10352
+ const { t, dataSourceRequestHandler, hostContext } = useWidgetContext();
10353
+ const dataSource = widgetConfig['widget-data-source'];
10354
+ const lookupConfig = widgetConfig['widget-lookup-config'];
10355
+ const pageSize = lookupConfig?.page_size ?? 10;
10356
+ const isCompact = !widgetConfig['widget-label'];
10357
+ const requestParams = React.useMemo(() => {
10358
+ const merged = { ...(hostContext || {}) };
10359
+ const schemaParams = dataSource?.params || {};
10360
+ for (const [key, paramValue] of Object.entries(schemaParams)) {
10361
+ if (paramValue !== null && paramValue !== undefined && paramValue !== '') {
10362
+ merged[key] = paramValue;
10363
+ }
10364
+ }
10365
+ return merged;
10366
+ }, [hostContext, dataSource?.params]);
10367
+ const [isOpen, setIsOpen] = React.useState(false);
10368
+ const [searchText, setSearchText] = React.useState('');
10369
+ const [searchResults, setSearchResults] = React.useState([]);
10370
+ const [currentPage, setCurrentPage] = React.useState(1);
10371
+ const [totalPages, setTotalPages] = React.useState(1);
10372
+ const [totalCount, setTotalCount] = React.useState(null);
10373
+ const [pendingRow, setPendingRow] = React.useState(null);
10374
+ const [appliedRecord, setAppliedRecord] = React.useState(null);
10375
+ const [isHydrating, setIsHydrating] = React.useState(false);
10376
+ const [modalPos, setModalPos] = React.useState({ x: 80, y: 80 });
10377
+ const [modalSize, setModalSize] = React.useState({ w: 860, h: 520 });
10378
+ const isDragging = React.useRef(false);
10379
+ const dragOrigin = React.useRef({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
10380
+ const searchInputRef = React.useRef(null);
10381
+ const hydratedValueRef = React.useRef(null);
10382
+ const fetchRecords = React.useCallback(async (text, page, size) => {
10383
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler) {
10384
+ return { rows: [], pagination: {} };
10385
+ }
10386
+ return fetchParentLookupPage(dataSourceRequestHandler, dataSource.service, dataSource.endpoint, dataSource.method, {
10387
+ ...requestParams,
10388
+ search_text: text,
10389
+ current_page: page,
10390
+ page_size: size,
10391
+ }, dataSource.headers);
10392
+ }, [dataSource, dataSourceRequestHandler, requestParams]);
10393
+ const findRecordByValue = React.useCallback(async (recordValue) => {
10394
+ const target = String(recordValue).trim();
10395
+ if (!target)
10396
+ return null;
10397
+ const cachedRecord = parentLookupRecordCache.get(target);
10398
+ if (cachedRecord) {
10399
+ return cachedRecord;
10400
+ }
10401
+ const hydratePageSize = lookupConfig?.hydrate_page_size ?? 50;
10402
+ let page = 1;
10403
+ let pages = 1;
10404
+ while (page <= pages) {
10405
+ const { rows, pagination } = await fetchRecords('', page, hydratePageSize);
10406
+ const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
10407
+ if (match)
10408
+ return match;
10409
+ pages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
10410
+ if (page >= pages)
10411
+ break;
10412
+ page += 1;
10413
+ }
10414
+ return null;
10415
+ }, [fetchRecords, lookupConfig?.hydrate_page_size]);
10416
+ const runSearch = React.useCallback(async (text, page = 1) => {
10417
+ try {
10418
+ const { rows, pagination } = await fetchRecords(text, page, pageSize);
10419
+ const parsed = parsePagination(pagination, rows.length, pageSize, page);
10420
+ setSearchResults(rows);
10421
+ setTotalCount(parsed.totalItems);
10422
+ setTotalPages(parsed.totalPages);
10423
+ setCurrentPage(parsed.currentPage);
10424
+ }
10425
+ catch {
10426
+ setSearchResults([]);
10427
+ setTotalCount(null);
10428
+ setTotalPages(1);
10429
+ setCurrentPage(1);
10430
+ }
10431
+ }, [fetchRecords, pageSize]);
10432
+ React.useEffect(() => {
10433
+ const onMove = (e) => {
10434
+ if (!isDragging.current)
10435
+ return;
10436
+ setModalPos({
10437
+ x: dragOrigin.current.posX + (e.clientX - dragOrigin.current.mouseX),
10438
+ y: dragOrigin.current.posY + (e.clientY - dragOrigin.current.mouseY),
10439
+ });
10440
+ };
10441
+ const onUp = () => {
10442
+ isDragging.current = false;
10443
+ };
10444
+ document.addEventListener('mousemove', onMove);
10445
+ document.addEventListener('mouseup', onUp);
10446
+ return () => {
10447
+ document.removeEventListener('mousemove', onMove);
10448
+ document.removeEventListener('mouseup', onUp);
10449
+ };
10450
+ }, []);
10451
+ const hasValue = value !== null && value !== undefined && value !== '';
10452
+ const applySelection = (row) => {
10453
+ indexParentRecords([row]);
10454
+ hydratedValueRef.current = row.internal_record_id;
10455
+ onChange(row.internal_record_id);
10456
+ setAppliedRecord(row);
10457
+ setIsOpen(false);
10458
+ };
10459
+ const clearSelection = () => {
10460
+ hydratedValueRef.current = null;
10461
+ onChange(null);
10462
+ setAppliedRecord(null);
10463
+ setPendingRow(null);
10464
+ };
10465
+ const openLookup = () => {
10466
+ const w = Math.min(Math.round(window.innerWidth * 0.82), 940);
10467
+ const h = Math.min(Math.round(window.innerHeight * 0.72), 560);
10468
+ setModalPos({
10469
+ x: Math.round((window.innerWidth - w) / 2),
10470
+ y: Math.round((window.innerHeight - h) / 2),
10471
+ });
10472
+ setModalSize({ w, h });
10473
+ setIsOpen(true);
10474
+ setSearchText('');
10475
+ setSearchResults([]);
10476
+ setTotalCount(null);
10477
+ setCurrentPage(1);
10478
+ setTotalPages(1);
10479
+ setPendingRow(appliedRecord);
10480
+ setTimeout(() => searchInputRef.current?.focus(), 50);
10481
+ runSearch('', 1);
10482
+ };
10483
+ React.useEffect(() => {
10484
+ if (!hasValue) {
10485
+ hydratedValueRef.current = null;
10486
+ setAppliedRecord(null);
10487
+ setIsHydrating(false);
10488
+ return;
10489
+ }
10490
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler)
10491
+ return;
10492
+ if (hydratedValueRef.current === value)
10493
+ return;
10494
+ let cancelled = false;
10495
+ setIsHydrating(true);
10496
+ setAppliedRecord(null);
10497
+ (async () => {
10498
+ try {
10499
+ const match = await findRecordByValue(value);
10500
+ if (cancelled)
10501
+ return;
10502
+ hydratedValueRef.current = value;
10503
+ setAppliedRecord(match ?? { internal_record_id: String(value) });
10504
+ }
10505
+ catch {
10506
+ if (!cancelled) {
10507
+ hydratedValueRef.current = value;
10508
+ setAppliedRecord({ internal_record_id: String(value) });
10509
+ }
10510
+ }
10511
+ finally {
10512
+ if (!cancelled)
10513
+ setIsHydrating(false);
10514
+ }
10515
+ })();
10516
+ return () => {
10517
+ cancelled = true;
10518
+ setIsHydrating(false);
10519
+ };
10520
+ }, [hasValue, value, dataSource, dataSourceRequestHandler, findRecordByValue]);
10521
+ const isReadonly = !!widgetConfig['widget-readonly'];
10522
+ const rawLabel = widgetConfig['widget-label'];
10523
+ const label = tSchema(t, rawLabel || 'Parent');
10524
+ const hasError = (touched && error.length > 0) || (!!widgetConfig['widget-required'] && !hasValue);
10525
+ const placeholder = tSchema(t, String(lookupConfig?.action_label ?? t?.('common.select') ?? 'Select'));
10526
+ const searchPlaceholder = tSchema(t, String(lookupConfig?.search_placeholder ?? 'Search parent...'));
10527
+ const selectRecordLabel = tSchema(t, String(lookupConfig?.select_record_label ?? 'Select Parent'));
10528
+ const displayName = isHydrating
10529
+ ? t?.('common.loading', { defaultValue: 'Loading...' })
10530
+ : parentLabel(appliedRecord) || (hasValue ? String(value) : '');
10531
+ const selectTrigger = isCompact ? (jsxRuntimeExports.jsxs("select", { value: hasValue ? '__selected__' : '', disabled: !isEnabled || isReadonly, onMouseDown: (e) => {
10532
+ if (!isEnabled || isReadonly)
10533
+ return;
10534
+ e.preventDefault();
10535
+ openLookup();
10536
+ }, onKeyDown: (e) => {
10537
+ if (!isEnabled || isReadonly)
10538
+ return;
10539
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
10540
+ e.preventDefault();
10541
+ openLookup();
10542
+ }
10543
+ }, onChange: () => undefined, onBlur: onBlur, className: `w-full h-[28px] px-2 text-sm border focus:outline-none table-cell-input ${!isEnabled || isReadonly ? 'cursor-not-allowed' : 'cursor-pointer'}`, style: {
10544
+ borderRadius: '10px',
10545
+ borderColor: hasError
10546
+ ? 'var(--owt-color-error, #B91C1C)'
10547
+ : 'var(--owt-widget-input-border, #C4C4C4)',
10548
+ backgroundColor: !isEnabled || isReadonly
10549
+ ? 'var(--owt-color-bg-alt, #F6F6F6)'
10550
+ : 'var(--owt-color-bg, #FFFFFF)',
10551
+ }, title: hasValue ? displayName : placeholder, children: [jsxRuntimeExports.jsx("option", { value: "", children: placeholder }), hasValue && jsxRuntimeExports.jsx("option", { value: "__selected__", children: displayName })] })) : (jsxRuntimeExports.jsxs("select", { value: hasValue ? '__selected__' : '', disabled: !isEnabled || isReadonly, onMouseDown: (e) => {
10552
+ if (!isEnabled || isReadonly)
10553
+ return;
10554
+ e.preventDefault();
10555
+ openLookup();
10556
+ }, onKeyDown: (e) => {
10557
+ if (!isEnabled || isReadonly)
10558
+ return;
10559
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
10560
+ e.preventDefault();
10561
+ openLookup();
10562
+ }
10563
+ }, onChange: () => undefined, onBlur: onBlur, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
10564
+ ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
10565
+ : 'border-gray-300'} ${!isEnabled || isReadonly
10566
+ ? 'bg-gray-100 cursor-not-allowed'
10567
+ : 'bg-white cursor-pointer'}`, style: { borderRadius: '10px' }, title: hasValue ? displayName : placeholder, children: [jsxRuntimeExports.jsx("option", { value: "", children: placeholder }), hasValue && jsxRuntimeExports.jsx("option", { value: "__selected__", children: displayName })] }));
10568
+ if (isReadonly && !isCompact) {
10569
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] flex flex-col sm:flex-row sm:items-start", children: [rawLabel && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1 text-base text-gray-900 font-medium", children: hasValue ? displayName : '-' })] }));
10570
+ }
10571
+ if (isReadonly && isCompact) {
10572
+ return (jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-900 truncate block", children: hasValue ? displayName : '-' }));
10573
+ }
10574
+ return (jsxRuntimeExports.jsxs("div", { className: isCompact ? 'table-cell-field w-full' : 'mb-[10px]', children: [isCompact ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [selectTrigger, jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] })) : (jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [rawLabel && (jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: rawLabel, required: isRequired })), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [selectTrigger, touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] })), isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[100]", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => {
10575
+ setIsOpen(false);
10576
+ onBlur();
10577
+ } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
10578
+ position: 'fixed',
10579
+ top: modalPos.y,
10580
+ left: modalPos.x,
10581
+ width: modalSize.w,
10582
+ height: modalSize.h,
10583
+ zIndex: 101,
10584
+ resize: 'both',
10585
+ minWidth: 340,
10586
+ minHeight: 260,
10587
+ maxWidth: '96vw',
10588
+ maxHeight: '92vh',
10589
+ backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
10590
+ borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
10591
+ boxShadow: '0 24px 64px rgba(0,0,0,0.28)',
10592
+ }, onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsxs("div", { onMouseDown: (e) => {
10593
+ e.preventDefault();
10594
+ isDragging.current = true;
10595
+ dragOrigin.current = {
10596
+ mouseX: e.clientX,
10597
+ mouseY: e.clientY,
10598
+ posX: modalPos.x,
10599
+ posY: modalPos.y,
10600
+ };
10601
+ }, className: "flex items-center justify-between px-5 py-4 flex-shrink-0 select-none border-b border-gray-200 cursor-grab", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: t?.('common.selectTitle', { label, defaultValue: `Select ${label}` }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
10602
+ setIsOpen(false);
10603
+ onBlur();
10604
+ }, onMouseDown: (e) => e.stopPropagation(), className: "p-0 border-0 bg-transparent cursor-pointer", "aria-label": t?.('common.close', { defaultValue: 'Close' }), children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "", className: "w-5 h-5 opacity-60" }) })] }), jsxRuntimeExports.jsx("div", { className: "px-5 py-3 flex-shrink-0 border-b border-gray-200", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-3 h-[30px] border border-gray-300 rounded-[10px] bg-white", children: [jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", value: searchText, onChange: (e) => setSearchText(e.target.value), onKeyDown: (e) => {
10605
+ if (e.key === 'Enter') {
10606
+ e.preventDefault();
10607
+ runSearch(searchText, 1);
10608
+ }
10609
+ }, placeholder: searchPlaceholder, className: "flex-1 outline-none text-sm text-gray-900 bg-transparent" }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => runSearch(searchText, 1), "aria-label": t?.('common.search', { defaultValue: 'Search' }), className: "flex-shrink-0 p-0 border-0 bg-transparent cursor-pointer", children: jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-40" }) })] }) }), jsxRuntimeExports.jsx("div", { className: "overflow-auto flex-1", children: searchResults.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-center text-sm text-gray-500 py-10", children: searchText
10610
+ ? t?.('common.noResults', { defaultValue: 'No results found' })
10611
+ : t?.('common.searchHint', {
10612
+ defaultValue: 'Type and press Enter or click search',
10613
+ }) })) : (jsxRuntimeExports.jsx("div", { className: "overflow-auto h-full", children: jsxRuntimeExports.jsxs("table", { className: "w-full text-sm border-collapse", children: [jsxRuntimeExports.jsx("thead", { className: "sticky top-0 z-[1] bg-gray-50", children: jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("th", { className: "text-left px-4 py-2 text-sm font-medium text-gray-600 whitespace-nowrap border-b border-gray-200 bg-gray-50", children: tSchema(t, 'record_name') }), jsxRuntimeExports.jsx("th", { className: "text-left px-4 py-2 text-sm font-medium text-gray-600 whitespace-nowrap border-b border-gray-200 bg-gray-50", children: tSchema(t, 'internal_record_id') })] }) }), jsxRuntimeExports.jsx("tbody", { children: searchResults.map((row, idx) => {
10614
+ const rowKey = row.internal_record_id ?? idx;
10615
+ const isSelected = pendingRow?.internal_record_id != null &&
10616
+ rowKey === pendingRow.internal_record_id;
10617
+ return (jsxRuntimeExports.jsxs("tr", { onClick: () => setPendingRow(row), onDoubleClick: () => applySelection(row), className: `cursor-pointer border-b border-gray-100 transition-colors ${isSelected ? 'bg-blue-100' : 'hover:bg-blue-50'}`, children: [jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: parentLabel(row) || '-' }), jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: row.internal_record_id != null
10618
+ ? String(row.internal_record_id)
10619
+ : '-' })] }, rowKey));
10620
+ }) })] }) })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-600", children: [totalCount === 1
10621
+ ? t?.('common.record', {
10622
+ count: totalCount,
10623
+ defaultValue: `${totalCount} record`,
10624
+ })
10625
+ : t?.('common.records', {
10626
+ count: totalCount,
10627
+ defaultValue: `${totalCount} records`,
10628
+ }), totalCount > 0 &&
10629
+ ` · ${(currentPage - 1) * pageSize + 1}-${Math.min(currentPage * pageSize, totalCount)}`] })), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [totalCount !== null && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: () => currentPage > 1 && runSearch(searchText, currentPage - 1), disabled: currentPage <= 1, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: t?.('common.previous', { defaultValue: 'Prev' }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => currentPage < totalPages && runSearch(searchText, currentPage + 1), disabled: currentPage >= totalPages, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: t?.('common.next', { defaultValue: 'Next' }) })] })), hasValue && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
10630
+ clearSelection();
10631
+ setIsOpen(false);
10632
+ onBlur();
10633
+ }, className: "px-4 h-9 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 flex-shrink-0", children: t?.('common.remove', { defaultValue: 'Clear' }) })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] })] }))] }));
10634
+ };
10635
+
10174
10636
  const MultiSelectWidget = ({ config }) => {
10175
10637
  const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
10176
10638
  const { t } = useWidgetContext();
@@ -11375,6 +11837,7 @@ const registerDefaultWidgets = () => {
11375
11837
  widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
11376
11838
  widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
11377
11839
  widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
11840
+ widgetRegistry.register({ widget: 'parent-lookup', component: ParentLookupWidget });
11378
11841
  widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
11379
11842
  widgetRegistry.register({ widget: 'geo-hierarchy', component: GeoHierarchyWidget });
11380
11843
  widgetRegistry.register({ widget: 'docs', component: DocsWidget });
@@ -11499,6 +11962,7 @@ exports.JSONEditorPanel = JSONEditorPanel;
11499
11962
  exports.MultiSelectWidget = MultiSelectWidget;
11500
11963
  exports.NumberInputWidget = NumberInputWidget;
11501
11964
  exports.PanelRenderer = PanelRenderer;
11965
+ exports.ParentLookupWidget = ParentLookupWidget;
11502
11966
  exports.PhoneInputWidget = PhoneInputWidget;
11503
11967
  exports.ProfileWidget = ProfileWidget;
11504
11968
  exports.PropertyEditor = PropertyEditor;