@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/README.md +1 -1
- package/dist/components/SectionBuilder/schemas.d.ts +5 -5
- package/dist/components/SectionBuilder/schemas.d.ts.map +1 -1
- package/dist/components/WidgetProvider.d.ts +3 -1
- package/dist/components/WidgetProvider.d.ts.map +1 -1
- package/dist/hooks/useBaseWidget.d.ts.map +1 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.esm.js +500 -42
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +500 -41
- package/dist/index.js.map +1 -1
- package/dist/registry/defaultWidgets.d.ts.map +1 -1
- package/dist/widgets/DialogTableWidget.d.ts.map +1 -1
- package/dist/widgets/ParentLookupWidget.d.ts +12 -0
- package/dist/widgets/ParentLookupWidget.d.ts.map +1 -0
- package/dist/widgets/SelectWidget.d.ts.map +1 -1
- package/dist/widgets/TableWidget.d.ts.map +1 -1
- package/dist/widgets/index.d.ts +1 -0
- package/dist/widgets/index.d.ts.map +1 -1
- package/package.json +1 -1
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", {
|
|
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
|
-
|
|
7444
|
+
const rowData = resolveEditingRowData();
|
|
7445
|
+
if (!rowData) {
|
|
7404
7446
|
return false;
|
|
7405
7447
|
}
|
|
7406
|
-
return isTableRowDataValid(
|
|
7407
|
-
}, [
|
|
7448
|
+
return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
|
|
7449
|
+
}, [resolveEditingRowData, columns, isReadonly, resolveSchemaLabel]);
|
|
7408
7450
|
const canSaveNewRow = React.useMemo(() => {
|
|
7409
|
-
|
|
7451
|
+
const rowData = resolveNewRowData();
|
|
7452
|
+
if (!rowData) {
|
|
7410
7453
|
return false;
|
|
7411
7454
|
}
|
|
7412
|
-
return isTableRowDataValid(
|
|
7413
|
-
}, [
|
|
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
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
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
|
|
7483
|
-
setNewRowData({
|
|
7484
|
-
|
|
7485
|
-
|
|
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,
|
|
7543
|
+
}, [editingState, isAdding, widgetConfig, dispatch]);
|
|
7489
7544
|
const saveEdit = React.useCallback(async () => {
|
|
7490
|
-
|
|
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
|
-
|
|
7621
|
+
const rowData = resolveNewRowData();
|
|
7622
|
+
if (!isAdding || !rowData)
|
|
7567
7623
|
return;
|
|
7568
7624
|
if (!canSaveNewRow)
|
|
7569
7625
|
return;
|
|
7570
|
-
setLoadingRowIndex(-1);
|
|
7626
|
+
setLoadingRowIndex(-1);
|
|
7571
7627
|
try {
|
|
7572
|
-
let savedRow = { ...
|
|
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
|
-
|
|
7577
|
-
|
|
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,
|
|
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
|
-
|
|
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;
|