@openg2p/registry-widgets 1.1.0-dev.2 → 1.1.0-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
@@ -8525,7 +8525,7 @@ const TableCellSelect = ({ config, value, onValueChange }) => {
8525
8525
  backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8526
8526
  }, children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') || 'Select' }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }));
8527
8527
  };
8528
- const SelectDisplayValue = ({ config, value }) => {
8528
+ const SelectDisplayValue$1 = ({ config, value }) => {
8529
8529
  const { dataSourceOptions, loading } = useBaseWidget({ config });
8530
8530
  if (loading) {
8531
8531
  return jsxRuntimeExports.jsx("span", { children: "-" });
@@ -9019,7 +9019,7 @@ const TableWidget = ({ config }) => {
9019
9019
  'widget-readonly': true,
9020
9020
  'widget-data-path': undefined,
9021
9021
  };
9022
- return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: cellConfig, value: cellValue }) }));
9022
+ return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue$1, { config: cellConfig, value: cellValue }) }));
9023
9023
  }
9024
9024
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: displayValue }));
9025
9025
  }
@@ -9158,6 +9158,225 @@ const TableWidget = ({ config }) => {
9158
9158
  }, children: translate('common.cancel') || 'Cancel' })] }) })] }))] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] })] }));
9159
9159
  };
9160
9160
 
