@openg2p/registry-widgets 1.1.6-dev.3 → 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`);
@@ -4389,6 +4391,8 @@ const WIDGET_TYPES = [
4389
4391
  'display',
4390
4392
  'profile',
4391
4393
  'geo-hierarchy',
4394
+ 'register-lookup',
4395
+ 'parent-lookup',
4392
4396
  ];
4393
4397
  const ORIENTATIONS = ['horizontal', 'vertical'];
4394
4398
  const CONDITION_OPERATORS = [
@@ -7012,7 +7016,18 @@ const SelectWidget = ({ config }) => {
7012
7016
  : (value != null && value !== '' ? tSchema(t, String(value)) : '-');
7013
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 }) })] }));
7014
7018
  }
7015
- 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 === ''))
7016
7031
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7017
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] }))] })] }) }));
7018
7033
  };
@@ -7399,18 +7414,46 @@ const TableWidget = ({ config }) => {
7399
7414
  const [originalRows, setOriginalRows] = React.useState(null);
7400
7415
  const isSectionEditMode = !isReadonly && operations.edit;
7401
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]);
7402
7443
  const canSaveEditingRow = React.useMemo(() => {
7403
- if (!editingState) {
7444
+ const rowData = resolveEditingRowData();
7445
+ if (!rowData) {
7404
7446
  return false;
7405
7447
  }
7406
- return isTableRowDataValid(editingState.currentValue, columns, isReadonly, resolveSchemaLabel);
7407
- }, [editingState, columns, isReadonly, resolveSchemaLabel]);
7448
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7449
+ }, [resolveEditingRowData, columns, isReadonly, resolveSchemaLabel]);
7408
7450
  const canSaveNewRow = React.useMemo(() => {
7409
- if (!isAdding || !newRowData) {
7451
+ const rowData = resolveNewRowData();
7452
+ if (!rowData) {
7410
7453
  return false;
7411
7454
  }
7412
- return isTableRowDataValid(newRowData, columns, isReadonly, resolveSchemaLabel);
7413
- }, [isAdding, newRowData, columns, isReadonly, resolveSchemaLabel]);
7455
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7456
+ }, [resolveNewRowData, columns, isReadonly, resolveSchemaLabel]);
7414
7457
  const showConfirmation = React.useCallback((message, onConfirm, onCancel) => {
7415
7458
  setConfirmationState({
7416
7459
  show: true,
@@ -7469,29 +7512,41 @@ const TableWidget = ({ config }) => {
7469
7512
  }, [isAnyRowEditing, rows, showConfirmation, cancelEdit, t]);
7470
7513
  const updateCellValue = React.useCallback((columnKey, newValue, rowIndex) => {
7471
7514
  if (editingState && rowIndex !== undefined) {
7472
- setEditingState({
7473
- ...editingState,
7474
- currentValue: {
7475
- ...editingState.currentValue,
7476
- [columnKey]: newValue,
7477
- },
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
+ };
7478
7525
  });
7479
7526
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7480
7527
  dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7481
7528
  }
7482
- else if (isAdding && newRowData) {
7483
- setNewRowData({
7484
- ...newRowData,
7485
- [columnKey]: newValue,
7529
+ else if (isAdding) {
7530
+ setNewRowData((prev) => {
7531
+ if (!prev)
7532
+ return prev;
7533
+ return {
7534
+ ...prev,
7535
+ [columnKey]: newValue,
7536
+ };
7486
7537
  });
7538
+ if (rowIndex !== undefined) {
7539
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7540
+ dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7541
+ }
7487
7542
  }
7488
- }, [editingState, isAdding, newRowData, widgetConfig, dispatch]);
7543
+ }, [editingState, isAdding, widgetConfig, dispatch]);
7489
7544
  const saveEdit = React.useCallback(async () => {
7490
- if (!editingState)
7545
+ const rowData = resolveEditingRowData();
7546
+ if (!editingState || !rowData)
7491
7547
  return;
7492
7548
  if (!canSaveEditingRow)
7493
7549
  return;
7494
- const rowData = editingState.currentValue;
7495
7550
  const rowIndex = editingState.rowIndex;
7496
7551
  setLoadingRowIndex(rowIndex);
7497
7552
  try {
@@ -7550,7 +7605,7 @@ const TableWidget = ({ config }) => {
7550
7605
  finally {
7551
7606
  setLoadingRowIndex(null);
7552
7607
  }
7553
- }, [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]);
7554
7609
  const startAdd = React.useCallback(() => {
7555
7610
  if (isAnyRowEditing) {
7556
7611
  cancelEdit();
@@ -7563,23 +7618,26 @@ const TableWidget = ({ config }) => {
7563
7618
  setNewRowData(emptyRow);
7564
7619
  }, [isAnyRowEditing, columns, cancelEdit]);
7565
7620
  const saveAdd = React.useCallback(async () => {
7566
- if (!isAdding || !newRowData)
7621
+ const rowData = resolveNewRowData();
7622
+ if (!isAdding || !rowData)
7567
7623
  return;
7568
7624
  if (!canSaveNewRow)
7569
7625
  return;
7570
- setLoadingRowIndex(-1); // Use -1 to indicate new row
7626
+ setLoadingRowIndex(-1);
7571
7627
  try {
7572
- let savedRow = { ...newRowData };
7573
- // TODO: Update to use dataSourceRequestHandler pattern
7628
+ let savedRow = { ...rowData };
7574
7629
  if (dataSourceRequestHandler && apiConfig.add) {
7575
7630
  console.warn('[TableWidget] API add operations require migration to dataSourceRequestHandler pattern');
7576
- const response = newRowData;
7577
- if (response && typeof response === 'object') {
7578
- savedRow = { ...savedRow, ...response };
7631
+ if (rowData && typeof rowData === 'object') {
7632
+ savedRow = { ...savedRow, ...rowData };
7579
7633
  }
7580
7634
  }
7581
7635
  savedRow = { ...savedRow, edit_action: 'ADD' };
7582
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
+ });
7583
7641
  setIsAdding(false);
7584
7642
  setNewRowData(null);
7585
7643
  }
@@ -7590,7 +7648,7 @@ const TableWidget = ({ config }) => {
7590
7648
  finally {
7591
7649
  setLoadingRowIndex(null);
7592
7650
  }
7593
- }, [isAdding, newRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode]);
7651
+ }, [isAdding, resolveNewRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, columns, widgetConfig, dispatch]);
7594
7652
  const deleteRow = React.useCallback(async (rowIndex) => {
7595
7653
  if (isAnyRowEditing) {
7596
7654
  showConfirmation(t?.('table.unsavedChanges') || 'You have unsaved changes. Do you want to discard them?', () => {
@@ -7641,6 +7699,11 @@ const TableWidget = ({ config }) => {
7641
7699
  return editingState.currentValue[columnKey];
7642
7700
  }
7643
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
+ }
7644
7707
  return newRowData?.[columnKey];
7645
7708
  }
7646
7709
  return rows[rowIndex]?.[columnKey];
@@ -7655,6 +7718,9 @@ const TableWidget = ({ config }) => {
7655
7718
  if (widgetType === 'select') {
7656
7719
  return null; // Will be handled by SelectDisplayValue component
7657
7720
  }
7721
+ if (widgetType === 'parent-lookup') {
7722
+ return null;
7723
+ }
7658
7724
  if (column['widget-data-format']) {
7659
7725
  return formatValue(cellValue, column['widget-data-format'], column.widget);
7660
7726
  }
@@ -7671,7 +7737,14 @@ const TableWidget = ({ config }) => {
7671
7737
  setOriginalRows(null);
7672
7738
  }
7673
7739
  }, [isSectionEditMode, rows, originalRows]);
7740
+ const editSessionKey = editingState
7741
+ ? `edit-${editingState.rowIndex}`
7742
+ : isAdding
7743
+ ? `add-${rows.length}`
7744
+ : null;
7674
7745
  React.useEffect(() => {
7746
+ if (!editSessionKey)
7747
+ return;
7675
7748
  if (editingState) {
7676
7749
  columns.forEach((col) => {
7677
7750
  const columnKey = col['column-key'];
@@ -7680,9 +7753,8 @@ const TableWidget = ({ config }) => {
7680
7753
  const defaultValue = cellValue !== undefined ? cellValue : (col['widget-data-default'] ?? '');
7681
7754
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7682
7755
  });
7756
+ return;
7683
7757
  }
7684
- }, [editingState, columns, widgetConfig, dispatch]);
7685
- React.useEffect(() => {
7686
7758
  if (isAdding && newRowData) {
7687
7759
  columns.forEach((col) => {
7688
7760
  const columnKey = col['column-key'];
@@ -7692,7 +7764,9 @@ const TableWidget = ({ config }) => {
7692
7764
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7693
7765
  });
7694
7766
  }
7695
- }, [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]);
7696
7770
  const getRowValuesForEdit = React.useCallback((rowIndex) => {
7697
7771
  if (editingState && editingState.rowIndex === rowIndex) {
7698
7772
  return editingState.currentValue ?? {};
@@ -7772,6 +7846,20 @@ const TableWidget = ({ config }) => {
7772
7846
  };
7773
7847
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue$1, { config: cellConfig, value: cellValue }) }));
7774
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
+ }
7775
7863
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: displayValue }));
7776
7864
  }
7777
7865
  }, [isRowEditing, getCellValue, getDisplayValue, renderTableCell]);
@@ -7867,7 +7955,7 @@ const TableWidget = ({ config }) => {
7867
7955
  border: '1px solid var(--owt-btn-primary-border, #F07B1A)',
7868
7956
  backgroundColor: 'var(--owt-color-primary, #F5BB1A)',
7869
7957
  color: 'var(--owt-color-bg, #FFFFFF)',
7870
- }, 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) => {
7871
7959
  const isEditing = isRowEditing(rowIndex);
7872
7960
  const isLoading = loadingRowIndex === rowIndex;
7873
7961
  return (jsxRuntimeExports.jsxs("tr", { className: `${isLoading ? 'opacity-50' : ''}${isEditing ? ' table-row-editing' : ''}`, style: {
@@ -7989,7 +8077,7 @@ const DialogTableField = React.memo(function DialogTableField({ col, cellWidgetI
7989
8077
  widget: widgetType,
7990
8078
  'widget-type': col['widget-type'] || 'input',
7991
8079
  'widget-id': cellWidgetId,
7992
- 'widget-label': col['widget-label'],
8080
+ 'widget-label': col['widget-label'] || col['column-label'] || col['column-key'] || '',
7993
8081
  'widget-readonly': isReadonly || col['widget-readonly'] === true,
7994
8082
  'widget-data-path': undefined,
7995
8083
  'widget-data-default': col['widget-data-default'],
@@ -8234,6 +8322,8 @@ const DialogTableWidget = ({ config }) => {
8234
8322
  return '-';
8235
8323
  if (widgetType === 'select')
8236
8324
  return null;
8325
+ if (widgetType === 'parent-lookup')
8326
+ return null;
8237
8327
  if (column['widget-data-format'])
8238
8328
  return formatValue(cellValue, column['widget-data-format'], column.widget);
8239
8329
  return String(cellValue);
@@ -8287,7 +8377,7 @@ const DialogTableWidget = ({ config }) => {
8287
8377
  }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
8288
8378
  borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
8289
8379
  borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
8290
- }, 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) => {
8291
8381
  const cellStyle = getRowCellStyle(row?.edit_action);
8292
8382
  return (jsxRuntimeExports.jsxs("tr", { style: {
8293
8383
  borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
@@ -8308,6 +8398,18 @@ const DialogTableWidget = ({ config }) => {
8308
8398
  };
8309
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));
8310
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
+ }
8311
8413
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: displayValue }) }, key));
8312
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: {
8313
8415
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
@@ -9882,7 +9984,7 @@ const normalizeDisplayFields = (row) => {
9882
9984
  value: f.value !== null && f.value !== undefined ? String(f.value) : '-',
9883
9985
  }));
9884
9986
  };
9885
- const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
9987
+ const parsePagination$1 = (pagination, rowCount, size, fallbackPage = 1) => {
9886
9988
  const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
9887
9989
  const totalPages = typeof pagination.number_of_pages === 'number'
9888
9990
  ? Math.max(1, pagination.number_of_pages)
@@ -10033,7 +10135,7 @@ const RegisterLookupWidget = ({ config }) => {
10033
10135
  const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
10034
10136
  if (match)
10035
10137
  return match;
10036
- totalPages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
10138
+ totalPages = parsePagination$1(pagination, rows.length, hydratePageSize).totalPages;
10037
10139
  if (page >= totalPages)
10038
10140
  break;
10039
10141
  page += 1;
@@ -10043,7 +10145,7 @@ const RegisterLookupWidget = ({ config }) => {
10043
10145
  const runSearch = React.useCallback(async (text, page = 1) => {
10044
10146
  try {
10045
10147
  const { rows, pagination } = await fetchRecords(text, page, pageSize);
10046
- const parsed = parsePagination(pagination, rows.length, pageSize, page);
10148
+ const parsed = parsePagination$1(pagination, rows.length, pageSize, page);
10047
10149
  setSearchResults(rows);
10048
10150
  setTotalCount(parsed.totalItems);
10049
10151
  setTotalPages(parsed.totalPages);
@@ -10176,6 +10278,361 @@ const RegisterLookupWidget = ({ config }) => {
10176
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 })] })] })] }))] }));
10177
10279
  };
10178
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
+
10179
10636
  const MultiSelectWidget = ({ config }) => {
10180
10637
  const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
10181
10638
  const { t } = useWidgetContext();
@@ -11380,6 +11837,7 @@ const registerDefaultWidgets = () => {
11380
11837
  widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
11381
11838
  widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
11382
11839
  widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
11840
+ widgetRegistry.register({ widget: 'parent-lookup', component: ParentLookupWidget });
11383
11841
  widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
11384
11842
  widgetRegistry.register({ widget: 'geo-hierarchy', component: GeoHierarchyWidget });
11385
11843
  widgetRegistry.register({ widget: 'docs', component: DocsWidget });
@@ -11504,6 +11962,7 @@ exports.JSONEditorPanel = JSONEditorPanel;
11504
11962
  exports.MultiSelectWidget = MultiSelectWidget;
11505
11963
  exports.NumberInputWidget = NumberInputWidget;
11506
11964
  exports.PanelRenderer = PanelRenderer;
11965
+ exports.ParentLookupWidget = ParentLookupWidget;
11507
11966
  exports.PhoneInputWidget = PhoneInputWidget;
11508
11967
  exports.ProfileWidget = ProfileWidget;
11509
11968
  exports.PropertyEditor = PropertyEditor;