9161
+ // Display select value label in view mode
9162
+ const SelectDisplayValue = ({ config, value }) => {
9163
+ const { dataSourceOptions, loading } = useBaseWidget({ config });
9164
+ if (loading)
9165
+ return jsxRuntimeExports.jsx("span", { children: "-" });
9166
+ if (value === null || value === undefined || value === '')
9167
+ return jsxRuntimeExports.jsx("span", { children: "-" });
9168
+ const selectedOption = dataSourceOptions.find((option) => option.value === value);
9169
+ return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
9170
+ };
9171
+ /**
9172
+ * Dialog table widget:
9173
+ * - Table displays a subset of columns (n out of x)
9174
+ * - Add/Edit happens in a modal dialog that shows ALL columns as a form
9175
+ *
9176
+ * Usage in schema:
9177
+ * {
9178
+ * "widget": "dialog-table",
9179
+ * "widget-type": "table",
9180
+ * "widget-label": "Household Members",
9181
+ * "widget-id": "householdMembers",
9182
+ * "widget-data-path": "household.members",
9183
+ * "widget-data-columns": [ ...all columns... ],
9184
+ * "widget-data-visible-columns": ["firstName", "lastName", "dob"], // optional override; default = by column flag
9185
+ * // Per-column control (recommended): set "column-visible-in-table": false to hide it in the table
9186
+ * "widget-data-operations": { "add": true, "edit": true, "remove": true }
9187
+ * }
9188
+ */
9189
+ const DialogTableWidget = ({ config }) => {
9190
+ const { value, error, touched, isEnabled, onChange, config: widgetConfig } = useBaseWidget({ config });
9191
+ const { translate, translateConfig } = useWidgetTranslation();
9192
+ const rows = Array.isArray(value) ? value : [];
9193
+ const columns = widgetConfig['widget-data-columns'] || [];
9194
+ const operations = widgetConfig['widget-data-operations'] || {};
9195
+ const isReadonly = widgetConfig['widget-readonly'] || false;
9196
+ const visibleColumnKeys = widgetConfig['widget-data-visible-columns'];
9197
+ const visibleColumns = React.useMemo(() => {
9198
+ // 1) If explicit list provided, it wins
9199
+ if (Array.isArray(visibleColumnKeys) && visibleColumnKeys.length > 0) {
9200
+ const keySet = new Set(visibleColumnKeys);
9201
+ return columns.filter((c) => keySet.has(c['column-key']));
9202
+ }
9203
+ // 2) Otherwise decide per column (default = visible)
9204
+ return columns.filter((c) => c['column-visible-in-table'] !== false);
9205
+ }, [columns, visibleColumnKeys]);
9206
+ const [dialogOpen, setDialogOpen] = React.useState(false);
9207
+ const [dialogMode, setDialogMode] = React.useState('add');
9208
+ const [activeRowIndex, setActiveRowIndex] = React.useState(null);
9209
+ const [formData, setFormData] = React.useState({});
9210
+ const addDialogTitle = translateConfig(widgetConfig['widget-data-dialog-title-add']) ||
9211
+ translate('table.addRecordDialog') ||
9212
+ 'Add record';
9213
+ const editDialogTitle = translateConfig(widgetConfig['widget-data-dialog-title-edit']) ||
9214
+ translate('table.editRecordDialog') ||
9215
+ 'Edit record';
9216
+ const buildEmptyRow = React.useCallback(() => {
9217
+ const emptyRow = {};
9218
+ columns.forEach((col) => {
9219
+ const key = col['column-key'];
9220
+ emptyRow[key] = col['widget-data-default'] ?? '';
9221
+ });
9222
+ return emptyRow;
9223
+ }, [columns]);
9224
+ const openAddDialog = React.useCallback(() => {
9225
+ setDialogMode('add');
9226
+ setActiveRowIndex(null);
9227
+ setFormData(buildEmptyRow());
9228
+ setDialogOpen(true);
9229
+ }, [buildEmptyRow]);
9230
+ const openEditDialog = React.useCallback((rowIndex) => {
9231
+ const row = rows[rowIndex] || {};
9232
+ const nextFormData = buildEmptyRow();
9233
+ columns.forEach((col) => {
9234
+ const key = col['column-key'];
9235
+ if (row[key] !== undefined)
9236
+ nextFormData[key] = row[key];
9237
+ });
9238
+ setDialogMode('edit');
9239
+ setActiveRowIndex(rowIndex);
9240
+ setFormData(nextFormData);
9241
+ setDialogOpen(true);
9242
+ }, [rows, columns, buildEmptyRow]);
9243
+ const closeDialog = React.useCallback(() => {
9244
+ setDialogOpen(false);
9245
+ setActiveRowIndex(null);
9246
+ setFormData({});
9247
+ }, []);
9248
+ const updateField = React.useCallback((columnKey, newValue) => {
9249
+ setFormData((prev) => ({ ...prev, [columnKey]: newValue }));
9250
+ }, []);
9251
+ const saveDialog = React.useCallback(() => {
9252
+ if (dialogMode === 'add') {
9253
+ const savedRow = { ...formData, edit_action: 'ADD' };
9254
+ onChange([...rows, savedRow]);
9255
+ closeDialog();
9256
+ return;
9257
+ }
9258
+ if (dialogMode === 'edit' && activeRowIndex !== null) {
9259
+ const newRows = [...rows];
9260
+ const currentRow = newRows[activeRowIndex] || {};
9261
+ const wasDeleted = currentRow.edit_action === 'DELETE';
9262
+ const editAction = wasDeleted ? 'UPDATE' : (currentRow.edit_action ?? 'UPDATE');
9263
+ newRows[activeRowIndex] = { ...currentRow, ...formData, edit_action: editAction };
9264
+ onChange(newRows);
9265
+ closeDialog();
9266
+ }
9267
+ }, [dialogMode, formData, onChange, rows, closeDialog, activeRowIndex]);
9268
+ const deleteRow = React.useCallback((rowIndex) => {
9269
+ const newRows = rows.filter((_, i) => i !== rowIndex);
9270
+ onChange(newRows);
9271
+ }, [rows, onChange]);
9272
+ const getDisplayValue = React.useCallback((rowIndex, column) => {
9273
+ const key = column['column-key'];
9274
+ const cellValue = rows[rowIndex]?.[key];
9275
+ const widgetType = column.widget || 'text';
9276
+ if (cellValue === null || cellValue === undefined || cellValue === '')
9277
+ return '-';
9278
+ if (widgetType === 'select')
9279
+ return null; // handled by SelectDisplayValue
9280
+ if (column['widget-data-format'])
9281
+ return formatValue(cellValue, column['widget-data-format'], column.widget);
9282
+ return String(cellValue);
9283
+ }, [rows]);
9284
+ const tableWidgetId = `dialog-table-widget-${widgetConfig['widget-id']}`;
9285
+ const columnSpan = widgetConfig['widget-column-span'] || 2;
9286
+ const minWidth = columnSpan * 200;
9287
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
9288
+ .${tableWidgetId} {
9289
+ width: 100%;
9290
+ min-width: ${minWidth}px;
9291
+ }
9292
+
9293
+ .widget-container[data-widget-id="${widgetConfig['widget-id']}"] {
9294
+ min-width: ${minWidth}px;
9295
+ width: 100%;
9296
+ flex: none;
9297
+ }
9298
+
9299
+ .panel-horizontal .widget-container[data-widget-id="${widgetConfig['widget-id']}"],
9300
+ [data-panel-orientation="horizontal"] .widget-container[data-widget-id="${widgetConfig['widget-id']}"] {
9301
+ grid-column: span ${columnSpan};
9302
+ }
9303
+ ` }), jsxRuntimeExports.jsxs("div", { className: `table-widget-container ${tableWidgetId}`, children: [operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("div", { className: "flex justify-end mb-2", children: jsxRuntimeExports.jsx("button", { type: "button", onClick: openAddDialog, className: "px-3 py-1 text-sm disabled:opacity-50 disabled:cursor-not-allowed", style: {
9304
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
9305
+ border: '1px solid var(--owt-btn-primary-border, #F07B1A)',
9306
+ backgroundColor: 'var(--owt-color-primary, #F5BB1A)',
9307
+ color: 'var(--owt-color-bg, #FFFFFF)',
9308
+ }, children: translate('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
9309
+ borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
9310
+ borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
9311
+ }, 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: translateConfig(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: translate('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: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => (jsxRuntimeExports.jsxs("tr", { style: {
9312
+ borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
9313
+ backgroundColor: row?.edit_action === 'DELETE'
9314
+ ? 'var(--owt-widget-table-deleted-row-bg, #FEE2E2)'
9315
+ : undefined,
9316
+ }, children: [visibleColumns.map((col) => {
9317
+ const key = col['column-key'];
9318
+ const widgetType = col.widget || 'text';
9319
+ const displayValue = getDisplayValue(rowIndex, col);
9320
+ if (widgetType === 'select' && displayValue === null) {
9321
+ const displayConfig = {
9322
+ ...col,
9323
+ 'widget-id': `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`,
9324
+ 'widget-label': '',
9325
+ 'widget-readonly': true,
9326
+ 'widget-data-path': undefined,
9327
+ };
9328
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: displayConfig, value: row?.[key] }) }) }, key));
9329
+ }
9330
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", children: displayValue }) }, key));
9331
+ }), ((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: {
9332
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
9333
+ color: 'var(--owt-color-primary-dark, #F07B1A)',
9334
+ backgroundColor: 'transparent',
9335
+ border: 'none',
9336
+ }, children: translate('common.edit') || 'Edit' })), operations.remove && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => deleteRow(rowIndex), disabled: !isEnabled, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
9337
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
9338
+ color: 'var(--owt-color-error, #B91C1C)',
9339
+ backgroundColor: 'transparent',
9340
+ border: 'none',
9341
+ }, children: translate('common.remove') || 'Delete' }))] }) }))] }, rowIndex)))] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] }), dialogOpen && (jsxRuntimeExports.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, children: jsxRuntimeExports.jsxs("div", { className: "rounded-lg p-6 w-full mx-4", style: {
9342
+ maxWidth: '900px',
9343
+ backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
9344
+ borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
9345
+ }, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-4 mb-4", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold", style: { color: 'var(--owt-color-text, #011627)' }, children: dialogMode === 'add' ? addDialogTitle : editDialogTitle }), jsxRuntimeExports.jsx("button", { type: "button", onClick: closeDialog, style: {
9346
+ border: 'none',
9347
+ background: 'transparent',
9348
+ color: 'var(--owt-color-text-muted, #727474)',
9349
+ cursor: 'pointer',
9350
+ fontSize: '20px',
9351
+ lineHeight: 1,
9352
+ }, "aria-label": "Close", children: "\u00D7" })] }), jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", style: { maxHeight: '70vh', overflow: 'auto' }, children: columns.map((col) => {
9353
+ const key = col['column-key'];
9354
+ const widgetType = col.widget || 'text';
9355
+ const cellWidgetId = `${widgetConfig['widget-id']}-dialog-${dialogMode}-${key}`;
9356
+ const fieldConfig = {
9357
+ ...col,
9358
+ widget: widgetType,
9359
+ 'widget-type': col['widget-type'] || 'input',
9360
+ 'widget-id': cellWidgetId,
9361
+ 'widget-label': col['widget-label'],
9362
+ 'widget-readonly': isReadonly || col['widget-readonly'] === true,
9363
+ 'widget-data-path': undefined,
9364
+ 'widget-data-default': formData[key] ?? col['widget-data-default'] ?? '',
9365
+ };
9366
+ return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig, schemaData: { [cellWidgetId]: formData[key] ?? col['widget-data-default'] ?? '' }, onValueChange: (_widgetId, newValue) => updateField(key, newValue) }) }, key));
9367
+ }) }), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3 mt-6", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: closeDialog, className: "px-4 py-2 text-sm font-medium", style: {
9368
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
9369
+ border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
9370
+ backgroundColor: 'var(--owt-btn-secondary-bg, #FFFFFF)',
9371
+ color: 'var(--owt-btn-secondary-color, #011627)',
9372
+ }, children: translate('common.cancel') || 'Cancel' }), jsxRuntimeExports.jsx("button", { type: "button", onClick: saveDialog, disabled: isReadonly || !isEnabled, className: "px-4 py-2 text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed", style: {
9373
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
9374
+ border: '1px solid var(--owt-btn-primary-border, #F07B1A)',
9375
+ backgroundColor: 'var(--owt-color-primary, #F5BB1A)',
9376
+ color: 'var(--owt-color-bg, #FFFFFF)',
9377
+ }, children: translate('common.save') || 'Save' })] })] }) }))] }));
9378
+ };
9379
+
9161
9380
  const ProfileWidget = ({ config }) => {
9162
9381
  const { value, config: widgetConfig, getFieldValue, } = useBaseWidget({ config });
9163
9382
  const { translateConfig } = useWidgetTranslation();
@@ -9571,6 +9790,68 @@ const HeaderSectionWidget = ({ config }) => {
9571
9790
  const createdAt = findValue('createdAt') || '';
9572
9791
  const lastApprovedBy = findValue('lastApprovedBy') || '';
9573
9792
  const lastApprovedAt = findValue('lastApprovedAt') || '';
9793
+ // ── Validation: status change requires reason ──────────────────
9794
+ // Behavior:
9795
+ // - When status changes away from its initial value, clear reason and require it.
9796
+ // - When status returns to initial value (or a parent "Cancel" restores it), restore initial reason.
9797
+ const initialStatusRef = React.useRef(null);
9798
+ const initialReasonRef = React.useRef(null);
9799
+ const prevStatusRef = React.useRef(null);
9800
+ const [showReasonRequired, setShowReasonRequired] = React.useState(false);
9801
+ React.useEffect(() => {
9802
+ // Capture initial status once when it becomes available.
9803
+ if (initialStatusRef.current === null) {
9804
+ const v = statusValue === undefined || statusValue === null ? '' : String(statusValue);
9805
+ initialStatusRef.current = v;
9806
+ }
9807
+ }, [statusValue]);
9808
+ React.useEffect(() => {
9809
+ // Capture initial reason once when it becomes available.
9810
+ if (initialReasonRef.current === null) {
9811
+ const v = statusReason === undefined || statusReason === null ? '' : String(statusReason);
9812
+ initialReasonRef.current = v;
9813
+ }
9814
+ }, [statusReason]);
9815
+ const isStatusChanged = React.useMemo(() => {
9816
+ const initial = initialStatusRef.current;
9817
+ if (initial === null)
9818
+ return false;
9819
+ return String(statusValue) !== initial;
9820
+ }, [statusValue]);
9821
+ const isReasonMissing = React.useMemo(() => {
9822
+ if (!isStatusChanged)
9823
+ return false;
9824
+ return String(statusReason || '').trim().length === 0;
9825
+ }, [isStatusChanged, statusReason]);
9826
+ React.useEffect(() => {
9827
+ // When status changes:
9828
+ // - If moved away from initial → clear reason.
9829
+ // - If returned to initial → restore initial reason.
9830
+ if (isReadonly)
9831
+ return;
9832
+ if (initialStatusRef.current === null)
9833
+ return;
9834
+ const currentStatus = String(statusValue || '');
9835
+ if (prevStatusRef.current === currentStatus)
9836
+ return;
9837
+ prevStatusRef.current = currentStatus;
9838
+ const initialStatus = initialStatusRef.current;
9839
+ const initialReason = initialReasonRef.current ?? '';
9840
+ if (currentStatus === initialStatus) {
9841
+ // Reverted / cancelled back to original
9842
+ if (String(statusReason || '') !== String(initialReason || '')) {
9843
+ updateFieldValue('statusReason', initialReason);
9844
+ }
9845
+ setShowReasonRequired(false);
9846
+ return;
9847
+ }
9848
+ // Status changed to a new value: clear reason (so user must re-enter)
9849
+ if (String(statusReason || '').trim().length > 0) {
9850
+ updateFieldValue('statusReason', '');
9851
+ }
9852
+ setShowReasonRequired(true);
9853
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9854
+ }, [statusValue, isReadonly]);
9574
9855
  const score = React.useMemo(() => {
9575
9856
  const toNum = (v) => {
9576
9857
  if (v === null || v === undefined || String(v).trim() === '')
@@ -9584,7 +9865,9 @@ const HeaderSectionWidget = ({ config }) => {
9584
9865
  return null;
9585
9866
  const ratio = completion / ideal;
9586
9867
  const percent = Math.max(0, Math.min(100, Math.round(ratio * 100)));
9587
- return { completion, ideal, percent };
9868
+ const completionDisplay = Number.isInteger(completion) ? completion : Math.round(completion);
9869
+ const idealDisplay = Number.isInteger(ideal) ? ideal : Math.round(ideal);
9870
+ return { completion, ideal, completionDisplay, idealDisplay, percent };
9588
9871
  }, [completionScoreRaw, idealScoreRaw]);
9589
9872
  // ── Format options ────────────────────────────────────────────
9590
9873
  const format = (widgetConfig['widget-data-format'] || {});
@@ -9880,6 +10163,19 @@ const HeaderSectionWidget = ({ config }) => {
9880
10163
  box-shadow: 0 0 0 2px rgba(237, 124, 34, 0.15);
9881
10164
  }
9882
10165
 
10166
+ .${cls} .hdr-input--error {
10167
+ border-color: var(--owt-color-danger, #DC2626);
10168
+ box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.12);
10169
+ }
10170
+
10171
+ .${cls} .hdr-error-text {
10172
+ margin-left: calc(0px);
10173
+ color: var(--owt-color-danger, #DC2626);
10174
+ font-size: 0.75rem;
10175
+ line-height: 1.2;
10176
+ font-weight: 500;
10177
+ }
10178
+
9883
10179
  @media (max-width: 768px) {
9884
10180
  .${cls} {
9885
10181
  flex-direction: column;
@@ -9894,7 +10190,15 @@ const HeaderSectionWidget = ({ config }) => {
9894
10190
  .parentElement?.querySelector('.hdr-avatar-placeholder');
9895
10191
  if (placeholder)
9896
10192
  placeholder.style.display = 'flex';
9897
- } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: getLabel('select') }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsx("input", { type: "text", className: "hdr-input", value: statusReason, placeholder: getLabel('enterReason'), onChange: (e) => updateFieldValue('statusReason', e.target.value) }))] })] })] }), jsxRuntimeExports.jsx("div", { className: "hdr-right", children: jsxRuntimeExports.jsxs("div", { className: "hdr-right-top", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-col", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdAt || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] }), score ? (jsxRuntimeExports.jsx("div", { className: "hdr-score-ring", style: { ['--pct']: score.percent }, "aria-label": `Completion score ${score.completion} of ${score.ideal} (${score.percent}%)`, title: `${score.completion} / ${score.ideal} (${score.percent}%)`, children: jsxRuntimeExports.jsx("div", { className: "hdr-score-value", children: String(score.completion) }) })) : null] }) })] })] }));
10193
+ } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: getLabel('select') }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [jsxRuntimeExports.jsx("input", { type: "text", className: `hdr-input ${(!isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing) ? 'hdr-input--error' : ''}`, value: statusReason, placeholder: getLabel('enterReason'), required: isStatusChanged, "aria-required": isStatusChanged, "aria-invalid": !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing, onBlur: () => {
10194
+ if (isReasonMissing)
10195
+ setShowReasonRequired(true);
10196
+ }, onChange: (e) => {
10197
+ updateFieldValue('statusReason', e.target.value);
10198
+ if (showReasonRequired && String(e.target.value || '').trim().length > 0) {
10199
+ setShowReasonRequired(false);
10200
+ }
10201
+ } }), !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing ? (jsxRuntimeExports.jsx("div", { className: "hdr-error-text", children: getLabel('enterReason') })) : null] }))] })] })] }), jsxRuntimeExports.jsx("div", { className: "hdr-right", children: jsxRuntimeExports.jsxs("div", { className: "hdr-right-top", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-col", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdAt || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] }), score ? (jsxRuntimeExports.jsx("div", { className: "hdr-score-ring", style: { ['--pct']: score.percent }, "aria-label": `Completion score ${score.completionDisplay} of ${score.idealDisplay} (${score.percent}%)`, title: `${score.completionDisplay} / ${score.idealDisplay} (${score.percent}%)`, children: jsxRuntimeExports.jsx("div", { className: "hdr-score-value", children: String(score.completionDisplay) }) })) : null] }) })] })] }));
9898
10202
  };
9899
10203
 
9900
10204
  function getValueByPathOrKey(obj, path) {
@@ -10195,7 +10499,8 @@ function pickAuthorizationUrl(resp, explicitKey) {
10195
10499
  if (v)
10196
10500
  return v;
10197
10501
  }
10198
- return (tryKey('authorization_url') ||
10502
+ return (tryKey('authentication_url') ||
10503
+ tryKey('authorization_url') ||
10199
10504
  tryKey('authorizationUrl') ||
10200
10505
  tryKey('auth_url') ||
10201
10506
  tryKey('authUrl') ||
@@ -10210,12 +10515,14 @@ function resolveValueFromSources(path, values, schemaData) {
10210
10515
  return fromValues;
10211
10516
  return getValueByPath(schemaData, path);
10212
10517
  }
10213
- async function fetchProviderAuthorizationUrl(authConfig, dataSourceRequestHandler, _values, _schemaData) {
10214
- const response = await dataSourceRequestHandler(authConfig.service, authConfig.endpoint, authConfig.method || 'GET', {});
10215
- const payload = response?.response_body?.response_payload && typeof response.response_body.response_payload === 'object'
10216
- ? response.response_body.response_payload
10217
- : response;
10218
- return pickAuthorizationUrl(payload, authConfig.authorizationUrlKey);
10518
+ function unwrapPayload(response) {
10519
+ if (response && typeof response === 'object') {
10520
+ if (response.response_body?.response_payload !== undefined)
10521
+ return response.response_body.response_payload;
10522
+ if (response.response_payload !== undefined)
10523
+ return response.response_payload;
10524
+ }
10525
+ return response;
10219
10526
  }
10220
10527
  const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10221
10528
  const { dataSourceRequestHandler, schemaData: ctxSchemaData } = useWidgetContext();
@@ -10229,6 +10536,11 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10229
10536
  return dataPath;
10230
10537
  }, [dataPath]);
10231
10538
  const authConfig = config['widget-auth-config'];
10539
+ const registerId = resolveValueFromSources(paths.registerId, values, schemaData);
10540
+ const internalRecordId = resolveValueFromSources(paths.internalRecordId, values, schemaData);
10541
+ const initiatedByStaffId = resolveValueFromSources(paths.initiatedByStaffId, values, schemaData);
10542
+ const providerId = authConfig?.providerId;
10543
+ const providerName = authConfig?.providerName;
10232
10544
  const foundationalId = resolveValueFromSources(paths.foundationalId, values, schemaData);
10233
10545
  const lastAuthenticatedOn = resolveValueFromSources(paths.lastAuthenticatedOn, values, schemaData);
10234
10546
  const lastAuthStatusRaw = resolveValueFromSources(paths.lastAuthenticationStatus, values, schemaData);
@@ -10237,11 +10549,11 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10237
10549
  const status = React.useMemo(() => normalizeStatus(lastAuthStatusRaw), [lastAuthStatusRaw]);
10238
10550
  /** URL from prefetch (or default); used when opening the OIDC / eSignet popup */
10239
10551
  const [resolvedAuthUrl, setResolvedAuthUrl] = React.useState(null);
10240
- const [providerLoading, setProviderLoading] = React.useState(false);
10241
10552
  const [authActionLoading, setAuthActionLoading] = React.useState(false);
10242
10553
  const [authError, setAuthError] = React.useState(null);
10243
10554
  const popupRef = React.useRef(null);
10244
10555
  const pollTimerRef = React.useRef(null);
10556
+ const [overlayUrl, setOverlayUrl] = React.useState(null);
10245
10557
  const emitHostEvent = React.useCallback((detail) => {
10246
10558
  if (typeof window === 'undefined')
10247
10559
  return;
@@ -10270,72 +10582,16 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10270
10582
  }
10271
10583
  };
10272
10584
  }, [cleanupPopup]);
10273
- const prefetchKey = React.useMemo(() => {
10274
- if (!authConfig)
10275
- return 'no-config';
10276
- return JSON.stringify({
10277
- s: authConfig.service,
10278
- e: authConfig.endpoint,
10279
- m: authConfig.method,
10280
- def: authConfig.defaultAuthorizationUrl,
10281
- prefetch: authConfig.prefetchOnMount,
10282
- });
10283
- }, [authConfig]);
10284
- // Prefetch provider login URL on mount (and when params / config change)
10585
+ // Provider details are supplied by host; clear any previous resolved URL on provider change.
10285
10586
  React.useEffect(() => {
10286
- if (!authConfig) {
10287
- setResolvedAuthUrl(null);
10288
- setProviderLoading(false);
10289
- return;
10290
- }
10291
- if (authConfig.prefetchOnMount === false) {
10292
- setResolvedAuthUrl(authConfig.defaultAuthorizationUrl || null);
10293
- setProviderLoading(false);
10294
- return;
10295
- }
10296
- const def = authConfig.defaultAuthorizationUrl;
10297
- if (def) {
10298
- setResolvedAuthUrl(def);
10299
- }
10300
- const canCallApi = Boolean(dataSourceRequestHandler && authConfig.service && authConfig.endpoint);
10301
- if (!canCallApi) {
10302
- setProviderLoading(false);
10303
- if (!def) {
10304
- setResolvedAuthUrl(null);
10305
- }
10587
+ setResolvedAuthUrl(null);
10588
+ }, [providerId, providerName]);
10589
+ const openAuthPopup = React.useCallback((authUrl) => {
10590
+ if (authConfig?.useIframeOverlay !== false) {
10591
+ setOverlayUrl(authUrl);
10592
+ emitHostEvent({ type: 'overlay_opened' });
10306
10593
  return;
10307
10594
  }
10308
- let cancelled = false;
10309
- setProviderLoading(true);
10310
- (async () => {
10311
- try {
10312
- const url = await fetchProviderAuthorizationUrl(authConfig, dataSourceRequestHandler, values, schemaData);
10313
- if (cancelled)
10314
- return;
10315
- if (url) {
10316
- setResolvedAuthUrl(url);
10317
- }
10318
- else if (!def) {
10319
- setResolvedAuthUrl(null);
10320
- }
10321
- }
10322
- catch {
10323
- if (cancelled)
10324
- return;
10325
- if (!def) {
10326
- setResolvedAuthUrl(null);
10327
- }
10328
- }
10329
- finally {
10330
- if (!cancelled)
10331
- setProviderLoading(false);
10332
- }
10333
- })();
10334
- return () => {
10335
- cancelled = true;
10336
- };
10337
- }, [authConfig, dataSourceRequestHandler, prefetchKey, values, schemaData]);
10338
- const openAuthPopup = React.useCallback((authUrl) => {
10339
10595
  const pw = authConfig?.popupWidth ?? 1024;
10340
10596
  const ph = authConfig?.popupHeight ?? 800;
10341
10597
  const features = getCenteredPopupFeatures(pw, ph);
@@ -10371,20 +10627,24 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10371
10627
  setAuthError('Missing widget-auth-config.');
10372
10628
  return;
10373
10629
  }
10374
- const def = authConfig.defaultAuthorizationUrl;
10375
- const canCallApi = Boolean(dataSourceRequestHandler && authConfig.service && authConfig.endpoint);
10630
+ const canCallAuthApi = Boolean(dataSourceRequestHandler && authConfig.service && authConfig.authenticateEndpoint);
10376
10631
  let url = resolvedAuthUrl;
10377
- if (!url && canCallApi) {
10632
+ if (!url && canCallAuthApi) {
10378
10633
  setAuthActionLoading(true);
10379
10634
  try {
10380
- const fetched = await fetchProviderAuthorizationUrl(authConfig, dataSourceRequestHandler, values, schemaData);
10381
- url = fetched || def || null;
10382
- if (fetched) {
10383
- setResolvedAuthUrl(fetched);
10384
- }
10635
+ const resp = await dataSourceRequestHandler(authConfig.service, authConfig.authenticateEndpoint, authConfig.authenticateMethod || 'POST', {
10636
+ register_id: registerId,
10637
+ internal_record_id: internalRecordId,
10638
+ provider_id: authConfig.providerId,
10639
+ initiated_by_staff_id: initiatedByStaffId,
10640
+ });
10641
+ const payload = unwrapPayload(resp);
10642
+ const authUrl = pickAuthorizationUrl(payload, authConfig.authorizationUrlKey);
10643
+ url = authUrl || null;
10644
+ if (authUrl)
10645
+ setResolvedAuthUrl(authUrl);
10385
10646
  }
10386
10647
  catch (e) {
10387
- url = def || null;
10388
10648
  if (!url) {
10389
10649
  setAuthError(e?.message || 'Could not load provider URL.');
10390
10650
  return;
@@ -10394,15 +10654,20 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10394
10654
  setAuthActionLoading(false);
10395
10655
  }
10396
10656
  }
10397
- else if (!url) {
10398
- url = def || null;
10399
- }
10400
10657
  if (!url) {
10401
- setAuthError('No authorization URL. Set widget-auth-config (service + endpoint, or defaultAuthorizationUrl) and dataSourceRequestHandler on WidgetProvider if using the API.');
10658
+ setAuthError('No authorization URL returned from authenticate_registrant.');
10402
10659
  return;
10403
10660
  }
10404
10661
  openAuthPopup(url);
10405
- }, [authConfig, dataSourceRequestHandler, openAuthPopup, resolvedAuthUrl, values, schemaData]);
10662
+ }, [
10663
+ authConfig,
10664
+ dataSourceRequestHandler,
10665
+ openAuthPopup,
10666
+ registerId,
10667
+ internalRecordId,
10668
+ initiatedByStaffId,
10669
+ resolvedAuthUrl,
10670
+ ]);
10406
10671
  React.useEffect(() => {
10407
10672
  const successType = authConfig?.successMessageType || 'openg2p:oidc:success';
10408
10673
  const handler = (event) => {
@@ -10447,8 +10712,7 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10447
10712
  return 'var(--owt-color-warning, #D97706)';
10448
10713
  return 'var(--owt-color-text-muted, #6B7280)';
10449
10714
  }, [status]);
10450
- const buttonBusy = authActionLoading ||
10451
- (providerLoading && !resolvedAuthUrl && !authConfig?.defaultAuthorizationUrl);
10715
+ const buttonBusy = authActionLoading;
10452
10716
  const buttonDisabled = !authConfig || buttonBusy;
10453
10717
  return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
10454
10718
  .${cls} {
@@ -10484,6 +10748,14 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10484
10748
  grid-column: 1 / -1;
10485
10749
  }
10486
10750
 
10751
+ /* Action cell: no left label spacer, align button to column start */
10752
+ .${cls} .auth-cell.auth-cell--action .auth-label {
10753
+ display: none;
10754
+ }
10755
+ .${cls} .auth-cell.auth-cell--action .auth-value {
10756
+ flex: 1 1 auto;
10757
+ }
10758
+
10487
10759
  .${cls} .auth-label {
10488
10760
  flex: 0 0 auto;
10489
10761
  min-width: 200px;
@@ -10506,17 +10778,15 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10506
10778
  word-break: break-word;
10507
10779
  }
10508
10780
 
10509
- .${cls} .auth-bottom-actions {
10510
- display: flex;
10511
- flex-direction: column;
10512
- align-items: flex-start;
10513
- justify-content: flex-start;
10514
- gap: 8px;
10515
- width: 100%;
10516
- margin-top: 20px;
10517
- margin-bottom: 0;
10781
+ .${cls} .auth-value--foundational {
10782
+ font-size: 18px;
10783
+ font-weight: 700;
10784
+ color: var(--owt-color-primary-dark, #F07B1A);
10785
+ letter-spacing: 0.1px;
10518
10786
  }
10519
10787
 
10788
+ /* Button is placed inside the grid (next to PSUT) */
10789
+
10520
10790
  .${cls} .auth-status {
10521
10791
  display: inline-flex;
10522
10792
  align-items: center;
@@ -10557,8 +10827,8 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10557
10827
  padding: 8px 24px;
10558
10828
  line-height: 1.5;
10559
10829
  border-radius: var(--owt-btn-border-radius, 10px);
10560
- border: 1px solid var(--owt-btn-primary-border, #F07B1A);
10561
- background-color: var(--owt-color-primary, #F5BB1A);
10830
+ border: 1px solid rgb(237, 124, 34);
10831
+ background-color: rgb(237, 124, 34);
10562
10832
  color: var(--owt-color-bg, #FFFFFF);
10563
10833
  font-family: Roboto, sans-serif;
10564
10834
  cursor: pointer;
@@ -10579,6 +10849,63 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10579
10849
  max-width: 100%;
10580
10850
  }
10581
10851
 
10852
+ .${cls} .overlay-backdrop {
10853
+ position: fixed;
10854
+ inset: 0;
10855
+ background: rgba(17, 24, 39, 0.55);
10856
+ z-index: 9999;
10857
+ display: flex;
10858
+ align-items: center;
10859
+ justify-content: center;
10860
+ padding: 24px;
10861
+ }
10862
+
10863
+ .${cls} .overlay-panel {
10864
+ width: min(1100px, 92vw);
10865
+ height: min(820px, 92vh);
10866
+ background: var(--owt-color-bg, #FFFFFF);
10867
+ border-radius: 12px;
10868
+ box-shadow: 0 10px 30px rgba(0,0,0,0.25);
10869
+ overflow: hidden;
10870
+ display: flex;
10871
+ flex-direction: column;
10872
+ }
10873
+
10874
+ .${cls} .overlay-header {
10875
+ display: flex;
10876
+ align-items: center;
10877
+ justify-content: space-between;
10878
+ padding: 10px 14px;
10879
+ border-bottom: 1px solid var(--owt-color-border-light, #E4E4E4);
10880
+ font-family: Roboto, sans-serif;
10881
+ }
10882
+
10883
+ .${cls} .overlay-title {
10884
+ font-size: 14px;
10885
+ color: var(--owt-color-text, #011627);
10886
+ font-weight: 600;
10887
+ min-width: 0;
10888
+ overflow: hidden;
10889
+ text-overflow: ellipsis;
10890
+ white-space: nowrap;
10891
+ }
10892
+
10893
+ .${cls} .overlay-close {
10894
+ border: 1px solid var(--owt-btn-secondary-border, #C4C4C4);
10895
+ background: var(--owt-btn-secondary-bg, #FFFFFF);
10896
+ color: var(--owt-btn-secondary-color, #011627);
10897
+ border-radius: var(--owt-btn-border-radius, 10px);
10898
+ padding: 6px 10px;
10899
+ font-size: 12px;
10900
+ cursor: pointer;
10901
+ }
10902
+
10903
+ .${cls} .overlay-iframe {
10904
+ flex: 1 1 auto;
10905
+ width: 100%;
10906
+ border: none;
10907
+ }
10908
+
10582
10909
  @media (max-width: 640px) {
10583
10910
  .${cls} .auth-grid {
10584
10911
  grid-template-columns: 1fr;
@@ -10593,7 +10920,15 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
10593
10920
  max-width: none;
10594
10921
  }
10595
10922
  }
10596
- ` }), jsxRuntimeExports.jsx("div", { className: cls, children: jsxRuntimeExports.jsxs("div", { className: "auth-content", children: [jsxRuntimeExports.jsxs("div", { className: "auth-grid", children: [jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Foundational ID:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: displayText(foundationalId) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authenticated on:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDateTime(lastAuthenticatedOn) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authentication status:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsxs("div", { className: "auth-status", "aria-label": `Authentication status: ${statusLabel}`, children: [jsxRuntimeExports.jsx("span", { className: "auth-dot" }), jsxRuntimeExports.jsx("span", { children: statusLabel })] }) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Expiry date:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDate(expiryDate) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell auth-cell--full", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Authentication token (PSUT):" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsx("div", { className: "auth-token", children: psut ? String(psut) : '-' }) })] })] }), jsxRuntimeExports.jsxs("div", { className: "auth-bottom-actions", children: [jsxRuntimeExports.jsx("button", { type: "button", className: "auth-button", onClick: onAuthenticate, disabled: buttonDisabled, children: buttonBusy ? 'Loading…' : 'Authenticate' }), authError ? jsxRuntimeExports.jsx("div", { className: "auth-error", children: authError }) : null] })] }) })] }));
10923
+ ` }), jsxRuntimeExports.jsxs("div", { className: cls, children: [overlayUrl ? (jsxRuntimeExports.jsx("div", { className: "overlay-backdrop", role: "dialog", "aria-modal": "true", "aria-label": "Authentication", onClick: (e) => {
10924
+ if (e.target === e.currentTarget) {
10925
+ setOverlayUrl(null);
10926
+ emitHostEvent({ type: 'overlay_closed' });
10927
+ }
10928
+ }, children: jsxRuntimeExports.jsxs("div", { className: "overlay-panel", children: [jsxRuntimeExports.jsxs("div", { className: "overlay-header", children: [jsxRuntimeExports.jsx("div", { className: "overlay-title", children: providerName ? `Authenticate via ${providerName}` : 'Authenticate' }), jsxRuntimeExports.jsx("button", { type: "button", className: "overlay-close", onClick: () => {
10929
+ setOverlayUrl(null);
10930
+ emitHostEvent({ type: 'overlay_closed' });
10931
+ }, children: "Close" })] }), jsxRuntimeExports.jsx("iframe", { className: "overlay-iframe", src: overlayUrl, title: "Authentication" })] }) })) : null, jsxRuntimeExports.jsx("div", { className: "auth-content", children: jsxRuntimeExports.jsxs("div", { className: "auth-grid", children: [jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Foundational ID:" }), jsxRuntimeExports.jsx("div", { className: "auth-value auth-value--foundational", children: displayText(foundationalId) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authenticated on:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDateTime(lastAuthenticatedOn) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Expiry date:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDate(expiryDate) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authentication status:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsxs("div", { className: "auth-status", "aria-label": `Authentication status: ${statusLabel}`, children: [jsxRuntimeExports.jsx("span", { className: "auth-dot" }), jsxRuntimeExports.jsx("span", { children: statusLabel })] }) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Authentication token (PSUT):" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsx("div", { className: "auth-token", children: psut ? String(psut) : '-' }) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell auth-cell--action", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", "aria-hidden": true }), jsxRuntimeExports.jsxs("div", { className: "auth-value", children: [jsxRuntimeExports.jsx("button", { type: "button", className: "auth-button", onClick: onAuthenticate, disabled: buttonDisabled, children: buttonBusy ? 'Loading…' : 'Authenticate' }), authError ? (jsxRuntimeExports.jsx("div", { className: "auth-error", style: { marginTop: 8 }, children: authError })) : null] })] })] }) })] })] }));
10597
10932
  };
10598
10933
 
10599
10934
  /**
@@ -10625,6 +10960,8 @@ const registerDefaultWidgets = () => {
10625
10960
  widgetRegistry.register({ widget: 'simple-table', component: SimpleTableWidget });
10626
10961
  // Table widget with record-level editing
10627
10962
  widgetRegistry.register({ widget: 'table', component: TableWidget });
10963
+ // Table widget with add/edit popup dialog
10964
+ widgetRegistry.register({ widget: 'dialog-table', component: DialogTableWidget });
10628
10965
  // Group widgets
10629
10966
  widgetRegistry.register({ widget: 'array-widget', component: ArrayWidget });
10630
10967
  widgetRegistry.register({ widget: 'iterable-accordion', component: IterableAccordionWidget });
@@ -11009,6 +11346,7 @@ exports.CheckboxWidget = CheckboxWidget;
11009
11346
  exports.CurrencyInputWidget = CurrencyInputWidget;
11010
11347
  exports.DateInputWidget = DateInputWidget;
11011
11348
  exports.DateTimeInputWidget = DateTimeInputWidget;
11349
+ exports.DialogTableWidget = DialogTableWidget;
11012
11350
  exports.DisplayWidget = DisplayWidget;
11013
11351
  exports.FileInputWidget = FileInputWidget;
11014
11352
  exports.HeaderSectionWidget = HeaderSectionWidget;