@openg2p/registry-widgets 1.1.2-dev.1 → 1.1.2-dev.10
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/assets/index.d.ts +3 -1
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/components/SectionBuilder/schemas.d.ts +204 -0
- package/dist/components/SectionBuilder/schemas.d.ts.map +1 -1
- package/dist/components/SectionRenderer.d.ts.map +1 -1
- package/dist/components/WidgetFieldLabel.d.ts +11 -0
- package/dist/components/WidgetFieldLabel.d.ts.map +1 -0
- package/dist/hooks/useBaseWidget.d.ts +2 -0
- package/dist/hooks/useBaseWidget.d.ts.map +1 -1
- package/dist/hooks/useGeoWidgetCascade.d.ts.map +1 -1
- package/dist/index.d.ts +89 -28
- package/dist/index.esm.js +1655 -534
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1671 -532
- package/dist/index.js.map +1 -1
- package/dist/registry/defaultWidgets.d.ts.map +1 -1
- package/dist/types/index.d.ts +11 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/conditions.d.ts +17 -11
- package/dist/utils/conditions.d.ts.map +1 -1
- package/dist/utils/dataSource.d.ts +6 -0
- package/dist/utils/dataSource.d.ts.map +1 -1
- package/dist/utils/geoHierarchy.d.ts +42 -0
- package/dist/utils/geoHierarchy.d.ts.map +1 -1
- package/dist/utils/schemaNamespace.d.ts.map +1 -1
- package/dist/utils/schemaTranslation.d.ts.map +1 -1
- package/dist/utils/sectionRevert.d.ts +24 -0
- package/dist/utils/sectionRevert.d.ts.map +1 -0
- package/dist/utils/sectionValidate.d.ts.map +1 -1
- package/dist/widgets/ArrayWidget.d.ts.map +1 -1
- package/dist/widgets/BooleanWidget.d.ts.map +1 -1
- package/dist/widgets/CheckboxWidget.d.ts.map +1 -1
- package/dist/widgets/CurrencyInputWidget.d.ts.map +1 -1
- package/dist/widgets/DateInputWidget.d.ts.map +1 -1
- package/dist/widgets/DateTimeInputWidget.d.ts.map +1 -1
- package/dist/widgets/DialogTableWidget.d.ts +0 -13
- package/dist/widgets/DialogTableWidget.d.ts.map +1 -1
- package/dist/widgets/FileInputWidget.d.ts.map +1 -1
- package/dist/widgets/HeaderSectionWidget.d.ts.map +1 -1
- package/dist/widgets/IterableAccordionWidget.d.ts.map +1 -1
- package/dist/widgets/MultiSelectWidget.d.ts +7 -0
- package/dist/widgets/MultiSelectWidget.d.ts.map +1 -0
- package/dist/widgets/NumberInputWidget.d.ts.map +1 -1
- package/dist/widgets/PhoneInputWidget.d.ts.map +1 -1
- package/dist/widgets/RadioWidget.d.ts.map +1 -1
- package/dist/widgets/RegisterLookupWidget.d.ts +5 -0
- package/dist/widgets/RegisterLookupWidget.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/TextAreaWidget.d.ts.map +1 -1
- package/dist/widgets/TextInputWidget.d.ts.map +1 -1
- package/dist/widgets/index.d.ts +2 -0
- package/dist/widgets/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSlice, configureStore } from '@reduxjs/toolkit';
|
|
2
|
-
import React, { useContext, createContext, useMemo, useEffect, useRef, useCallback, useState, useId } from 'react';
|
|
2
|
+
import React, { useContext, createContext, useMemo, useEffect, useRef, useCallback, useState, useId, memo } from 'react';
|
|
3
3
|
import { Provider, useDispatch, useSelector, useStore } from 'react-redux';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
@@ -402,6 +402,18 @@ const createZodSchema = (validation, required = false) => {
|
|
|
402
402
|
return schema;
|
|
403
403
|
};
|
|
404
404
|
|
|
405
|
+
const normalizeBooleanLike = (val) => {
|
|
406
|
+
if (val === true || val === 1)
|
|
407
|
+
return true;
|
|
408
|
+
if (val === false || val === 0 || val === null || val === undefined || val === '') {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
if (typeof val === 'string') {
|
|
412
|
+
const normalized = val.trim().toLowerCase();
|
|
413
|
+
return normalized === 'true' || normalized === 'yes' || normalized === '1';
|
|
414
|
+
}
|
|
415
|
+
return Boolean(val);
|
|
416
|
+
};
|
|
405
417
|
/**
|
|
406
418
|
* Evaluate condition against field value
|
|
407
419
|
*/
|
|
@@ -410,6 +422,9 @@ const evaluateCondition = (condition, allValues) => {
|
|
|
410
422
|
const { operator, value } = condition;
|
|
411
423
|
switch (operator) {
|
|
412
424
|
case 'equals':
|
|
425
|
+
if (typeof value === 'boolean' || typeof fieldValue === 'boolean') {
|
|
426
|
+
return normalizeBooleanLike(fieldValue) === normalizeBooleanLike(value);
|
|
427
|
+
}
|
|
413
428
|
return fieldValue === value;
|
|
414
429
|
case 'notEquals':
|
|
415
430
|
return fieldValue !== value;
|
|
@@ -442,37 +457,62 @@ const evaluateCondition = (condition, allValues) => {
|
|
|
442
457
|
}
|
|
443
458
|
};
|
|
444
459
|
/**
|
|
445
|
-
*
|
|
460
|
+
* Normalize widget-data-options into a sequential list of action rules.
|
|
461
|
+
* Supports legacy single { action, condition } and new { actions: [...] }.
|
|
446
462
|
*/
|
|
447
|
-
const
|
|
448
|
-
if (!options
|
|
449
|
-
return
|
|
463
|
+
const normalizeOptionRules = (options) => {
|
|
464
|
+
if (!options) {
|
|
465
|
+
return [];
|
|
450
466
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return conditionResult;
|
|
467
|
+
if (Array.isArray(options.actions) && options.actions.length > 0) {
|
|
468
|
+
return options.actions.filter((rule) => !!rule?.action);
|
|
454
469
|
}
|
|
455
|
-
if (options.action
|
|
456
|
-
return
|
|
470
|
+
if (options.action && options.condition) {
|
|
471
|
+
return [{ action: options.action, condition: options.condition }];
|
|
457
472
|
}
|
|
458
|
-
return
|
|
473
|
+
return [];
|
|
474
|
+
};
|
|
475
|
+
const hasVisibilityRules = (options) => {
|
|
476
|
+
return normalizeOptionRules(options).some((rule) => rule.action === 'show' || rule.action === 'hide');
|
|
459
477
|
};
|
|
460
478
|
/**
|
|
461
|
-
*
|
|
479
|
+
* Evaluate widget-data-options rules sequentially.
|
|
480
|
+
* show/hide and enable/disable only affect visibility and enabled state.
|
|
481
|
+
* require is independent: required = widget-required OR require-condition-match.
|
|
462
482
|
*/
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
483
|
+
const evaluateWidgetConditions = (options, allValues, baseRequired = false) => {
|
|
484
|
+
let visible = true;
|
|
485
|
+
let enabled = true;
|
|
486
|
+
let required = baseRequired;
|
|
487
|
+
const rules = normalizeOptionRules(options);
|
|
488
|
+
for (const rule of rules) {
|
|
489
|
+
if (!rule.condition) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
const match = evaluateCondition(rule.condition, allValues);
|
|
493
|
+
switch (rule.action) {
|
|
494
|
+
case 'show':
|
|
495
|
+
visible = match;
|
|
496
|
+
break;
|
|
497
|
+
case 'hide':
|
|
498
|
+
visible = !match;
|
|
499
|
+
break;
|
|
500
|
+
case 'enable':
|
|
501
|
+
enabled = match;
|
|
502
|
+
break;
|
|
503
|
+
case 'disable':
|
|
504
|
+
enabled = !match;
|
|
505
|
+
break;
|
|
506
|
+
case 'require':
|
|
507
|
+
required = baseRequired || match;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
473
510
|
}
|
|
474
|
-
return
|
|
511
|
+
return { visible, enabled, required };
|
|
475
512
|
};
|
|
513
|
+
const shouldShowWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).visible;
|
|
514
|
+
const shouldEnableWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).enabled;
|
|
515
|
+
const shouldRequireWidget = (options, allValues, baseRequired = false) => evaluateWidgetConditions(options, allValues, baseRequired).required;
|
|
476
516
|
|
|
477
517
|
/**
|
|
478
518
|
* Format number with thousand and decimal separators
|
|
@@ -1042,6 +1082,67 @@ const formatValue = (value, format, widgetType) => {
|
|
|
1042
1082
|
return value?.toString() || '';
|
|
1043
1083
|
};
|
|
1044
1084
|
|
|
1085
|
+
const apiDataSourceCache = new Map();
|
|
1086
|
+
const apiDataSourceInflight = new Map();
|
|
1087
|
+
function buildApiRequestContext(dataSource, allValues, levelId) {
|
|
1088
|
+
let depValue = null;
|
|
1089
|
+
if (dataSource.dependsOn) {
|
|
1090
|
+
if (dataSource.dependsOn.includes('.')) {
|
|
1091
|
+
depValue = getValueByPath(allValues, dataSource.dependsOn);
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
depValue = resolveWidgetIdValue(allValues, dataSource.dependsOn);
|
|
1095
|
+
}
|
|
1096
|
+
if (depValue === null || depValue === undefined || depValue === '') {
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const method = dataSource.method || 'GET';
|
|
1101
|
+
const staticParams = { ...dataSource.params };
|
|
1102
|
+
const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
|
|
1103
|
+
for (const [key, value] of Object.entries(dataSource)) {
|
|
1104
|
+
if (!standardFields.includes(key) && value !== undefined && value !== null) {
|
|
1105
|
+
staticParams[key] = value;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (levelId) {
|
|
1109
|
+
staticParams.level_id = levelId;
|
|
1110
|
+
}
|
|
1111
|
+
const requestParams = { ...staticParams };
|
|
1112
|
+
if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
|
|
1113
|
+
const parentValueId = typeof depValue === 'object' && depValue !== null
|
|
1114
|
+
? (depValue.level_value_id || depValue.id || depValue.value || depValue)
|
|
1115
|
+
: depValue;
|
|
1116
|
+
if (staticParams.level_id) {
|
|
1117
|
+
requestParams.parent_level_value_id = parentValueId;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
|
|
1121
|
+
requestParams[paramKey] = parentValueId;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
else if (staticParams.level_id) {
|
|
1125
|
+
requestParams.parent_level_value_id = '';
|
|
1126
|
+
}
|
|
1127
|
+
const service = dataSource.service;
|
|
1128
|
+
const endpoint = dataSource.endpoint;
|
|
1129
|
+
if (!service || !endpoint) {
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
return { service, endpoint, method, requestParams };
|
|
1133
|
+
}
|
|
1134
|
+
function buildApiDataSourceCacheKey(service, endpoint, method, requestParams) {
|
|
1135
|
+
return `${service}|${endpoint}|${method}|${JSON.stringify(requestParams)}`;
|
|
1136
|
+
}
|
|
1137
|
+
/** Return cached API options when already fetched (e.g. duplicate table cells). */
|
|
1138
|
+
function getCachedApiDataSource(dataSource, allValues, levelId) {
|
|
1139
|
+
const context = buildApiRequestContext(dataSource, allValues, levelId);
|
|
1140
|
+
if (!context) {
|
|
1141
|
+
return undefined;
|
|
1142
|
+
}
|
|
1143
|
+
const cacheKey = buildApiDataSourceCacheKey(context.service, context.endpoint, context.method, context.requestParams);
|
|
1144
|
+
return apiDataSourceCache.get(cacheKey);
|
|
1145
|
+
}
|
|
1045
1146
|
/**
|
|
1046
1147
|
* Get static data source options
|
|
1047
1148
|
*/
|
|
@@ -1059,98 +1160,33 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
|
|
|
1059
1160
|
return [];
|
|
1060
1161
|
}
|
|
1061
1162
|
try {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
let depValue = null;
|
|
1065
|
-
if (dataSource.dependsOn) {
|
|
1066
|
-
if (dataSource.dependsOn.includes('.')) {
|
|
1067
|
-
depValue = getValueByPath(allValues, dataSource.dependsOn);
|
|
1068
|
-
}
|
|
1069
|
-
else {
|
|
1070
|
-
depValue = resolveWidgetIdValue(allValues, dataSource.dependsOn);
|
|
1071
|
-
}
|
|
1072
|
-
if (depValue === null || depValue === undefined || depValue === '') {
|
|
1073
|
-
// If dependency is empty, return empty array
|
|
1074
|
-
return [];
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
// Build request parameters
|
|
1078
|
-
const method = dataSource.method || 'GET';
|
|
1079
|
-
// Extract static params from dataSource
|
|
1080
|
-
// Include explicit params object and any additional fields (like level_id)
|
|
1081
|
-
const staticParams = { ...dataSource.params };
|
|
1082
|
-
// Extract additional fields that aren't part of the standard ApiDataSource interface
|
|
1083
|
-
// These are fields like level_id that might be directly on the dataSource
|
|
1084
|
-
// BUT: level_id should come from widget-geo-config.level, not from dataSource
|
|
1085
|
-
const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
|
|
1086
|
-
for (const [key, value] of Object.entries(dataSource)) {
|
|
1087
|
-
if (!standardFields.includes(key) && value !== undefined && value !== null) {
|
|
1088
|
-
staticParams[key] = value;
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
// If levelId is provided (from widget-geo-config.level), use it instead of any level_id in dataSource
|
|
1092
|
-
if (levelId) {
|
|
1093
|
-
staticParams.level_id = levelId;
|
|
1094
|
-
}
|
|
1095
|
-
// Build request params object
|
|
1096
|
-
const requestParams = { ...staticParams };
|
|
1097
|
-
// Add dependency value to params
|
|
1098
|
-
if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
|
|
1099
|
-
// Extract the actual value ID if depValue is an object
|
|
1100
|
-
const parentValueId = typeof depValue === 'object' && depValue !== null
|
|
1101
|
-
? (depValue.level_value_id || depValue.id || depValue.value || depValue)
|
|
1102
|
-
: depValue;
|
|
1103
|
-
// For geo APIs, use parent_level_value_id
|
|
1104
|
-
if (staticParams.level_id) {
|
|
1105
|
-
requestParams.parent_level_value_id = parentValueId;
|
|
1106
|
-
}
|
|
1107
|
-
else {
|
|
1108
|
-
// For other APIs, use the dependency field name as param key
|
|
1109
|
-
const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
|
|
1110
|
-
requestParams[paramKey] = parentValueId;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
else if (staticParams.level_id) {
|
|
1114
|
-
// First level has no parent, send empty string as many OpenG2P APIs expect it
|
|
1115
|
-
requestParams.parent_level_value_id = "";
|
|
1116
|
-
}
|
|
1117
|
-
// Get service mnemonic and endpoint (required)
|
|
1118
|
-
const service = dataSource.service;
|
|
1119
|
-
const endpoint = dataSource.endpoint;
|
|
1120
|
-
if (!service) {
|
|
1121
|
-
console.error('[getApiDataSource] API data source missing service mnemonic. Use "service" field instead of "url"');
|
|
1122
|
-
return [];
|
|
1123
|
-
}
|
|
1124
|
-
if (!endpoint) {
|
|
1125
|
-
console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
|
|
1163
|
+
const context = buildApiRequestContext(dataSource, allValues, levelId);
|
|
1164
|
+
if (!context) {
|
|
1126
1165
|
return [];
|
|
1127
1166
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
if (
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
return
|
|
1167
|
+
const { service, endpoint, method, requestParams } = context;
|
|
1168
|
+
const cacheKey = buildApiDataSourceCacheKey(service, endpoint, method, requestParams);
|
|
1169
|
+
const cached = apiDataSourceCache.get(cacheKey);
|
|
1170
|
+
if (cached) {
|
|
1171
|
+
return cached;
|
|
1172
|
+
}
|
|
1173
|
+
const inflight = apiDataSourceInflight.get(cacheKey);
|
|
1174
|
+
if (inflight) {
|
|
1175
|
+
return inflight;
|
|
1176
|
+
}
|
|
1177
|
+
const fetchPromise = (async () => {
|
|
1178
|
+
const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, { headers: dataSource.headers });
|
|
1179
|
+
const parsed = Array.isArray(response) ? response : [];
|
|
1180
|
+
apiDataSourceCache.set(cacheKey, parsed);
|
|
1181
|
+
return parsed;
|
|
1182
|
+
})();
|
|
1183
|
+
apiDataSourceInflight.set(cacheKey, fetchPromise);
|
|
1184
|
+
try {
|
|
1185
|
+
return await fetchPromise;
|
|
1143
1186
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (response.data && Array.isArray(response.data)) {
|
|
1147
|
-
return response.data;
|
|
1148
|
-
}
|
|
1149
|
-
if (response.results && Array.isArray(response.results)) {
|
|
1150
|
-
return response.results;
|
|
1151
|
-
}
|
|
1187
|
+
finally {
|
|
1188
|
+
apiDataSourceInflight.delete(cacheKey);
|
|
1152
1189
|
}
|
|
1153
|
-
return [];
|
|
1154
1190
|
}
|
|
1155
1191
|
catch (error) {
|
|
1156
1192
|
// Rethrow so useBaseWidget's catch can log it with full widget context
|
|
@@ -1936,6 +1972,98 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
|
|
|
1936
1972
|
return content;
|
|
1937
1973
|
};
|
|
1938
1974
|
|
|
1975
|
+
/**
|
|
1976
|
+
* Custom hook for widget translations
|
|
1977
|
+
* Provides translation function with widget-specific namespace and fallback support
|
|
1978
|
+
*/
|
|
1979
|
+
const useWidgetTranslation = () => {
|
|
1980
|
+
const { translate: translateFunction } = useWidgetContext();
|
|
1981
|
+
/**
|
|
1982
|
+
* Translate a key with flexible namespace support
|
|
1983
|
+
* Supports translation keys in various formats and direct strings
|
|
1984
|
+
*
|
|
1985
|
+
* Translation key formats supported:
|
|
1986
|
+
* - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
|
|
1987
|
+
* - "Name" - Direct string (will be looked up in flat translation structure)
|
|
1988
|
+
* - "sections.personalDetails" - Nested key (for backward compatibility)
|
|
1989
|
+
*
|
|
1990
|
+
* With flat translation structure, direct strings like "Name" are automatically
|
|
1991
|
+
* translated by looking them up in the translation resources.
|
|
1992
|
+
*
|
|
1993
|
+
* @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
|
|
1994
|
+
* @param options - Translation options (interpolation values, default value, etc.)
|
|
1995
|
+
* @returns Translated string or original string if translation not found
|
|
1996
|
+
*/
|
|
1997
|
+
const translate = (keyOrString, options) => {
|
|
1998
|
+
if (!keyOrString) {
|
|
1999
|
+
return options?.defaultValue || '';
|
|
2000
|
+
}
|
|
2001
|
+
// Use the provided translation function or fallback to the key
|
|
2002
|
+
if (translateFunction) {
|
|
2003
|
+
return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
|
|
2004
|
+
}
|
|
2005
|
+
// Fallback to key if no translation function available
|
|
2006
|
+
return options?.defaultValue || keyOrString;
|
|
2007
|
+
};
|
|
2008
|
+
/**
|
|
2009
|
+
* Translate widget config property
|
|
2010
|
+
* Attempts to translate the value, but if translation is not found,
|
|
2011
|
+
* returns the original value as-is (graceful fallback)
|
|
2012
|
+
*
|
|
2013
|
+
* This function will:
|
|
2014
|
+
* - Try to translate any string value
|
|
2015
|
+
* - If translation exists, use the translated value
|
|
2016
|
+
* - If translation doesn't exist (returns same value or throws), use original value
|
|
2017
|
+
* - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
|
|
2018
|
+
*/
|
|
2019
|
+
const translateConfig = (value, fallback) => {
|
|
2020
|
+
if (!value) {
|
|
2021
|
+
return fallback || '';
|
|
2022
|
+
}
|
|
2023
|
+
// Try to translate the value
|
|
2024
|
+
if (translateFunction) {
|
|
2025
|
+
try {
|
|
2026
|
+
// Pass defaultValue to ensure we get the original value if translation fails
|
|
2027
|
+
const translated = translateFunction(value, { defaultValue: value });
|
|
2028
|
+
// If translation returns empty, null, undefined, or the exact same value,
|
|
2029
|
+
// it means no translation was found - return the original value
|
|
2030
|
+
if (!translated || translated === value) {
|
|
2031
|
+
return value;
|
|
2032
|
+
}
|
|
2033
|
+
// Translation found, return it
|
|
2034
|
+
return translated;
|
|
2035
|
+
}
|
|
2036
|
+
catch (error) {
|
|
2037
|
+
// If translation throws an error (e.g., missing key warning), return original value
|
|
2038
|
+
return value;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
// No translation function available, return value as-is
|
|
2042
|
+
return value;
|
|
2043
|
+
};
|
|
2044
|
+
// No need of this getLanguage and changeLanguage functions
|
|
2045
|
+
/**
|
|
2046
|
+
* Get current language
|
|
2047
|
+
*/
|
|
2048
|
+
// const getLanguage = (): string => {
|
|
2049
|
+
// return i18n.language || 'en';
|
|
2050
|
+
// };
|
|
2051
|
+
/**
|
|
2052
|
+
* Change language
|
|
2053
|
+
*/
|
|
2054
|
+
// const changeLanguage = (lng: string): Promise<void> => {
|
|
2055
|
+
// return i18n.changeLanguage(lng).then(() => undefined);
|
|
2056
|
+
// };
|
|
2057
|
+
return {
|
|
2058
|
+
t: translate,
|
|
2059
|
+
translate,
|
|
2060
|
+
translateConfig,
|
|
2061
|
+
// getLanguage,
|
|
2062
|
+
// changeLanguage,
|
|
2063
|
+
// i18n: null,
|
|
2064
|
+
};
|
|
2065
|
+
};
|
|
2066
|
+
|
|
1939
2067
|
/**
|
|
1940
2068
|
* Geo Hierarchy Builder
|
|
1941
2069
|
* Manages geo hierarchy state and builds hierarchy JSON structure
|
|
@@ -2115,12 +2243,145 @@ const geoHierarchyBuilder = new GeoHierarchyBuilder();
|
|
|
2115
2243
|
/** Sentinel written to Redux when a geo level is cleared by cascade (distinct from "never set"). */
|
|
2116
2244
|
const GEO_LEVEL_CLEARED = null;
|
|
2117
2245
|
const geoWidgetParentRegistry = new Map();
|
|
2246
|
+
const geoWidgetConfigRegistry = new Map();
|
|
2118
2247
|
function registerGeoWidgetParent(widgetId, parentWidgetId) {
|
|
2119
2248
|
geoWidgetParentRegistry.set(widgetId, parentWidgetId || null);
|
|
2120
2249
|
}
|
|
2121
2250
|
function unregisterGeoWidgetParent(widgetId) {
|
|
2122
2251
|
geoWidgetParentRegistry.delete(widgetId);
|
|
2123
2252
|
}
|
|
2253
|
+
function registerGeoWidget(widgetId, geoConfig, dataPath) {
|
|
2254
|
+
if (typeof dataPath !== 'string') {
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
const parentWidgetId = geoConfig.parentWidgetId?.trim() ? geoConfig.parentWidgetId : null;
|
|
2258
|
+
registerGeoWidgetParent(widgetId, parentWidgetId);
|
|
2259
|
+
geoWidgetConfigRegistry.set(widgetId, {
|
|
2260
|
+
widgetId,
|
|
2261
|
+
parentWidgetId,
|
|
2262
|
+
level: geoConfig.level,
|
|
2263
|
+
geoConfig,
|
|
2264
|
+
dataPath,
|
|
2265
|
+
groupId: getGeoGroupId(dataPath),
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
function unregisterGeoWidget(widgetId) {
|
|
2269
|
+
unregisterGeoWidgetParent(widgetId);
|
|
2270
|
+
geoWidgetConfigRegistry.delete(widgetId);
|
|
2271
|
+
}
|
|
2272
|
+
function orderGeoWidgetRegistrations(registrations) {
|
|
2273
|
+
if (registrations.length <= 1) {
|
|
2274
|
+
return registrations;
|
|
2275
|
+
}
|
|
2276
|
+
const roots = registrations.filter((entry) => !entry.parentWidgetId);
|
|
2277
|
+
if (roots.length === 0) {
|
|
2278
|
+
return registrations;
|
|
2279
|
+
}
|
|
2280
|
+
const ordered = [];
|
|
2281
|
+
let current = roots[0];
|
|
2282
|
+
const visited = new Set();
|
|
2283
|
+
while (current && !visited.has(current.widgetId)) {
|
|
2284
|
+
visited.add(current.widgetId);
|
|
2285
|
+
ordered.push(current);
|
|
2286
|
+
current = registrations.find((entry) => entry.parentWidgetId === current.widgetId);
|
|
2287
|
+
}
|
|
2288
|
+
return ordered.length > 0 ? ordered : registrations;
|
|
2289
|
+
}
|
|
2290
|
+
function resolveLevelValueId(rawValue) {
|
|
2291
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
if (typeof rawValue === 'string' || typeof rawValue === 'number') {
|
|
2295
|
+
return String(rawValue);
|
|
2296
|
+
}
|
|
2297
|
+
if (typeof rawValue === 'object') {
|
|
2298
|
+
const id = rawValue.level_value_id || rawValue.id || rawValue.value;
|
|
2299
|
+
return id != null && id !== '' ? String(id) : null;
|
|
2300
|
+
}
|
|
2301
|
+
return null;
|
|
2302
|
+
}
|
|
2303
|
+
function resolveStoredMnemonic(values, registration, valueId) {
|
|
2304
|
+
const stored = getWidgetValue(values, registration.dataPath, registration.widgetId);
|
|
2305
|
+
const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
|
|
2306
|
+
if (!Array.isArray(hierarchy)) {
|
|
2307
|
+
return undefined;
|
|
2308
|
+
}
|
|
2309
|
+
const levelData = hierarchy.find((entry) => entry.level === registration.level);
|
|
2310
|
+
if (levelData && String(levelData.level_value_id) === String(valueId)) {
|
|
2311
|
+
return levelData.level_value_mnemonic ? String(levelData.level_value_mnemonic) : undefined;
|
|
2312
|
+
}
|
|
2313
|
+
return undefined;
|
|
2314
|
+
}
|
|
2315
|
+
/** Resolve display mnemonic from cached dropdown options, then stored hierarchy. */
|
|
2316
|
+
function createGeoLevelMnemonicResolver(values, dataSources) {
|
|
2317
|
+
return (registration, valueId) => {
|
|
2318
|
+
const options = dataSources[registration.widgetId];
|
|
2319
|
+
const option = options?.find((entry) => String(entry.value) === String(valueId));
|
|
2320
|
+
if (option?.label) {
|
|
2321
|
+
return option.label;
|
|
2322
|
+
}
|
|
2323
|
+
return resolveStoredMnemonic(values, registration, valueId);
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
/** Rebuild group hierarchy from widget values in parent→child order; stop at first missing level. */
|
|
2327
|
+
function rebuildGeoHierarchyFromRegistrations(groupId, values, registrations, resolveMnemonic) {
|
|
2328
|
+
const ordered = orderGeoWidgetRegistrations(registrations.filter((entry) => entry.groupId === groupId));
|
|
2329
|
+
geoHierarchyBuilder.clear(groupId);
|
|
2330
|
+
for (const registration of ordered) {
|
|
2331
|
+
const rawValue = resolveGeoWidgetLevelValue(values, registration.widgetId, registration.dataPath, registration.geoConfig);
|
|
2332
|
+
const valueId = resolveLevelValueId(rawValue);
|
|
2333
|
+
if (!valueId) {
|
|
2334
|
+
break;
|
|
2335
|
+
}
|
|
2336
|
+
const mnemonic = resolveMnemonic?.(registration, valueId) ??
|
|
2337
|
+
resolveStoredMnemonic(values, registration, valueId) ??
|
|
2338
|
+
valueId;
|
|
2339
|
+
geoHierarchyBuilder.addLevel(registration.level, valueId, mnemonic, groupId);
|
|
2340
|
+
}
|
|
2341
|
+
return geoHierarchyBuilder.buildHierarchyJson(groupId) !== null;
|
|
2342
|
+
}
|
|
2343
|
+
function collectGeoWidgetRegistrationsFromWidgets(widgets, namespace) {
|
|
2344
|
+
return widgets
|
|
2345
|
+
.filter((widget) => widget['widget-geo-config'] && typeof widget['widget-data-path'] === 'string')
|
|
2346
|
+
.map((widget) => {
|
|
2347
|
+
const originalWidgetId = widget['widget-id'];
|
|
2348
|
+
const originalDataPath = widget['widget-data-path'];
|
|
2349
|
+
const geoConfig = widget['widget-geo-config'];
|
|
2350
|
+
const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
|
|
2351
|
+
const parentWidgetId = geoConfig.parentWidgetId?.trim()
|
|
2352
|
+
? (namespace ? `${namespace}__${geoConfig.parentWidgetId}` : geoConfig.parentWidgetId)
|
|
2353
|
+
: null;
|
|
2354
|
+
const dataPath = namespace ? `${namespace}.${originalDataPath}` : originalDataPath;
|
|
2355
|
+
return {
|
|
2356
|
+
widgetId,
|
|
2357
|
+
parentWidgetId,
|
|
2358
|
+
level: geoConfig.level,
|
|
2359
|
+
geoConfig,
|
|
2360
|
+
dataPath,
|
|
2361
|
+
groupId: getGeoGroupId(dataPath),
|
|
2362
|
+
};
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
/** Reconcile all geo groups in section values before save. */
|
|
2366
|
+
function reconcileGeoHierarchiesInValues(values, registrations, dataSources = {}) {
|
|
2367
|
+
const groupIds = [...new Set(registrations.map((entry) => entry.groupId))];
|
|
2368
|
+
let updatedValues = values;
|
|
2369
|
+
const resolveMnemonic = createGeoLevelMnemonicResolver(updatedValues, dataSources);
|
|
2370
|
+
for (const groupId of groupIds) {
|
|
2371
|
+
const groupRegistrations = registrations.filter((entry) => entry.groupId === groupId);
|
|
2372
|
+
const dataPath = groupRegistrations[0]?.dataPath;
|
|
2373
|
+
const widgetId = groupRegistrations[0]?.widgetId;
|
|
2374
|
+
if (!dataPath || !widgetId) {
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
rebuildGeoHierarchyFromRegistrations(groupId, updatedValues, groupRegistrations, resolveMnemonic);
|
|
2378
|
+
updatedValues = applySharedGeoHierarchyToValues(updatedValues, groupId, dataPath, widgetId);
|
|
2379
|
+
}
|
|
2380
|
+
return updatedValues;
|
|
2381
|
+
}
|
|
2382
|
+
function getGeoWidgetRegistrationsInGroup(groupId) {
|
|
2383
|
+
return orderGeoWidgetRegistrations([...geoWidgetConfigRegistry.values()].filter((entry) => entry.groupId === groupId));
|
|
2384
|
+
}
|
|
2124
2385
|
/** True when changedWidgetId is any upstream geo parent of widgetId (not only immediate parent). */
|
|
2125
2386
|
function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetId) {
|
|
2126
2387
|
if (changedWidgetId === widgetId) {
|
|
@@ -2135,6 +2396,42 @@ function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetI
|
|
|
2135
2396
|
}
|
|
2136
2397
|
return false;
|
|
2137
2398
|
}
|
|
2399
|
+
/** Group id for geo widgets sharing the same register prefix (e.g. `{registerId}`). */
|
|
2400
|
+
function getGeoGroupId(dataPath) {
|
|
2401
|
+
if (typeof dataPath === 'string' && dataPath.includes('.')) {
|
|
2402
|
+
return dataPath.split('.').slice(0, -1).join('.');
|
|
2403
|
+
}
|
|
2404
|
+
return 'default';
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Resolve the human-readable label for a geo level from persisted hierarchy JSON.
|
|
2408
|
+
* Used in readonly mode when API options are not loaded.
|
|
2409
|
+
*/
|
|
2410
|
+
function resolveGeoWidgetLevelLabel(values, widgetId, dataPath, geoConfig) {
|
|
2411
|
+
if (!dataPath || typeof dataPath !== 'string') {
|
|
2412
|
+
return undefined;
|
|
2413
|
+
}
|
|
2414
|
+
const stored = getWidgetValue(values, dataPath, widgetId);
|
|
2415
|
+
const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
|
|
2416
|
+
if (!Array.isArray(hierarchy)) {
|
|
2417
|
+
return undefined;
|
|
2418
|
+
}
|
|
2419
|
+
const levelData = hierarchy.find((l) => l.level === geoConfig.level);
|
|
2420
|
+
if (levelData?.level_value_mnemonic) {
|
|
2421
|
+
return String(levelData.level_value_mnemonic);
|
|
2422
|
+
}
|
|
2423
|
+
return undefined;
|
|
2424
|
+
}
|
|
2425
|
+
/** All registered geo widgets that are descendants of ancestorWidgetId. */
|
|
2426
|
+
function getGeoDescendantWidgetIds(ancestorWidgetId) {
|
|
2427
|
+
const descendants = [];
|
|
2428
|
+
for (const [childId, parentId] of geoWidgetParentRegistry.entries()) {
|
|
2429
|
+
if (isUpstreamGeoAncestor(ancestorWidgetId, childId, parentId)) {
|
|
2430
|
+
descendants.push(childId);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return descendants;
|
|
2434
|
+
}
|
|
2138
2435
|
function readStoredHierarchyLevels(values, dataPath, widgetId) {
|
|
2139
2436
|
const stored = getWidgetValue(values, dataPath, widgetId);
|
|
2140
2437
|
const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
|
|
@@ -2179,12 +2476,13 @@ function resetAndSeedGeoHierarchyFromValues(values, dataPath, widgetId, groupId)
|
|
|
2179
2476
|
|
|
2180
2477
|
// Define stable empty arrays to avoid selector reference issues
|
|
2181
2478
|
const EMPTY_ERRORS = [];
|
|
2182
|
-
const EMPTY_DATA_SOURCE
|
|
2479
|
+
const EMPTY_DATA_SOURCE = [];
|
|
2183
2480
|
const useBaseWidget = (options) => {
|
|
2184
2481
|
const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
|
|
2185
2482
|
const dispatch = useDispatch();
|
|
2186
2483
|
const context = useWidgetContext();
|
|
2187
2484
|
const eventBus = useWidgetEventBus();
|
|
2485
|
+
const { translateConfig } = useWidgetTranslation();
|
|
2188
2486
|
const widgetId = config['widget-id'];
|
|
2189
2487
|
// Fall back to WidgetContext for dataSourceRequestHandler
|
|
2190
2488
|
const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
|
|
@@ -2193,7 +2491,7 @@ const useBaseWidget = (options) => {
|
|
|
2193
2491
|
const errors = useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
|
|
2194
2492
|
const touched = useSelector((state) => state.widget.touched[widgetId] || false);
|
|
2195
2493
|
const loading = useSelector((state) => state.widget.loading[widgetId] || false);
|
|
2196
|
-
const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE
|
|
2494
|
+
const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
|
|
2197
2495
|
// Skip value handling for layout widgets (they don't store data values)
|
|
2198
2496
|
// Infer layout from widget-type
|
|
2199
2497
|
const isLayoutWidget = config['widget-type'] === 'layout';
|
|
@@ -2355,6 +2653,15 @@ const useBaseWidget = (options) => {
|
|
|
2355
2653
|
}
|
|
2356
2654
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2357
2655
|
}, [isLayoutWidget]); // Only run once on mount
|
|
2656
|
+
const resolveIsRequired = useCallback((currentValues) => {
|
|
2657
|
+
if (isLayoutWidget) {
|
|
2658
|
+
return false;
|
|
2659
|
+
}
|
|
2660
|
+
if (config['widget-readonly']) {
|
|
2661
|
+
return false;
|
|
2662
|
+
}
|
|
2663
|
+
return evaluateWidgetConditions(config['widget-data-options'], currentValues, config['widget-required'] ?? false).required;
|
|
2664
|
+
}, [config, isLayoutWidget]);
|
|
2358
2665
|
// Handle value change
|
|
2359
2666
|
// CRITICAL: Don't include 'values' in dependency array - it causes the callback to be recreated
|
|
2360
2667
|
// every time values change, which can lead to stale closures and double dispatches
|
|
@@ -2371,13 +2678,16 @@ const useBaseWidget = (options) => {
|
|
|
2371
2678
|
// This prevents data disappearance when switching to Edit mode and components
|
|
2372
2679
|
// incorrectly clear values before options load or if handler is temporarily missing.
|
|
2373
2680
|
if (newValue === '' || newValue === null || newValue === undefined) {
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2681
|
+
const allowEmptyClear = config.widget === 'register-lookup';
|
|
2682
|
+
if (!allowEmptyClear) {
|
|
2683
|
+
if (loadingRef.current) {
|
|
2684
|
+
console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
if (config['widget-data-source']?.type === 'api' && dataSourceOptionsRef.current.length === 0) {
|
|
2688
|
+
console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because API options are empty`);
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2381
2691
|
}
|
|
2382
2692
|
}
|
|
2383
2693
|
// Mark that user has set a value (unless this is the default initialization)
|
|
@@ -2395,29 +2705,26 @@ const useBaseWidget = (options) => {
|
|
|
2395
2705
|
lastDispatchedValueRef.current = newValue;
|
|
2396
2706
|
dispatch(setValue({ widgetId, value: newValue }));
|
|
2397
2707
|
}
|
|
2708
|
+
else if (config['widget-geo-config']) {
|
|
2709
|
+
// Geo widgets: hierarchy dataPath is managed by useGeoWidgetCascade
|
|
2710
|
+
getGeoDescendantWidgetIds(widgetId).forEach((descendantId) => {
|
|
2711
|
+
dispatch(setValue({ widgetId: descendantId, value: GEO_LEVEL_CLEARED }));
|
|
2712
|
+
dispatch(setDataSource({ widgetId: descendantId, data: [] }));
|
|
2713
|
+
});
|
|
2714
|
+
dispatch(setValue({ widgetId, value: newValue }));
|
|
2715
|
+
}
|
|
2398
2716
|
else {
|
|
2399
|
-
//
|
|
2400
|
-
// CRITICAL: For geo widgets, we do NOT want to overwrite the shared hierarchy dataPath
|
|
2401
|
-
// with a primitive value (the selected ID). The hierarchy object is managed by useGeoWidgetCascade.
|
|
2402
|
-
if (config['widget-geo-config']) {
|
|
2403
|
-
dispatch(setValue({ widgetId, value: newValue }));
|
|
2404
|
-
return;
|
|
2405
|
-
}
|
|
2406
|
-
// For non-geo widgets, update both widgetId and dataPath
|
|
2407
|
-
// CRITICAL: Create updated values object with newValue already set
|
|
2408
|
-
// This prevents setWidgetValue from reading stale values
|
|
2717
|
+
// Non-geo widgets: update both widgetId and dataPath
|
|
2409
2718
|
const currentValuesWithUpdate = {
|
|
2410
2719
|
...valuesRef.current,
|
|
2411
|
-
[widgetId]: newValue,
|
|
2720
|
+
[widgetId]: newValue,
|
|
2412
2721
|
};
|
|
2413
2722
|
const updatedValues = setWidgetValue(currentValuesWithUpdate, config['widget-data-path'], widgetId, newValue);
|
|
2414
|
-
// setWidgetValue returns the complete updated structure with all existing data preserved
|
|
2415
|
-
// Use setValues to update the entire state with deep merge
|
|
2416
2723
|
dispatch(setValues(updatedValues));
|
|
2417
2724
|
}
|
|
2418
2725
|
// Validate if needed
|
|
2419
2726
|
if (validate) {
|
|
2420
|
-
const validationErrors = validateWidget(newValue, config['widget-data-validation'],
|
|
2727
|
+
const validationErrors = validateWidget(newValue, config['widget-data-validation'], resolveIsRequired(currentValues));
|
|
2421
2728
|
dispatch(setError({ widgetId, errors: validationErrors }));
|
|
2422
2729
|
}
|
|
2423
2730
|
// Call custom onChange if provided
|
|
@@ -2436,13 +2743,12 @@ const useBaseWidget = (options) => {
|
|
|
2436
2743
|
timestamp: Date.now(),
|
|
2437
2744
|
});
|
|
2438
2745
|
}
|
|
2439
|
-
}, [config, widgetId, dispatch, onValueChange, eventBus] // Removed 'values' to prevent stale closures
|
|
2746
|
+
}, [config, widgetId, dispatch, onValueChange, eventBus, resolveIsRequired] // Removed 'values' to prevent stale closures
|
|
2440
2747
|
);
|
|
2441
2748
|
// Handle blur
|
|
2442
2749
|
const handleBlur = useCallback(() => {
|
|
2443
2750
|
dispatch(setTouched({ widgetId, touched: true }));
|
|
2444
|
-
|
|
2445
|
-
const validationErrors = validateWidget(currentValue, config['widget-data-validation'], config['widget-required']);
|
|
2751
|
+
const validationErrors = validateWidget(currentValue, config['widget-data-validation'], resolveIsRequired(valuesRef.current));
|
|
2446
2752
|
dispatch(setError({ widgetId, errors: validationErrors }));
|
|
2447
2753
|
// Publish widget:blur event
|
|
2448
2754
|
if (eventBus) {
|
|
@@ -2453,7 +2759,7 @@ const useBaseWidget = (options) => {
|
|
|
2453
2759
|
timestamp: Date.now(),
|
|
2454
2760
|
});
|
|
2455
2761
|
}
|
|
2456
|
-
}, [currentValue, config, widgetId, dispatch, eventBus]);
|
|
2762
|
+
}, [currentValue, config, widgetId, dispatch, eventBus, resolveIsRequired]);
|
|
2457
2763
|
// Get field value helper
|
|
2458
2764
|
const getFieldValue = useCallback((path) => {
|
|
2459
2765
|
return getWidgetValue(values, path, '');
|
|
@@ -2461,7 +2767,7 @@ const useBaseWidget = (options) => {
|
|
|
2461
2767
|
// Conditional visibility and enablement
|
|
2462
2768
|
const isVisible = useMemo(() => {
|
|
2463
2769
|
// Layout widgets are always visible unless explicitly hidden
|
|
2464
|
-
if (isLayoutWidget && !config['widget-data-options']
|
|
2770
|
+
if (isLayoutWidget && !hasVisibilityRules(config['widget-data-options'])) {
|
|
2465
2771
|
return true;
|
|
2466
2772
|
}
|
|
2467
2773
|
return shouldShowWidget(config['widget-data-options'], values);
|
|
@@ -2476,6 +2782,7 @@ const useBaseWidget = (options) => {
|
|
|
2476
2782
|
}
|
|
2477
2783
|
return shouldEnableWidget(config['widget-data-options'], values);
|
|
2478
2784
|
}, [config['widget-readonly'], config['widget-data-options'], values, isLayoutWidget]);
|
|
2785
|
+
const isRequired = useMemo(() => resolveIsRequired(values), [resolveIsRequired, values]);
|
|
2479
2786
|
// Format value for display
|
|
2480
2787
|
const formattedValue = useMemo(() => {
|
|
2481
2788
|
if (!config['widget-data-format']) {
|
|
@@ -2506,6 +2813,8 @@ const useBaseWidget = (options) => {
|
|
|
2506
2813
|
const apiService = dataSource?.type === 'api' ? dataSource.service : '';
|
|
2507
2814
|
const apiEndpoint = dataSource?.type === 'api' ? dataSource.endpoint : '';
|
|
2508
2815
|
const configKey = `${widgetId}-${isReadonly}-${dataSource?.type || 'none'}-${apiService}-${apiEndpoint}`;
|
|
2816
|
+
// Stable key so inline schemaData objects (e.g. dialog-table fields) don't retrigger loads every render
|
|
2817
|
+
const schemaDataKey = useMemo(() => (schemaData ? JSON.stringify(schemaData) : ''), [schemaData]);
|
|
2509
2818
|
// Extract dependency value using a granular selector to prevent unnecessary re-renders
|
|
2510
2819
|
// and infinite loops when other unrelated values in the state change.
|
|
2511
2820
|
const dependencyValue = useSelector((state) => {
|
|
@@ -2522,10 +2831,9 @@ const useBaseWidget = (options) => {
|
|
|
2522
2831
|
if (!dataSource) {
|
|
2523
2832
|
return;
|
|
2524
2833
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
if (dataSource.type === 'api' && isReadonly) {
|
|
2834
|
+
const loadApiInReadonly = !!geoConfig ||
|
|
2835
|
+
['select', 'radio', 'checkbox', 'multi-select'].includes(config.widget);
|
|
2836
|
+
if (dataSource.type === 'api' && isReadonly && !loadApiInReadonly) {
|
|
2529
2837
|
return;
|
|
2530
2838
|
}
|
|
2531
2839
|
// For widgets with dependencies, check if dependency value exists
|
|
@@ -2567,6 +2875,30 @@ const useBaseWidget = (options) => {
|
|
|
2567
2875
|
// React will call this effect again when the handler is ready
|
|
2568
2876
|
return;
|
|
2569
2877
|
}
|
|
2878
|
+
const resolveOptionKeys = () => {
|
|
2879
|
+
if (dataSource.type === 'static') {
|
|
2880
|
+
return { valueKey: undefined, labelKey: undefined };
|
|
2881
|
+
}
|
|
2882
|
+
if (geoConfig) {
|
|
2883
|
+
return {
|
|
2884
|
+
valueKey: dataSource.valueKey || 'level_value_id',
|
|
2885
|
+
labelKey: dataSource.labelKey || 'level_value_mnemonic',
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
return { valueKey: dataSource.valueKey, labelKey: dataSource.labelKey };
|
|
2889
|
+
};
|
|
2890
|
+
if (dataSource.type === 'api') {
|
|
2891
|
+
const levelId = geoConfig?.level;
|
|
2892
|
+
const cached = getCachedApiDataSource(dataSource, valuesRef.current, levelId);
|
|
2893
|
+
if (cached) {
|
|
2894
|
+
const { valueKey, labelKey } = resolveOptionKeys();
|
|
2895
|
+
dispatch(setDataSource({
|
|
2896
|
+
widgetId,
|
|
2897
|
+
data: transformDataSourceOptions(cached, valueKey, labelKey),
|
|
2898
|
+
}));
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2570
2902
|
dispatch(setLoading({ widgetId, loading: true }));
|
|
2571
2903
|
let data = [];
|
|
2572
2904
|
if (dataSource.type === 'static') {
|
|
@@ -2579,31 +2911,13 @@ const useBaseWidget = (options) => {
|
|
|
2579
2911
|
dispatch(setDataSource({ widgetId, data: [] }));
|
|
2580
2912
|
return;
|
|
2581
2913
|
}
|
|
2582
|
-
// Extract level_id from widget-geo-config.level if available
|
|
2583
2914
|
const levelId = geoConfig?.level;
|
|
2584
2915
|
data = await getApiDataSource(dataSource, valuesRef.current, currentHandler, levelId);
|
|
2585
2916
|
}
|
|
2586
2917
|
else if (dataSource.type === 'schema') {
|
|
2587
2918
|
data = getSchemaDataSource(dataSource, schemaData || {});
|
|
2588
2919
|
}
|
|
2589
|
-
|
|
2590
|
-
// For geo widgets, default to level_value_id and level_value_mnemonic
|
|
2591
|
-
let valueKey;
|
|
2592
|
-
let labelKey;
|
|
2593
|
-
if (dataSource.type === 'static') {
|
|
2594
|
-
valueKey = undefined;
|
|
2595
|
-
labelKey = undefined;
|
|
2596
|
-
}
|
|
2597
|
-
else if (geoConfig) {
|
|
2598
|
-
// Geo widgets: default to level_value_id and level_value_mnemonic
|
|
2599
|
-
valueKey = dataSource.valueKey || 'level_value_id';
|
|
2600
|
-
labelKey = dataSource.labelKey || 'level_value_mnemonic';
|
|
2601
|
-
}
|
|
2602
|
-
else {
|
|
2603
|
-
// Non-geo widgets: use specified keys or undefined
|
|
2604
|
-
valueKey = dataSource.valueKey;
|
|
2605
|
-
labelKey = dataSource.labelKey;
|
|
2606
|
-
}
|
|
2920
|
+
const { valueKey, labelKey } = resolveOptionKeys();
|
|
2607
2921
|
const transformed = transformDataSourceOptions(data, valueKey, labelKey);
|
|
2608
2922
|
dispatch(setDataSource({ widgetId, data: transformed }));
|
|
2609
2923
|
}
|
|
@@ -2618,16 +2932,25 @@ const useBaseWidget = (options) => {
|
|
|
2618
2932
|
loadDataSource();
|
|
2619
2933
|
// Use configKey and dependencyValue to ensure effect runs only when relevant state changes
|
|
2620
2934
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2621
|
-
}, [configKey, dependencyValue, dataSourceRequestHandler,
|
|
2935
|
+
}, [configKey, dependencyValue, dataSourceRequestHandler, schemaDataKey, widgetId, dispatch]);
|
|
2936
|
+
const geoDisplayLabel = useMemo(() => {
|
|
2937
|
+
if (!geoConfig) {
|
|
2938
|
+
return undefined;
|
|
2939
|
+
}
|
|
2940
|
+
const rawLabel = resolveGeoWidgetLevelLabel(values, widgetId, config['widget-data-path'], geoConfig);
|
|
2941
|
+
return rawLabel ? translateConfig(rawLabel) : undefined;
|
|
2942
|
+
}, [values, widgetId, config, geoConfig, translateConfig]);
|
|
2622
2943
|
return {
|
|
2623
2944
|
widgetId,
|
|
2624
2945
|
value: currentValue,
|
|
2946
|
+
geoDisplayLabel,
|
|
2625
2947
|
formattedValue,
|
|
2626
2948
|
error: errors,
|
|
2627
2949
|
touched,
|
|
2628
2950
|
loading,
|
|
2629
2951
|
isVisible,
|
|
2630
2952
|
isEnabled,
|
|
2953
|
+
isRequired,
|
|
2631
2954
|
onChange: handleChange,
|
|
2632
2955
|
onBlur: handleBlur,
|
|
2633
2956
|
setError: (errors) => dispatch(setError({ widgetId, errors })),
|
|
@@ -2698,8 +3021,6 @@ const useWidgetCascade = (options) => {
|
|
|
2698
3021
|
}, [cascadeConfig, eventBus, dataSource, widgetId, dispatch]);
|
|
2699
3022
|
};
|
|
2700
3023
|
|
|
2701
|
-
// Define stable empty array to avoid selector reference issues
|
|
2702
|
-
const EMPTY_DATA_SOURCE = [];
|
|
2703
3024
|
/**
|
|
2704
3025
|
* Hook for geo widget cascade functionality
|
|
2705
3026
|
* Handles geo hierarchy building and cascade behavior
|
|
@@ -2718,6 +3039,7 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2718
3039
|
const valuesRef = useRef(values);
|
|
2719
3040
|
const handlerRef = useRef(dataSourceRequestHandler);
|
|
2720
3041
|
const lastCascadePublishRef = useRef(undefined);
|
|
3042
|
+
const lastDirectParentValueRef = useRef(undefined);
|
|
2721
3043
|
// Keep refs updated
|
|
2722
3044
|
useEffect(() => {
|
|
2723
3045
|
valuesRef.current = values;
|
|
@@ -2728,15 +3050,15 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2728
3050
|
? resolveGeoWidgetLevelValue(state.widget.values, widgetId, dataPath, geoConfig)
|
|
2729
3051
|
: state.widget.values[widgetId]);
|
|
2730
3052
|
// Memoize selector to avoid returning new array reference
|
|
2731
|
-
const
|
|
3053
|
+
const allDataSources = useSelector((state) => state.widget.dataSources);
|
|
2732
3054
|
// Register parent chain for upstream-ancestor detection (grandparent → grandchild reset)
|
|
2733
3055
|
useEffect(() => {
|
|
2734
|
-
if (!geoConfig) {
|
|
3056
|
+
if (!geoConfig || typeof dataPath !== 'string') {
|
|
2735
3057
|
return;
|
|
2736
3058
|
}
|
|
2737
|
-
|
|
2738
|
-
return () =>
|
|
2739
|
-
}, [widgetId, geoConfig]);
|
|
3059
|
+
registerGeoWidget(widgetId, geoConfig, dataPath);
|
|
3060
|
+
return () => unregisterGeoWidget(widgetId);
|
|
3061
|
+
}, [widgetId, geoConfig, dataPath]);
|
|
2740
3062
|
// Keep in-memory builder in sync with persisted hierarchy (reload, cancel→re-edit, etc.)
|
|
2741
3063
|
useEffect(() => {
|
|
2742
3064
|
if (!geoConfig || typeof dataPath !== 'string') {
|
|
@@ -2787,6 +3109,13 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2787
3109
|
event.value === null ||
|
|
2788
3110
|
event.value === '' ||
|
|
2789
3111
|
event.value === GEO_LEVEL_CLEARED;
|
|
3112
|
+
const isFirstParentEvent = lastDirectParentValueRef.current === undefined;
|
|
3113
|
+
const parentValueChanged = !isFirstParentEvent &&
|
|
3114
|
+
lastDirectParentValueRef.current !== event.value;
|
|
3115
|
+
lastDirectParentValueRef.current = event.value;
|
|
3116
|
+
if (!parentCleared && !parentValueChanged && !isFirstParentEvent) {
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
2790
3119
|
let parentValue = event.value;
|
|
2791
3120
|
if (!parentCleared && (parentValue === undefined || parentValue === null)) {
|
|
2792
3121
|
parentValue = currentValues[parentWidgetId];
|
|
@@ -2821,18 +3150,20 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2821
3150
|
}, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch, groupId]);
|
|
2822
3151
|
// Handle value changes to build hierarchy
|
|
2823
3152
|
useEffect(() => {
|
|
2824
|
-
if (!geoConfig) {
|
|
3153
|
+
if (!geoConfig || typeof dataPath !== 'string') {
|
|
2825
3154
|
return;
|
|
2826
3155
|
}
|
|
2827
|
-
|
|
3156
|
+
const { level, isLastLevel } = geoConfig;
|
|
3157
|
+
const groupRegistrations = getGeoWidgetRegistrationsInGroup(groupId);
|
|
3158
|
+
const applyGroupRebuild = () => {
|
|
3159
|
+
const resolveMnemonic = createGeoLevelMnemonicResolver(valuesRef.current, allDataSources);
|
|
3160
|
+
rebuildGeoHierarchyFromRegistrations(groupId, valuesRef.current, groupRegistrations, resolveMnemonic);
|
|
3161
|
+
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
3162
|
+
};
|
|
2828
3163
|
// ONLY clear hierarchy if the value is explicitly null or empty string (user action)
|
|
2829
3164
|
if (currentValue === null || currentValue === '') {
|
|
2830
|
-
const { level, isLastLevel } = geoConfig;
|
|
2831
3165
|
geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
|
|
2832
|
-
|
|
2833
|
-
if (dataPath) {
|
|
2834
|
-
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
2835
|
-
}
|
|
3166
|
+
applyGroupRebuild();
|
|
2836
3167
|
if (!isLastLevel && eventBus && lastCascadePublishRef.current !== GEO_LEVEL_CLEARED) {
|
|
2837
3168
|
lastCascadePublishRef.current = GEO_LEVEL_CLEARED;
|
|
2838
3169
|
eventBus.publish({
|
|
@@ -2845,66 +3176,13 @@ const useGeoWidgetCascade = (options) => {
|
|
|
2845
3176
|
return;
|
|
2846
3177
|
}
|
|
2847
3178
|
if (currentValue === undefined) {
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
// Check if hierarchy is already built to prevent endless loops
|
|
2852
|
-
if (dataPath) {
|
|
2853
|
-
const currentHierarchy = getWidgetValue(valuesRef.current, dataPath, widgetId);
|
|
2854
|
-
// If hierarchy JSON is already set and matches current value, skip rebuilding
|
|
2855
|
-
if (currentHierarchy && typeof currentHierarchy === 'object') {
|
|
2856
|
-
// Check if this specific level's value matches the hierarchy
|
|
2857
|
-
const hierarchyArray = currentHierarchy.hierarchy || currentHierarchy.geo_code_hierarchy_json?.hierarchy;
|
|
2858
|
-
if (Array.isArray(hierarchyArray)) {
|
|
2859
|
-
const currentLevelValue = typeof currentValue === 'object'
|
|
2860
|
-
? (currentValue.level_value_id || currentValue.id || currentValue.value)
|
|
2861
|
-
: currentValue;
|
|
2862
|
-
const levelData = hierarchyArray.find((l) => l.level === geoConfig.level);
|
|
2863
|
-
// If this level is already correctly represented in the hierarchy, skip rebuilding
|
|
2864
|
-
// String conversion ensures comparison works for mixed types
|
|
2865
|
-
if (levelData && String(levelData.level_value_id) === String(currentLevelValue)) {
|
|
2866
|
-
return;
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
3179
|
+
const hasOwnValue = Object.prototype.hasOwnProperty.call(valuesRef.current, widgetId);
|
|
3180
|
+
if (!hasOwnValue) {
|
|
3181
|
+
return;
|
|
2869
3182
|
}
|
|
2870
3183
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
let level_value_id;
|
|
2874
|
-
let level_value_mnemonic;
|
|
2875
|
-
if (typeof currentValue === 'string' || typeof currentValue === 'number') {
|
|
2876
|
-
// Value is just the ID, need to find mnemonic from data source
|
|
2877
|
-
level_value_id = String(currentValue);
|
|
2878
|
-
// Try to get mnemonic from data source options
|
|
2879
|
-
const option = dataSourceOptions.find((opt) => opt.value === currentValue);
|
|
2880
|
-
level_value_mnemonic = option?.label || String(currentValue);
|
|
2881
|
-
}
|
|
2882
|
-
else if (currentValue && typeof currentValue === 'object') {
|
|
2883
|
-
level_value_id = currentValue.level_value_id || currentValue.id || currentValue.value;
|
|
2884
|
-
level_value_mnemonic = currentValue.level_value_mnemonic || currentValue.name || currentValue.label;
|
|
2885
|
-
}
|
|
2886
|
-
else {
|
|
2887
|
-
return;
|
|
2888
|
-
}
|
|
2889
|
-
// When a widget's own value changes, remove this level and all below from hierarchy first
|
|
2890
|
-
geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
|
|
2891
|
-
// Add level to hierarchy
|
|
2892
|
-
geoHierarchyBuilder.addLevel(level, level_value_id, level_value_mnemonic, groupId);
|
|
2893
|
-
// Build and store hierarchy JSON on every change
|
|
2894
|
-
if (dataPath && geoHierarchyBuilder.buildHierarchyJson(groupId)) {
|
|
2895
|
-
dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
|
|
2896
|
-
}
|
|
2897
|
-
// Notify descendants when this level changes via hierarchy/rehydration (handleChange may not run).
|
|
2898
|
-
if (!isLastLevel && eventBus && lastCascadePublishRef.current !== level_value_id) {
|
|
2899
|
-
lastCascadePublishRef.current = level_value_id;
|
|
2900
|
-
eventBus.publish({
|
|
2901
|
-
type: 'widget:change',
|
|
2902
|
-
widgetId,
|
|
2903
|
-
value: level_value_id,
|
|
2904
|
-
timestamp: Date.now(),
|
|
2905
|
-
});
|
|
2906
|
-
}
|
|
2907
|
-
}, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions, eventBus, groupId]);
|
|
3184
|
+
applyGroupRebuild();
|
|
3185
|
+
}, [geoConfig, currentValue, widgetId, dataPath, dispatch, allDataSources, groupId, eventBus]);
|
|
2908
3186
|
};
|
|
2909
3187
|
|
|
2910
3188
|
class WidgetRegistry {
|
|
@@ -3029,98 +3307,6 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
|
|
|
3029
3307
|
return (jsxRuntimeExports.jsx("div", { className: "widget-container", "data-widget-id": widgetContext.widgetId, style: { marginBottom: 0 }, children: widgetRegistry.render(config, widgetContext, defaultComponent) }));
|
|
3030
3308
|
};
|
|
3031
3309
|
|
|
3032
|
-
/**
|
|
3033
|
-
* Custom hook for widget translations
|
|
3034
|
-
* Provides translation function with widget-specific namespace and fallback support
|
|
3035
|
-
*/
|
|
3036
|
-
const useWidgetTranslation = () => {
|
|
3037
|
-
const { translate: translateFunction } = useWidgetContext();
|
|
3038
|
-
/**
|
|
3039
|
-
* Translate a key with flexible namespace support
|
|
3040
|
-
* Supports translation keys in various formats and direct strings
|
|
3041
|
-
*
|
|
3042
|
-
* Translation key formats supported:
|
|
3043
|
-
* - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
|
|
3044
|
-
* - "Name" - Direct string (will be looked up in flat translation structure)
|
|
3045
|
-
* - "sections.personalDetails" - Nested key (for backward compatibility)
|
|
3046
|
-
*
|
|
3047
|
-
* With flat translation structure, direct strings like "Name" are automatically
|
|
3048
|
-
* translated by looking them up in the translation resources.
|
|
3049
|
-
*
|
|
3050
|
-
* @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
|
|
3051
|
-
* @param options - Translation options (interpolation values, default value, etc.)
|
|
3052
|
-
* @returns Translated string or original string if translation not found
|
|
3053
|
-
*/
|
|
3054
|
-
const translate = (keyOrString, options) => {
|
|
3055
|
-
if (!keyOrString) {
|
|
3056
|
-
return options?.defaultValue || '';
|
|
3057
|
-
}
|
|
3058
|
-
// Use the provided translation function or fallback to the key
|
|
3059
|
-
if (translateFunction) {
|
|
3060
|
-
return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
|
|
3061
|
-
}
|
|
3062
|
-
// Fallback to key if no translation function available
|
|
3063
|
-
return options?.defaultValue || keyOrString;
|
|
3064
|
-
};
|
|
3065
|
-
/**
|
|
3066
|
-
* Translate widget config property
|
|
3067
|
-
* Attempts to translate the value, but if translation is not found,
|
|
3068
|
-
* returns the original value as-is (graceful fallback)
|
|
3069
|
-
*
|
|
3070
|
-
* This function will:
|
|
3071
|
-
* - Try to translate any string value
|
|
3072
|
-
* - If translation exists, use the translated value
|
|
3073
|
-
* - If translation doesn't exist (returns same value or throws), use original value
|
|
3074
|
-
* - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
|
|
3075
|
-
*/
|
|
3076
|
-
const translateConfig = (value, fallback) => {
|
|
3077
|
-
if (!value) {
|
|
3078
|
-
return fallback || '';
|
|
3079
|
-
}
|
|
3080
|
-
// Try to translate the value
|
|
3081
|
-
if (translateFunction) {
|
|
3082
|
-
try {
|
|
3083
|
-
// Pass defaultValue to ensure we get the original value if translation fails
|
|
3084
|
-
const translated = translateFunction(value, { defaultValue: value });
|
|
3085
|
-
// If translation returns empty, null, undefined, or the exact same value,
|
|
3086
|
-
// it means no translation was found - return the original value
|
|
3087
|
-
if (!translated || translated === value) {
|
|
3088
|
-
return value;
|
|
3089
|
-
}
|
|
3090
|
-
// Translation found, return it
|
|
3091
|
-
return translated;
|
|
3092
|
-
}
|
|
3093
|
-
catch (error) {
|
|
3094
|
-
// If translation throws an error (e.g., missing key warning), return original value
|
|
3095
|
-
return value;
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
// No translation function available, return value as-is
|
|
3099
|
-
return value;
|
|
3100
|
-
};
|
|
3101
|
-
// No need of this getLanguage and changeLanguage functions
|
|
3102
|
-
/**
|
|
3103
|
-
* Get current language
|
|
3104
|
-
*/
|
|
3105
|
-
// const getLanguage = (): string => {
|
|
3106
|
-
// return i18n.language || 'en';
|
|
3107
|
-
// };
|
|
3108
|
-
/**
|
|
3109
|
-
* Change language
|
|
3110
|
-
*/
|
|
3111
|
-
// const changeLanguage = (lng: string): Promise<void> => {
|
|
3112
|
-
// return i18n.changeLanguage(lng).then(() => undefined);
|
|
3113
|
-
// };
|
|
3114
|
-
return {
|
|
3115
|
-
t: translate,
|
|
3116
|
-
translate,
|
|
3117
|
-
translateConfig,
|
|
3118
|
-
// getLanguage,
|
|
3119
|
-
// changeLanguage,
|
|
3120
|
-
// i18n: null,
|
|
3121
|
-
};
|
|
3122
|
-
};
|
|
3123
|
-
|
|
3124
3310
|
/**
|
|
3125
3311
|
* Renders a panel with its nested panels or widgets
|
|
3126
3312
|
*
|
|
@@ -3239,6 +3425,16 @@ const PanelRenderer = ({ panel, dataSourceRequestHandler, schemaData, onValueCha
|
|
|
3239
3425
|
return (jsxRuntimeExports.jsx("div", { className: `panel panel-${orientation}`, "data-panel-id": panel['panel-id'], style: orientation === 'horizontal' ? { width: '100%' } : {}, children: content }));
|
|
3240
3426
|
};
|
|
3241
3427
|
|
|
3428
|
+
/**
|
|
3429
|
+
* Field label: long text truncates with ellipsis; required asterisk always stays visible.
|
|
3430
|
+
*/
|
|
3431
|
+
const WidgetFieldLabel = ({ label, required = false, className = '', title, }) => {
|
|
3432
|
+
const { translateConfig } = useWidgetTranslation();
|
|
3433
|
+
const translatedLabel = translateConfig(label);
|
|
3434
|
+
const tooltip = title !== undefined ? translateConfig(title) : translatedLabel;
|
|
3435
|
+
return (jsxRuntimeExports.jsxs("label", { className: `flex items-baseline min-w-0 max-w-full ${className}`, style: { fontFamily: 'Roboto, sans-serif' }, title: tooltip, children: [jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: translatedLabel }), required && jsxRuntimeExports.jsx("span", { className: "ml-1 shrink-0 text-red-500", children: "*" })] }));
|
|
3436
|
+
};
|
|
3437
|
+
|
|
3242
3438
|
/**
|
|
3243
3439
|
* Utility functions for file preview functionality
|
|
3244
3440
|
*/
|
|
@@ -3282,7 +3478,11 @@ const canPreviewInWeb = (file) => {
|
|
|
3282
3478
|
return previewableExtensions.includes(extension.toLowerCase());
|
|
3283
3479
|
};
|
|
3284
3480
|
|
|
3285
|
-
var img$f = "data:image/png;base64,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";
|
|
3481
|
+
var img$h = "data:image/png;base64,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";
|
|
3482
|
+
|
|
3483
|
+
var img$g = "data:image/png;base64,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";
|
|
3484
|
+
|
|
3485
|
+
var img$f = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAACAKADAAQAAAABAAACAAAAAAAL+LWFAAAkvUlEQVR4Ae3d344sVb0H8I1GuMFofAD5p95443ucGwHJifgg8g7wDpoYJecCvOGEIHAFr2ACMeobqES4OCGBsxZ7L2emd/f8urtqVa0/n04qvWemu2rVp35rfX/Tu2fmwQM3AgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBA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AfCGPyVNIBYYqQnIa5cbAQILBF5Kz+29AfCd/4IC8NTpBEZpAl6c7so5YQIrC+T3aXyStl6bAN/5r1wQdjeFQO9NQF6zvMdsilJ1krUFcifdYwPgO//alWH/Iwv03AT8fOQL49wIbC3wbjpgT02A8N+6QhxvRIEem4D3R7wQzonAngLPp4Pnvw/QQxPgZf89K8WxRxPoqQnIa1Req9wIEFhZ4JW0v9YbAN/5r3zR7Y5AEuilCXjV1SJAoJ7A62nXrTYBvvOvd93tmUDrTUBem9wIEKgo8ETa92/S1loT4Dv/ihfdrgk8Emi1Cfh9Gp93/StTAhsI5EXgd2lrpQnIY8ljciNAoL6A+V/f2BEINC2QXwl4I217NwH5Zb88FjcCBLYTMP+3s3YkAs0K5N8U+I+0bd0IfJaO+ctmVQyMwBwC5v8c19lZEjgp8EL6Sv7Z262agPxXCv2oz8nL4QsENhUw/zfldjACbQrk7wY+TVutRiD/ek+/37vNa29UBMx/NUBgcoH8TtyX0/Zx2tb4U8J5Hx+lLS8u3uWbENwINCxg/jd8cQyNwJYCz6aDvZa2D9P2RdrOfWUgP/aDtP06bc+kzY0Agf4Enk1DNv/7u26bjdi7tzej3v1AT6YR/DRtP0rbc2n7QdqeTlu+fZ62/EbCv6ftL2n7c9q+TJsbAQJjCJj/Y1xHZ0GAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgssD/A1hw9f23AMmGAAAAAElFTkSuQmCC";
|
|
3286
3486
|
|
|
3287
3487
|
var img$e = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAkCAYAAACe0YppAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAHqADAAQAAAABAAAAJAAAAAAh0JciAAACrUlEQVRYCcWXPW8TQRCG31kbpNBChGiiRJQ0tFDggPgBtKGhJgEJUGxDFCmJUBKwDUoV2jRxS2NEhZCgAPELKBAhIPFRpKIwUXIe3vFHFJzbs2zWupHWu7e7M8/OenbuVkDReUzoLjbZvGDP/ysiWJQSlpLsiN7HOBp4rco6oBC+Svicz2QWERYULSjh605Q3dvHnk/B138sg1EV1DrjtPVAC9j1eS6NPD5x8lko3rgKch3FfmvbOY2w1a3n89xxlWdsMuvP3UqDPhO2xPLF9NueL3Tbct0dIZ4jJdTh8iH4Irf9H/hQwLZ4edSCs7ltz/Tc4CvWNhka2IwbXDKYZLMDt4Brep61CUFE8Jt26iwj9KbcKGDW7GrDfnGi+csf8zwqoh7MY1nFjhLYBpziKTl3UIDRDthqp5gJ5zENZsrMCXlscQE3DoMO2sJ8wURFr08GBRtAythgZeWIRAVsSDtZBdvqI5QeHamBE7c6yqPKJND/G0sZvIqXmQpmfI57wVrEGI/CFINhIOGCp3lml/mS+B5nwAuWx/iqs5hnDr8ap9ijT7noF+5JPNR0vWAblAqWWVkJLqkFV2rgxK1mVG8ySC4Oss/NqC5j2qfrBdf5RSERrg8c1cBNvYOHsoYfcXAveISvNHpcZIrLxSkm9rXO8SvngZquF2yDTPolVlaCS2rBlRo4cav56fscgvNJ+8wYqDEt3k6aEzfmBVtU82P/Wq+oZiq/pfdQkqf4Fgfw9XnBFtXM1XeZq73nmGPK68871yfUFuMF2yBz9RorK8ElteDiHY2blYI4/ks7xuUCxofNJ2OizfiZZVTW2GGfKJNMkc9cFlUu5k/QRQiOYx9TfHFcMrtkvhWdw2l2fmDnWFCY39i2XWucrOAXb1A5ruKjf26wkfeEXrE71V+iV9IhdJQEvwAAAABJRU5ErkJggg==";
|
|
3288
3488
|
|
|
@@ -3584,7 +3784,7 @@ const deserializeValue = (value) => {
|
|
|
3584
3784
|
};
|
|
3585
3785
|
|
|
3586
3786
|
const FileInputWidget = ({ config }) => {
|
|
3587
|
-
const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
3787
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
3588
3788
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
3589
3789
|
const accept = widgetConfig['widget-data-options']?.accept;
|
|
3590
3790
|
const multiple = widgetConfig['widget-data-options']?.multiple || false;
|
|
@@ -3824,7 +4024,7 @@ const FileInputWidget = ({ config }) => {
|
|
|
3824
4024
|
setPreviewFile(null);
|
|
3825
4025
|
} })] }));
|
|
3826
4026
|
}
|
|
3827
|
-
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
4027
|
+
return (jsxRuntimeExports.jsxs("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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:space-x-4", children: [jsxRuntimeExports.jsxs("label", { className: `cursor-pointer inline-flex items-center justify-between gap-2 border border-gray-300 shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isEnabled
|
|
3828
4028
|
? 'opacity-50 cursor-not-allowed'
|
|
3829
4029
|
: ''}`, style: {
|
|
3830
4030
|
width: '100%',
|
|
@@ -3885,6 +4085,13 @@ const namespaceWidgetConfig = (widgetConfig, namespace) => {
|
|
|
3885
4085
|
if (namespaced['widget-data-path']) {
|
|
3886
4086
|
namespaced['widget-data-path'] = namespaceDataPath(namespaced['widget-data-path'], namespace);
|
|
3887
4087
|
}
|
|
4088
|
+
// Namespace geo parent references so cascade events match namespaced widget-id
|
|
4089
|
+
if (namespaced['widget-geo-config']?.parentWidgetId) {
|
|
4090
|
+
namespaced['widget-geo-config'] = {
|
|
4091
|
+
...namespaced['widget-geo-config'],
|
|
4092
|
+
parentWidgetId: `${namespace}__${namespaced['widget-geo-config'].parentWidgetId}`,
|
|
4093
|
+
};
|
|
4094
|
+
}
|
|
3888
4095
|
// Recursively namespace nested widgets (for layout widgets)
|
|
3889
4096
|
if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
|
|
3890
4097
|
namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
|
|
@@ -4089,6 +4296,9 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
|
|
|
4089
4296
|
const isVisible = shouldShowWidget(widget['widget-data-options'], currentSchemaData);
|
|
4090
4297
|
if (!isVisible)
|
|
4091
4298
|
continue;
|
|
4299
|
+
const isEnabled = shouldEnableWidget(widget['widget-data-options'], currentSchemaData);
|
|
4300
|
+
if (!isEnabled)
|
|
4301
|
+
continue;
|
|
4092
4302
|
const widgetId = widget['widget-id'];
|
|
4093
4303
|
if (isTableLikeWidget(widget)) {
|
|
4094
4304
|
const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
|
|
@@ -4098,7 +4308,8 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
|
|
|
4098
4308
|
continue;
|
|
4099
4309
|
}
|
|
4100
4310
|
const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
|
|
4101
|
-
const
|
|
4311
|
+
const isRequired = shouldRequireWidget(widget['widget-data-options'], currentSchemaData, widget['widget-required'] ?? false);
|
|
4312
|
+
const errors = validateWidget(value, widget['widget-data-validation'], isRequired, skipRequired);
|
|
4102
4313
|
if (errors.length > 0) {
|
|
4103
4314
|
isValid = false;
|
|
4104
4315
|
dispatch(setTouched({ widgetId, touched: true }));
|
|
@@ -4131,6 +4342,108 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
|
|
|
4131
4342
|
return isValid;
|
|
4132
4343
|
};
|
|
4133
4344
|
|
|
4345
|
+
const cloneValue = (value) => {
|
|
4346
|
+
if (value === undefined) {
|
|
4347
|
+
return undefined;
|
|
4348
|
+
}
|
|
4349
|
+
try {
|
|
4350
|
+
return structuredClone(value);
|
|
4351
|
+
}
|
|
4352
|
+
catch {
|
|
4353
|
+
return JSON.parse(JSON.stringify(value));
|
|
4354
|
+
}
|
|
4355
|
+
};
|
|
4356
|
+
const resolveNamespacedWidgetId = (widgetId, namespace) => namespace ? `${namespace}__${widgetId}` : widgetId;
|
|
4357
|
+
const resolveStoreDataPath = (dataPath, namespace) => {
|
|
4358
|
+
if (!dataPath) {
|
|
4359
|
+
return dataPath;
|
|
4360
|
+
}
|
|
4361
|
+
if (!namespace) {
|
|
4362
|
+
return dataPath;
|
|
4363
|
+
}
|
|
4364
|
+
if (typeof dataPath === 'string') {
|
|
4365
|
+
return `${namespace}.${dataPath}`;
|
|
4366
|
+
}
|
|
4367
|
+
return Object.fromEntries(Object.entries(dataPath).map(([key, path]) => [key, `${namespace}.${path}`]));
|
|
4368
|
+
};
|
|
4369
|
+
/**
|
|
4370
|
+
* Capture Redux widget values for a section at edit entry.
|
|
4371
|
+
* Used to restore exact pre-edit state on Cancel (schemaData may be stale or shared with Redux).
|
|
4372
|
+
*/
|
|
4373
|
+
function captureSectionEditSnapshot(values, section, options) {
|
|
4374
|
+
const { namespace, sectionId, supportingDocuments = [] } = options ?? {};
|
|
4375
|
+
const dataPaths = [];
|
|
4376
|
+
const processedPaths = new Set();
|
|
4377
|
+
const widgetIds = {};
|
|
4378
|
+
const addPath = (path) => {
|
|
4379
|
+
if (!path || processedPaths.has(path)) {
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
processedPaths.add(path);
|
|
4383
|
+
dataPaths.push({
|
|
4384
|
+
path,
|
|
4385
|
+
value: cloneValue(getValueByPath(values, path)),
|
|
4386
|
+
});
|
|
4387
|
+
if (path.endsWith('.geo_code_hierarchy_json')) {
|
|
4388
|
+
const prefix = path.slice(0, -'.geo_code_hierarchy_json'.length);
|
|
4389
|
+
addPath(`${prefix}.geo_lowest_level_value_id`);
|
|
4390
|
+
}
|
|
4391
|
+
};
|
|
4392
|
+
collectWidgets(section.panels).forEach((widget) => {
|
|
4393
|
+
const widgetId = resolveNamespacedWidgetId(widget['widget-id'], namespace);
|
|
4394
|
+
const storeDataPath = resolveStoreDataPath(widget['widget-data-path'], namespace);
|
|
4395
|
+
if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
|
|
4396
|
+
widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
|
|
4397
|
+
}
|
|
4398
|
+
else {
|
|
4399
|
+
widgetIds[widgetId] = { present: false };
|
|
4400
|
+
}
|
|
4401
|
+
if (typeof storeDataPath === 'string') {
|
|
4402
|
+
addPath(storeDataPath);
|
|
4403
|
+
}
|
|
4404
|
+
else if (storeDataPath && typeof storeDataPath === 'object') {
|
|
4405
|
+
Object.values(storeDataPath).forEach((path) => {
|
|
4406
|
+
if (typeof path === 'string') {
|
|
4407
|
+
addPath(path);
|
|
4408
|
+
}
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
});
|
|
4412
|
+
supportingDocuments.forEach((doc, index) => {
|
|
4413
|
+
const widgetId = `supporting-doc-${sectionId ?? 'section'}-${index}`;
|
|
4414
|
+
const storeDataPath = namespace && doc['document-data-path']
|
|
4415
|
+
? `${namespace}.${doc['document-data-path']}`
|
|
4416
|
+
: doc['document-data-path'];
|
|
4417
|
+
if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
|
|
4418
|
+
widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
|
|
4419
|
+
}
|
|
4420
|
+
else {
|
|
4421
|
+
widgetIds[widgetId] = { present: false };
|
|
4422
|
+
}
|
|
4423
|
+
if (typeof storeDataPath === 'string') {
|
|
4424
|
+
addPath(storeDataPath);
|
|
4425
|
+
}
|
|
4426
|
+
});
|
|
4427
|
+
return { dataPaths, widgetIds };
|
|
4428
|
+
}
|
|
4429
|
+
/** Apply a section edit snapshot back onto the full Redux values object. */
|
|
4430
|
+
function applySectionEditSnapshot(currentValues, snapshot) {
|
|
4431
|
+
let result = currentValues;
|
|
4432
|
+
for (const { path, value } of snapshot.dataPaths) {
|
|
4433
|
+
result = setValueByPath(result, path, cloneValue(value));
|
|
4434
|
+
}
|
|
4435
|
+
for (const [widgetId, entry] of Object.entries(snapshot.widgetIds)) {
|
|
4436
|
+
if (entry.present) {
|
|
4437
|
+
result = { ...result, [widgetId]: cloneValue(entry.value) };
|
|
4438
|
+
}
|
|
4439
|
+
else {
|
|
4440
|
+
const { [widgetId]: _removed, ...rest } = result;
|
|
4441
|
+
result = rest;
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
return result;
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4134
4447
|
/** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
|
|
4135
4448
|
const READONLY_VALUE_ROW_ROOT_CLASSES = [
|
|
4136
4449
|
'TextDisplayWidget',
|
|
@@ -4342,6 +4655,8 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4342
4655
|
}, [forceExitEdit]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
4343
4656
|
const [isDocumentsExpanded, setIsDocumentsExpanded] = useState(true);
|
|
4344
4657
|
const sectionRef = useRef(null);
|
|
4658
|
+
const baselineSnapshotRef = useRef(null);
|
|
4659
|
+
const editEntrySnapshotRef = useRef(null);
|
|
4345
4660
|
const [sectionHeight, setSectionHeight] = useState(null);
|
|
4346
4661
|
const [editSectionPosition, setEditSectionPosition] = useState(null);
|
|
4347
4662
|
// Capture section position when entering edit mode and update on scroll
|
|
@@ -4406,6 +4721,18 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4406
4721
|
panels: makePanelsEditable(sectionToRender.panels, widgetsEditable),
|
|
4407
4722
|
};
|
|
4408
4723
|
}, [sectionToRender, widgetsEditable]);
|
|
4724
|
+
const effectiveHideEditButton = hideEditButton ||
|
|
4725
|
+
section['section-hide-edit-button'] === true ||
|
|
4726
|
+
!collectWidgets(section.panels || []).some((w) => section['section-editable'] === true || w['widget-readonly'] !== true);
|
|
4727
|
+
const captureEditEntrySnapshot = useCallback(() => {
|
|
4728
|
+
const currentValues = store.getState().widget.values;
|
|
4729
|
+
const supportingDocuments = section['section-supporting-documents'] || [];
|
|
4730
|
+
editEntrySnapshotRef.current = captureSectionEditSnapshot(currentValues, section, {
|
|
4731
|
+
namespace,
|
|
4732
|
+
sectionId,
|
|
4733
|
+
supportingDocuments: hasSupportingDocuments ? supportingDocuments : [],
|
|
4734
|
+
});
|
|
4735
|
+
}, [store, section, namespace, sectionId, hasSupportingDocuments]);
|
|
4409
4736
|
// Handle edit button click
|
|
4410
4737
|
const handleEdit = () => {
|
|
4411
4738
|
// Capture height BEFORE entering edit mode to preserve space
|
|
@@ -4413,6 +4740,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4413
4740
|
const height = sectionRef.current.offsetHeight;
|
|
4414
4741
|
setSectionHeight(height);
|
|
4415
4742
|
}
|
|
4743
|
+
captureEditEntrySnapshot();
|
|
4416
4744
|
setIsEditMode(true);
|
|
4417
4745
|
onEditModeChange?.(originalSectionId, true);
|
|
4418
4746
|
};
|
|
@@ -4587,8 +4915,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4587
4915
|
}
|
|
4588
4916
|
return { records, files };
|
|
4589
4917
|
}, [originalSection, hasSupportingDocuments]);
|
|
4590
|
-
// Capture baseline when entering edit mode (used for isDirty comparison)
|
|
4591
|
-
const baselineSnapshotRef = useRef(null);
|
|
4592
4918
|
// IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
|
|
4593
4919
|
const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = useState(0);
|
|
4594
4920
|
// IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
|
|
@@ -4606,6 +4932,9 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4606
4932
|
// Set baseline when entering edit mode; clear when leaving (baseline captured only on entry)
|
|
4607
4933
|
useEffect(() => {
|
|
4608
4934
|
if (effectiveEditModeForDirty) {
|
|
4935
|
+
if (!editEntrySnapshotRef.current) {
|
|
4936
|
+
captureEditEntrySnapshot();
|
|
4937
|
+
}
|
|
4609
4938
|
const oldSchemaData = schemaData || contextSchemaData || {};
|
|
4610
4939
|
if (namespace) {
|
|
4611
4940
|
const namespacedSchema = getValueByPath(oldSchemaData, namespace);
|
|
@@ -4614,11 +4943,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4614
4943
|
: buildSectionSnapshot(oldSchemaData);
|
|
4615
4944
|
}
|
|
4616
4945
|
else {
|
|
4617
|
-
baselineSnapshotRef.current = buildSectionSnapshot(
|
|
4946
|
+
baselineSnapshotRef.current = buildSectionSnapshot(store.getState().widget.values, namespace);
|
|
4618
4947
|
}
|
|
4619
4948
|
}
|
|
4620
4949
|
else {
|
|
4621
4950
|
baselineSnapshotRef.current = null;
|
|
4951
|
+
editEntrySnapshotRef.current = null;
|
|
4622
4952
|
onSectionDirtyChange?.(sectionId, false);
|
|
4623
4953
|
}
|
|
4624
4954
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- baseline must be captured only when effectiveEditModeForDirty toggles
|
|
@@ -4644,56 +4974,103 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4644
4974
|
// and handleCancel.
|
|
4645
4975
|
const revertToOriginalValues = useCallback(() => {
|
|
4646
4976
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4647
|
-
const oldSchemaData = schemaData || contextSchemaData;
|
|
4648
4977
|
const currentStoreValues = store.getState().widget.values;
|
|
4978
|
+
const snapshot = editEntrySnapshotRef.current;
|
|
4649
4979
|
let newStoreValues = currentStoreValues;
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
const
|
|
4655
|
-
const
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4980
|
+
if (snapshot) {
|
|
4981
|
+
newStoreValues = applySectionEditSnapshot(currentStoreValues, snapshot);
|
|
4982
|
+
}
|
|
4983
|
+
else {
|
|
4984
|
+
const oldSchemaData = schemaData || contextSchemaData;
|
|
4985
|
+
const processedGeoGroups = new Set();
|
|
4986
|
+
sectionWidgets.forEach((widget) => {
|
|
4987
|
+
const originalWidgetId = widget['widget-id'];
|
|
4988
|
+
const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
|
|
4989
|
+
const widgetId = namespacedWidgetId;
|
|
4990
|
+
const originalDataPath = widget['widget-data-path'];
|
|
4991
|
+
const storeDataPath = namespace && originalDataPath
|
|
4992
|
+
? (typeof originalDataPath === 'string'
|
|
4993
|
+
? `${namespace}.${originalDataPath}`
|
|
4994
|
+
: Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
|
|
4995
|
+
: originalDataPath;
|
|
4996
|
+
const geoConfig = widget['widget-geo-config'];
|
|
4997
|
+
if (widgetId && originalDataPath) {
|
|
4998
|
+
let oldValue;
|
|
4999
|
+
if (typeof originalDataPath === 'object') {
|
|
5000
|
+
oldValue = {};
|
|
5001
|
+
Object.entries(originalDataPath).forEach(([key, path]) => {
|
|
5002
|
+
if (typeof path === 'string') {
|
|
5003
|
+
oldValue[key] = getValueByPath(oldSchemaData, path);
|
|
5004
|
+
}
|
|
5005
|
+
});
|
|
5006
|
+
}
|
|
5007
|
+
else if (typeof originalDataPath === 'string') {
|
|
5008
|
+
oldValue = getValueByPath(oldSchemaData, originalDataPath);
|
|
5009
|
+
}
|
|
5010
|
+
if (oldValue !== undefined) {
|
|
5011
|
+
newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
|
|
5012
|
+
if (geoConfig && typeof storeDataPath === 'string') {
|
|
5013
|
+
const groupId = getGeoGroupId(storeDataPath);
|
|
5014
|
+
const levelValue = resolveGeoWidgetLevelValue(newStoreValues, widgetId, storeDataPath, geoConfig);
|
|
5015
|
+
if (levelValue !== undefined && levelValue !== null && levelValue !== '') {
|
|
5016
|
+
newStoreValues = { ...newStoreValues, [widgetId]: levelValue };
|
|
5017
|
+
}
|
|
5018
|
+
else {
|
|
5019
|
+
const { [widgetId]: _removed, ...rest } = newStoreValues;
|
|
5020
|
+
newStoreValues = rest;
|
|
5021
|
+
}
|
|
5022
|
+
if (!processedGeoGroups.has(groupId)) {
|
|
5023
|
+
resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
|
|
5024
|
+
processedGeoGroups.add(groupId);
|
|
5025
|
+
}
|
|
5026
|
+
if (geoConfig.parentWidgetId) {
|
|
5027
|
+
dispatch(setDataSource({ widgetId, data: [] }));
|
|
5028
|
+
}
|
|
4667
5029
|
}
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
5030
|
+
else {
|
|
5031
|
+
newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
4672
5034
|
}
|
|
4673
|
-
|
|
5035
|
+
});
|
|
5036
|
+
if (hasSupportingDocuments) {
|
|
5037
|
+
const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
|
|
5038
|
+
originalSupportingDocuments.forEach((doc, index) => {
|
|
5039
|
+
const widgetId = `supporting-doc-${sectionId}-${index}`;
|
|
5040
|
+
const originalDataPath = doc['document-data-path'];
|
|
5041
|
+
const storeDataPath = namespace && originalDataPath
|
|
5042
|
+
? `${namespace}.${originalDataPath}`
|
|
5043
|
+
: originalDataPath;
|
|
5044
|
+
const oldValue = getValueByPath(oldSchemaData, originalDataPath);
|
|
4674
5045
|
newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
|
|
4675
|
-
|
|
4676
|
-
// sets values[widgetId] during editing, and useBaseWidget.currentValue
|
|
4677
|
-
// reads values[widgetId] first before falling through to the dataPath.
|
|
4678
|
-
newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
|
|
4679
|
-
}
|
|
5046
|
+
});
|
|
4680
5047
|
}
|
|
4681
|
-
});
|
|
4682
|
-
if (hasSupportingDocuments) {
|
|
4683
|
-
const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
|
|
4684
|
-
originalSupportingDocuments.forEach((doc, index) => {
|
|
4685
|
-
const widgetId = `supporting-doc-${sectionId}-${index}`;
|
|
4686
|
-
const originalDataPath = doc['document-data-path'];
|
|
4687
|
-
const storeDataPath = namespace && originalDataPath
|
|
4688
|
-
? `${namespace}.${originalDataPath}`
|
|
4689
|
-
: originalDataPath;
|
|
4690
|
-
const oldValue = getValueByPath(oldSchemaData, originalDataPath);
|
|
4691
|
-
newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
|
|
4692
|
-
});
|
|
4693
|
-
}
|
|
4694
|
-
if (newStoreValues !== currentStoreValues) {
|
|
4695
|
-
dispatch(setValues(newStoreValues));
|
|
4696
5048
|
}
|
|
5049
|
+
const processedGeoGroups = new Set();
|
|
5050
|
+
sectionWidgets.forEach((widget) => {
|
|
5051
|
+
const geoConfig = widget['widget-geo-config'];
|
|
5052
|
+
if (!geoConfig) {
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
5055
|
+
const originalWidgetId = widget['widget-id'];
|
|
5056
|
+
const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
|
|
5057
|
+
const originalDataPath = widget['widget-data-path'];
|
|
5058
|
+
const storeDataPath = namespace && typeof originalDataPath === 'string'
|
|
5059
|
+
? `${namespace}.${originalDataPath}`
|
|
5060
|
+
: originalDataPath;
|
|
5061
|
+
if (typeof storeDataPath !== 'string') {
|
|
5062
|
+
return;
|
|
5063
|
+
}
|
|
5064
|
+
const groupId = getGeoGroupId(storeDataPath);
|
|
5065
|
+
if (!processedGeoGroups.has(groupId)) {
|
|
5066
|
+
resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
|
|
5067
|
+
processedGeoGroups.add(groupId);
|
|
5068
|
+
}
|
|
5069
|
+
if (geoConfig.parentWidgetId) {
|
|
5070
|
+
dispatch(setDataSource({ widgetId, data: [] }));
|
|
5071
|
+
}
|
|
5072
|
+
});
|
|
5073
|
+
dispatch(setValues(newStoreValues));
|
|
4697
5074
|
}, [originalSection, schemaData, contextSchemaData, store, namespace, hasSupportingDocuments, sectionId, dispatch]);
|
|
4698
5075
|
// Handle save button click
|
|
4699
5076
|
const handleSave = async () => {
|
|
@@ -4707,7 +5084,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4707
5084
|
// This ensures we use the original widget IDs and data paths
|
|
4708
5085
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4709
5086
|
const currentState = store.getState().widget;
|
|
4710
|
-
|
|
5087
|
+
let currentSchemaData = currentState.values || {};
|
|
5088
|
+
const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
|
|
5089
|
+
if (geoRegistrations.length > 0) {
|
|
5090
|
+
currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
|
|
5091
|
+
dispatch(setValues(currentSchemaData));
|
|
5092
|
+
}
|
|
4711
5093
|
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
4712
5094
|
if (!isSectionValid) {
|
|
4713
5095
|
return;
|
|
@@ -4773,7 +5155,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4773
5155
|
if (isDraft !== false && store && onSectionSave) {
|
|
4774
5156
|
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4775
5157
|
const currentState = store.getState().widget;
|
|
4776
|
-
|
|
5158
|
+
let currentSchemaData = currentState.values || {};
|
|
5159
|
+
const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
|
|
5160
|
+
if (geoRegistrations.length > 0) {
|
|
5161
|
+
currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
|
|
5162
|
+
dispatch(setValues(currentSchemaData));
|
|
5163
|
+
}
|
|
4777
5164
|
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
|
|
4778
5165
|
if (!isSectionValid)
|
|
4779
5166
|
return;
|
|
@@ -5189,7 +5576,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
5189
5576
|
color: changeRequestType === 'new' ? 'var(--owt-color-bg, #FFFFFF)' : 'var(--owt-color-error, #B91C1C)',
|
|
5190
5577
|
whiteSpace: 'nowrap',
|
|
5191
5578
|
boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
|
|
5192
|
-
}, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' &&
|
|
5579
|
+
}, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && effectiveHideEditButton ? { paddingBottom: '30px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
|
|
5193
5580
|
marginTop: '20px',
|
|
5194
5581
|
paddingBottom: '30px',
|
|
5195
5582
|
display: 'flex',
|
|
@@ -5237,7 +5624,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
5237
5624
|
fontSize: '14px',
|
|
5238
5625
|
color: 'var(--owt-color-text, #011627)',
|
|
5239
5626
|
fontWeight: 'normal',
|
|
5240
|
-
}, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !
|
|
5627
|
+
}, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !effectiveHideEditButton && (jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: !isEditMode ? '10px' : 0, marginBottom: '14px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } })), mode === 'RegistryView' && !isEditMode && !effectiveHideEditButton && (jsxRuntimeExports.jsx("div", { className: "flex justify-center items-center", style: { marginBottom: '20px' }, children: jsxRuntimeExports.jsxs("button", { onClick: handleEdit, className: "font-normal inline-flex items-center gap-2 bg-transparent border-0 p-0 cursor-pointer hover:opacity-80", style: {
|
|
5241
5628
|
fontFamily: 'Roboto, sans-serif',
|
|
5242
5629
|
fontSize: '16px',
|
|
5243
5630
|
color: 'var(--owt-color-text-muted, #727474)',
|
|
@@ -6043,7 +6430,7 @@ const JSONEditorPanel = ({ section, onChange, onReset, context = 'section', }) =
|
|
|
6043
6430
|
'widget-data-format.dateConstraint': ['any', 'past-only', 'future-only'],
|
|
6044
6431
|
'widget-data-format.dateTimeConstraint': ['any', 'past-only', 'future-only'],
|
|
6045
6432
|
// Widget options
|
|
6046
|
-
'widget-data-options.action': ['show', 'hide', 'enable', 'disable'],
|
|
6433
|
+
'widget-data-options.action': ['show', 'hide', 'enable', 'disable', 'require'],
|
|
6047
6434
|
'widget-data-options.condition.operator': CONDITION_OPERATORS,
|
|
6048
6435
|
};
|
|
6049
6436
|
}, []);
|
|
@@ -7428,7 +7815,7 @@ const removeMask = (value, mask) => {
|
|
|
7428
7815
|
};
|
|
7429
7816
|
|
|
7430
7817
|
const TextInputWidget = ({ config }) => {
|
|
7431
|
-
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
7818
|
+
const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
7432
7819
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
7433
7820
|
// Track raw value separately for masking (to preserve unmasked value internally)
|
|
7434
7821
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
@@ -7565,7 +7952,7 @@ const TextInputWidget = ({ config }) => {
|
|
|
7565
7952
|
const label = translateConfig(widgetConfig['widget-label']);
|
|
7566
7953
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextDisplayWidget 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 }) })] }));
|
|
7567
7954
|
}
|
|
7568
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
7955
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntimeExports.jsx("input", { type: getInputType(), value: displayValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: formatConfig?.mask ? undefined : maxLength, inputMode: formatConfig?.currency
|
|
7569
7956
|
? 'decimal'
|
|
7570
7957
|
: formatConfig?.characterType === 'numeric' || formatConfig?.characterType === 'numeric-decimal'
|
|
7571
7958
|
? 'numeric'
|
|
@@ -7588,7 +7975,7 @@ const NumberInputWidget = ({ config }) => {
|
|
|
7588
7975
|
}
|
|
7589
7976
|
return { ...config, 'widget-data-default': normalizedDefault };
|
|
7590
7977
|
}, [config]);
|
|
7591
|
-
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
|
|
7978
|
+
const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
|
|
7592
7979
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
7593
7980
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
7594
7981
|
const validationConfig = widgetConfig['widget-data-validation'];
|
|
@@ -7719,7 +8106,7 @@ const NumberInputWidget = ({ config }) => {
|
|
|
7719
8106
|
const display = numValue !== null ? formatNumber(numValue, formatConfig) : '';
|
|
7720
8107
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] NumberDisplayWidget 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 ${textAlignClass}`, title: String(display ?? ''), children: display }) })] }));
|
|
7721
8108
|
}
|
|
7722
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
8109
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: maxLength, 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 ${textAlignClass} ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
|
|
7723
8110
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
7724
8111
|
: 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), maxLength && (jsxRuntimeExports.jsxs("span", { className: `text-xs ml-2 flex-shrink-0 ${currentLength > maxLength
|
|
7725
8112
|
? 'text-red-500'
|
|
@@ -7727,7 +8114,7 @@ const NumberInputWidget = ({ config }) => {
|
|
|
7727
8114
|
};
|
|
7728
8115
|
|
|
7729
8116
|
const BooleanWidget = ({ config }) => {
|
|
7730
|
-
const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
8117
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
7731
8118
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
7732
8119
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
7733
8120
|
const representation = formatConfig?.booleanRepresentation || 'true-false';
|
|
@@ -7796,7 +8183,7 @@ const BooleanWidget = ({ config }) => {
|
|
|
7796
8183
|
}
|
|
7797
8184
|
// Render based on control type
|
|
7798
8185
|
if (controlType === 'checkbox') {
|
|
7799
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.
|
|
8186
|
+
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex items-baseline cursor-pointer gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), (currentValue === true || currentValue === false) && (jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: currentValue === true ? trueLabel : falseLabel }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
7800
8187
|
}
|
|
7801
8188
|
if (controlType === 'radio') {
|
|
7802
8189
|
const containerClass = orientation === 'horizontal'
|
|
@@ -7804,10 +8191,10 @@ const BooleanWidget = ({ config }) => {
|
|
|
7804
8191
|
: 'flex flex-col items-start gap-2';
|
|
7805
8192
|
const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
|
|
7806
8193
|
const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
|
|
7807
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.
|
|
8194
|
+
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: containerClass, onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === null, onChange: () => handleRadioChange(null), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: unsetLabel })] })), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === true, onChange: () => handleRadioChange(true), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: trueLabel })] }), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === false, onChange: () => handleRadioChange(false), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
7808
8195
|
}
|
|
7809
8196
|
// Toggle/switch control type
|
|
7810
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.
|
|
8197
|
+
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 sm:min-w-[150px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(null), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === null
|
|
7811
8198
|
? 'bg-blue-600 text-white border-blue-600'
|
|
7812
8199
|
: 'bg-white text-gray-700 border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`, style: { borderRadius: '15px' }, children: unsetLabel })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(true), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === true
|
|
7813
8200
|
? 'bg-blue-600 text-white border-blue-600'
|
|
@@ -7817,7 +8204,7 @@ const BooleanWidget = ({ config }) => {
|
|
|
7817
8204
|
};
|
|
7818
8205
|
|
|
7819
8206
|
const DateInputWidget = ({ config }) => {
|
|
7820
|
-
const { value, error, touched, isEnabled, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
|
|
8207
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
|
|
7821
8208
|
const formValues = useSelector((state) => state.widget.values);
|
|
7822
8209
|
const { translateConfig } = useWidgetTranslation();
|
|
7823
8210
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
@@ -8018,7 +8405,7 @@ const DateInputWidget = ({ config }) => {
|
|
|
8018
8405
|
}
|
|
8019
8406
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateDisplayWidget 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 }) })] }));
|
|
8020
8407
|
}
|
|
8021
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
8408
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDate : undefined, max: inputMethod === 'picker' ? effectiveMaxDate : undefined, 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 ${showValidationError || showRequiredError
|
|
8022
8409
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
8023
8410
|
: 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), showValidationError && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })] }) }));
|
|
8024
8411
|
};
|
|
@@ -8308,7 +8695,7 @@ const validateDateTimeConstraints = (date, minDateTime, maxDateTime, constraint)
|
|
|
8308
8695
|
};
|
|
8309
8696
|
|
|
8310
8697
|
const DateTimeInputWidget = ({ config }) => {
|
|
8311
|
-
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
8698
|
+
const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
8312
8699
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8313
8700
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
8314
8701
|
const optionsConfig = widgetConfig['widget-data-options'];
|
|
@@ -8461,29 +8848,33 @@ const DateTimeInputWidget = ({ config }) => {
|
|
|
8461
8848
|
}
|
|
8462
8849
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateTimeDisplayWidget 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 }) })] }));
|
|
8463
8850
|
}
|
|
8464
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
8851
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDateTime : undefined, max: inputMethod === 'picker' ? effectiveMaxDateTime : undefined, 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 === ''))
|
|
8465
8852
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
8466
8853
|
: 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8467
8854
|
};
|
|
8468
8855
|
|
|
8469
8856
|
const SelectWidget = ({ config }) => {
|
|
8470
|
-
const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8857
|
+
const { value, geoDisplayLabel, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8471
8858
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8472
8859
|
// For readonly mode, render as display text showing only the selected label
|
|
8473
8860
|
if (widgetConfig['widget-readonly']) {
|
|
8474
8861
|
const label = translateConfig(widgetConfig['widget-label']);
|
|
8475
8862
|
// Find the selected option's label
|
|
8476
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
8477
|
-
const displayValue = selectedOption
|
|
8863
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
8864
|
+
const displayValue = selectedOption
|
|
8865
|
+
? translateConfig(selectedOption.label)
|
|
8866
|
+
: loading
|
|
8867
|
+
? (geoDisplayLabel || '-')
|
|
8868
|
+
: (geoDisplayLabel || (value != null && value !== '' ? translateConfig(String(value)) : '-'));
|
|
8478
8869
|
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 }) })] }));
|
|
8479
8870
|
}
|
|
8480
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
8871
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
|
|
8481
8872
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
8482
|
-
: 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8873
|
+
: 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: translateConfig(option.label) }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8483
8874
|
};
|
|
8484
8875
|
|
|
8485
8876
|
const RadioWidget = ({ config }) => {
|
|
8486
|
-
const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8877
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8487
8878
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8488
8879
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
8489
8880
|
const layout = formatConfig?.layout || widgetConfig['widget-orientation'] || 'vertical';
|
|
@@ -8546,14 +8937,16 @@ const RadioWidget = ({ config }) => {
|
|
|
8546
8937
|
if (widgetConfig['widget-readonly']) {
|
|
8547
8938
|
const label = translateConfig(widgetConfig['widget-label']);
|
|
8548
8939
|
const selectedOption = processedOptions.find(opt => opt.value === currentValue);
|
|
8549
|
-
const displayValue = selectedOption
|
|
8940
|
+
const displayValue = selectedOption
|
|
8941
|
+
? translateConfig(selectedOption.label)
|
|
8942
|
+
: (allowUnset && currentValue === null ? '-' : '');
|
|
8550
8943
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] RadioDisplayWidget 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 }) })] }));
|
|
8551
8944
|
}
|
|
8552
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
8945
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null, onChange: handleUnset, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: option.value, checked: currentValue === option.value, onChange: (e) => handleChange(option.value), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, option.value)))] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8553
8946
|
};
|
|
8554
8947
|
|
|
8555
8948
|
const CheckboxWidget = ({ config }) => {
|
|
8556
|
-
const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8949
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8557
8950
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8558
8951
|
const hasDataSource = !!widgetConfig['widget-data-source'];
|
|
8559
8952
|
const formatConfig = widgetConfig['widget-data-format'];
|
|
@@ -8568,7 +8961,7 @@ const CheckboxWidget = ({ config }) => {
|
|
|
8568
8961
|
const displayValue = isChecked ? 'Yes' : 'No';
|
|
8569
8962
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CheckboxDisplayWidget 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 }) })] }));
|
|
8570
8963
|
}
|
|
8571
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.
|
|
8964
|
+
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex cursor-pointer items-baseline gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onChange(e.target.checked), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: isChecked ? 'Yes' : 'No' })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8572
8965
|
}
|
|
8573
8966
|
// Multiple checkboxes (with data source) - for array values
|
|
8574
8967
|
// Process and sort options if needed
|
|
@@ -8638,7 +9031,7 @@ const CheckboxWidget = ({ config }) => {
|
|
|
8638
9031
|
: '-';
|
|
8639
9032
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-3 CheckboxDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", 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 }) })] }));
|
|
8640
9033
|
}
|
|
8641
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.
|
|
9034
|
+
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `inline-flex cursor-pointer items-baseline gap-2 ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", value: option.value, checked: selectedValues.includes(option.value), onChange: (e) => handleCheckboxChange(option.value, e.target.checked), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: translateConfig(option.label) })] }, option.value)))) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8642
9035
|
};
|
|
8643
9036
|
|
|
8644
9037
|
const SimpleTableWidget = ({ config }) => {
|
|
@@ -8689,7 +9082,7 @@ const SimpleTableWidget = ({ config }) => {
|
|
|
8689
9082
|
};
|
|
8690
9083
|
|
|
8691
9084
|
const ArrayWidget = ({ config }) => {
|
|
8692
|
-
const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
|
|
9085
|
+
const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
|
|
8693
9086
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8694
9087
|
const items = Array.isArray(value) ? value : [];
|
|
8695
9088
|
const itemConfig = widgetConfig['widget-item'];
|
|
@@ -8713,7 +9106,7 @@ const ArrayWidget = ({ config }) => {
|
|
|
8713
9106
|
newItems[index] = newValue;
|
|
8714
9107
|
onChange(newItems);
|
|
8715
9108
|
};
|
|
8716
|
-
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.
|
|
9109
|
+
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", 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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
|
|
8717
9110
|
({
|
|
8718
9111
|
...itemConfig,
|
|
8719
9112
|
'widget-id': `${widgetConfig['widget-id']}-item-${index}`,
|
|
@@ -8724,7 +9117,7 @@ const ArrayWidget = ({ config }) => {
|
|
|
8724
9117
|
};
|
|
8725
9118
|
|
|
8726
9119
|
const IterableAccordionWidget = ({ config }) => {
|
|
8727
|
-
const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
|
|
9120
|
+
const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
|
|
8728
9121
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8729
9122
|
const items = Array.isArray(value) ? value : [];
|
|
8730
9123
|
const itemConfig = widgetConfig['widget-item'];
|
|
@@ -8773,7 +9166,7 @@ const IterableAccordionWidget = ({ config }) => {
|
|
|
8773
9166
|
newItems[index] = newValue;
|
|
8774
9167
|
onChange(newItems);
|
|
8775
9168
|
};
|
|
8776
|
-
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.
|
|
9169
|
+
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", 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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
|
|
8777
9170
|
const isCollapsed = collapsedItems[index] ?? defaultCollapsed;
|
|
8778
9171
|
const parentPath = widgetConfig['widget-data-path'];
|
|
8779
9172
|
const childPath = itemConfig['widget-data-path'];
|
|
@@ -8812,7 +9205,7 @@ const IterableAccordionWidget = ({ config }) => {
|
|
|
8812
9205
|
};
|
|
8813
9206
|
|
|
8814
9207
|
const PhoneInputWidget = ({ config }) => {
|
|
8815
|
-
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
9208
|
+
const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
8816
9209
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8817
9210
|
// Use formatted value if available, otherwise raw value
|
|
8818
9211
|
const displayValue = formattedValue !== undefined && formattedValue !== value
|
|
@@ -8823,13 +9216,13 @@ const PhoneInputWidget = ({ config }) => {
|
|
|
8823
9216
|
const label = translateConfig(widgetConfig['widget-label']);
|
|
8824
9217
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] PhoneDisplayWidget 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 || '-' }) })] }));
|
|
8825
9218
|
}
|
|
8826
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
9219
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: "tel", value: displayValue, onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), 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 === ''))
|
|
8827
9220
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
8828
9221
|
: 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8829
9222
|
};
|
|
8830
9223
|
|
|
8831
9224
|
const CurrencyInputWidget = ({ config }) => {
|
|
8832
|
-
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
9225
|
+
const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
8833
9226
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
8834
9227
|
// For input, use raw numeric value; formatted value is for display only
|
|
8835
9228
|
const numericValue = typeof value === 'number' ? value : (value ? parseFloat(String(value)) : '');
|
|
@@ -8851,7 +9244,7 @@ const CurrencyInputWidget = ({ config }) => {
|
|
|
8851
9244
|
const display = formattedValue || (value !== null && value !== undefined ? String(value) : '-');
|
|
8852
9245
|
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CurrencyDisplayWidget 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(display ?? ''), children: display }) })] }));
|
|
8853
9246
|
}
|
|
8854
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
9247
|
+
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: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "relative", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: numericValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), 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 === null || value === undefined || value === ''))
|
|
8855
9248
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
8856
9249
|
: 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), formattedValue && formattedValue !== String(value) && (jsxRuntimeExports.jsx("span", { className: "absolute right-3 top-2 text-gray-500 text-sm", children: formattedValue }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
8857
9250
|
};
|
|
@@ -8957,7 +9350,7 @@ const TableCellSelect = ({ config, value, onValueChange }) => {
|
|
|
8957
9350
|
// Use useBaseWidget to get data source options (it handles loading)
|
|
8958
9351
|
const { dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
8959
9352
|
const isReadonly = config['widget-readonly'] || false;
|
|
8960
|
-
return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onValueChange(e.target.value), disabled: isReadonly || loading, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly || loading ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
|
|
9353
|
+
return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onValueChange(e.target.value === '' ? undefined : e.target.value), disabled: isReadonly || loading, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly || loading ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
|
|
8961
9354
|
borderRadius: '10px',
|
|
8962
9355
|
borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
|
|
8963
9356
|
backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
|
|
@@ -8971,7 +9364,7 @@ const SelectDisplayValue$1 = ({ config, value }) => {
|
|
|
8971
9364
|
if (value === null || value === undefined || value === '') {
|
|
8972
9365
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
8973
9366
|
}
|
|
8974
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
9367
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
8975
9368
|
return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
|
|
8976
9369
|
};
|
|
8977
9370
|
const TableCellText = ({ config, value, onValueChange }) => {
|
|
@@ -9694,6 +10087,23 @@ const TableWidget = ({ config }) => {
|
|
|
9694
10087
|
}, 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] }))] })] }));
|
|
9695
10088
|
};
|
|
9696
10089
|
|
|
10090
|
+
const isUnsetRowValue = (value) => value === null || value === undefined || value === '';
|
|
10091
|
+
/** Match TableWidget cell styling for add / update / delete rows */
|
|
10092
|
+
const getRowCellStyle = (editAction) => {
|
|
10093
|
+
if (editAction === 'ADD') {
|
|
10094
|
+
return { color: 'var(--owt-color-success, #16A34A)' };
|
|
10095
|
+
}
|
|
10096
|
+
if (editAction === 'DELETE') {
|
|
10097
|
+
return {
|
|
10098
|
+
color: 'var(--owt-color-error, #B91C1C)',
|
|
10099
|
+
textDecoration: 'line-through',
|
|
10100
|
+
};
|
|
10101
|
+
}
|
|
10102
|
+
if (editAction === 'UPDATE') {
|
|
10103
|
+
return { color: 'var(--owt-color-warning, #F59E0B)' };
|
|
10104
|
+
}
|
|
10105
|
+
return {};
|
|
10106
|
+
};
|
|
9697
10107
|
// Display select value label in view mode
|
|
9698
10108
|
const SelectDisplayValue = ({ config, value }) => {
|
|
9699
10109
|
const { dataSourceOptions, loading } = useBaseWidget({ config });
|
|
@@ -9701,53 +10111,57 @@ const SelectDisplayValue = ({ config, value }) => {
|
|
|
9701
10111
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
9702
10112
|
if (value === null || value === undefined || value === '')
|
|
9703
10113
|
return jsxRuntimeExports.jsx("span", { children: "-" });
|
|
9704
|
-
const selectedOption = dataSourceOptions.find((option) => option.value === value);
|
|
10114
|
+
const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
|
|
9705
10115
|
return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
|
|
9706
10116
|
};
|
|
10117
|
+
/** Isolated dialog field — avoids re-running data-source effects when sibling fields update. */
|
|
10118
|
+
const DialogTableField = memo(function DialogTableField({ col, cellWidgetId, dialogRowValues, isReadonly, }) {
|
|
10119
|
+
const widgetType = col.widget || 'text';
|
|
10120
|
+
const fieldConfig = useMemo(() => {
|
|
10121
|
+
return {
|
|
10122
|
+
...col,
|
|
10123
|
+
widget: widgetType,
|
|
10124
|
+
'widget-type': col['widget-type'] || 'input',
|
|
10125
|
+
'widget-id': cellWidgetId,
|
|
10126
|
+
'widget-label': col['widget-label'],
|
|
10127
|
+
'widget-readonly': isReadonly || col['widget-readonly'] === true,
|
|
10128
|
+
'widget-data-path': undefined,
|
|
10129
|
+
'widget-data-default': col['widget-data-default'],
|
|
10130
|
+
'widget-data-options': undefined,
|
|
10131
|
+
'widget-required': shouldRequireWidget(col['widget-data-options'], dialogRowValues, col['widget-required']),
|
|
10132
|
+
};
|
|
10133
|
+
}, [col, cellWidgetId, dialogRowValues, isReadonly, widgetType]);
|
|
10134
|
+
return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig }) }));
|
|
10135
|
+
});
|
|
9707
10136
|
/**
|
|
9708
10137
|
* Dialog table widget:
|
|
9709
10138
|
* - Table displays a subset of columns (n out of x)
|
|
9710
10139
|
* - Add/Edit happens in a modal dialog that shows ALL columns as a form
|
|
9711
|
-
*
|
|
9712
|
-
* Usage in schema:
|
|
9713
|
-
* {
|
|
9714
|
-
* "widget": "dialog-table",
|
|
9715
|
-
* "widget-type": "table",
|
|
9716
|
-
* "widget-label": "Household Members",
|
|
9717
|
-
* "widget-id": "householdMembers",
|
|
9718
|
-
* "widget-data-path": "household.members",
|
|
9719
|
-
* "widget-data-columns": [ ...all columns... ],
|
|
9720
|
-
* "widget-data-visible-columns": ["firstName", "lastName", "dob"], // optional override; default = by column flag
|
|
9721
|
-
* // Per-column control (recommended): set "column-visible-in-table": false to hide it in the table
|
|
9722
|
-
* "widget-data-operations": { "add": true, "edit": true, "remove": true }
|
|
9723
|
-
* }
|
|
9724
10140
|
*/
|
|
9725
10141
|
const DialogTableWidget = ({ config }) => {
|
|
9726
10142
|
const { value, error, touched, isEnabled, onChange, config: widgetConfig } = useBaseWidget({ config });
|
|
9727
10143
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
9728
10144
|
const dispatch = useDispatch();
|
|
9729
|
-
const storeValues = useSelector((state) => state.widget?.values ?? {});
|
|
9730
10145
|
const rows = Array.isArray(value) ? value : [];
|
|
9731
10146
|
const columns = widgetConfig['widget-data-columns'] || [];
|
|
9732
10147
|
const operations = widgetConfig['widget-data-operations'] || {};
|
|
9733
10148
|
const isReadonly = widgetConfig['widget-readonly'] || false;
|
|
10149
|
+
// Soft-delete (keep row, red + strikethrough) whenever remove is allowed — matches TableWidget
|
|
10150
|
+
const shouldSoftDeleteOnRemove = !isReadonly && !!operations.remove;
|
|
9734
10151
|
const visibleColumnKeys = widgetConfig['widget-data-visible-columns'];
|
|
9735
10152
|
const visibleColumns = useMemo(() => {
|
|
9736
|
-
// 1) If explicit list provided, it wins
|
|
9737
10153
|
if (Array.isArray(visibleColumnKeys) && visibleColumnKeys.length > 0) {
|
|
9738
10154
|
const keySet = new Set(visibleColumnKeys);
|
|
9739
10155
|
return columns.filter((c) => keySet.has(c['column-key']));
|
|
9740
10156
|
}
|
|
9741
|
-
// 2) Otherwise decide per column (default = visible)
|
|
9742
10157
|
return columns.filter((c) => c['column-visible-in-table'] !== false);
|
|
9743
10158
|
}, [columns, visibleColumnKeys]);
|
|
9744
10159
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
9745
10160
|
const [dialogMode, setDialogMode] = useState('add');
|
|
9746
10161
|
const [activeRowIndex, setActiveRowIndex] = useState(null);
|
|
9747
|
-
const [formData, setFormData] = useState({});
|
|
9748
|
-
/** Unique per dialog open so Redux widget ids don't reuse stale values across rows/add sessions */
|
|
9749
10162
|
const dialogSessionRef = useRef(0);
|
|
9750
10163
|
const [dialogSessionId, setDialogSessionId] = useState(0);
|
|
10164
|
+
const membersWidgetId = widgetConfig['widget-id'];
|
|
9751
10165
|
const addDialogTitle = translateConfig(widgetConfig['widget-data-dialog-title-add']) ||
|
|
9752
10166
|
translate('table.addRecordDialog') ||
|
|
9753
10167
|
'Add record';
|
|
@@ -9758,19 +10172,35 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9758
10172
|
const emptyRow = {};
|
|
9759
10173
|
columns.forEach((col) => {
|
|
9760
10174
|
const key = col['column-key'];
|
|
9761
|
-
|
|
10175
|
+
if (col['widget-data-default'] !== undefined) {
|
|
10176
|
+
emptyRow[key] = col['widget-data-default'];
|
|
10177
|
+
}
|
|
10178
|
+
else if (col.widget === 'checkbox') {
|
|
10179
|
+
emptyRow[key] = false;
|
|
10180
|
+
}
|
|
9762
10181
|
});
|
|
9763
10182
|
return emptyRow;
|
|
9764
10183
|
}, [columns]);
|
|
9765
|
-
const dialogFieldWidgetId = useCallback((columnKey) => `${
|
|
10184
|
+
const dialogFieldWidgetId = useCallback((sessionId, columnKey) => `${membersWidgetId}-dlg-${sessionId}-${columnKey}`, [membersWidgetId]);
|
|
9766
10185
|
const resetDialogWidgets = useCallback((sessionId) => {
|
|
9767
10186
|
if (sessionId <= 0)
|
|
9768
10187
|
return;
|
|
9769
10188
|
columns.forEach((col) => {
|
|
9770
|
-
|
|
9771
|
-
|
|
10189
|
+
dispatch(resetWidget(dialogFieldWidgetId(sessionId, col['column-key'])));
|
|
10190
|
+
});
|
|
10191
|
+
}, [columns, dialogFieldWidgetId, dispatch]);
|
|
10192
|
+
const seedDialogReduxValues = useCallback((sessionId, rowData) => {
|
|
10193
|
+
const seeds = {};
|
|
10194
|
+
columns.forEach((col) => {
|
|
10195
|
+
const key = col['column-key'];
|
|
10196
|
+
if (rowData[key] !== undefined) {
|
|
10197
|
+
seeds[dialogFieldWidgetId(sessionId, key)] = rowData[key];
|
|
10198
|
+
}
|
|
9772
10199
|
});
|
|
9773
|
-
|
|
10200
|
+
if (Object.keys(seeds).length > 0) {
|
|
10201
|
+
dispatch(setValues(seeds));
|
|
10202
|
+
}
|
|
10203
|
+
}, [columns, dialogFieldWidgetId, dispatch]);
|
|
9774
10204
|
const beginDialogSession = useCallback(() => {
|
|
9775
10205
|
dialogSessionRef.current += 1;
|
|
9776
10206
|
const nextSession = dialogSessionRef.current;
|
|
@@ -9779,15 +10209,21 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9779
10209
|
}, []);
|
|
9780
10210
|
const openAddDialog = useCallback(() => {
|
|
9781
10211
|
resetDialogWidgets(dialogSessionId);
|
|
9782
|
-
beginDialogSession();
|
|
10212
|
+
const sessionId = beginDialogSession();
|
|
10213
|
+
seedDialogReduxValues(sessionId, buildEmptyRow());
|
|
9783
10214
|
setDialogMode('add');
|
|
9784
10215
|
setActiveRowIndex(null);
|
|
9785
|
-
setFormData(buildEmptyRow());
|
|
9786
10216
|
setDialogOpen(true);
|
|
9787
|
-
}, [
|
|
10217
|
+
}, [
|
|
10218
|
+
buildEmptyRow,
|
|
10219
|
+
beginDialogSession,
|
|
10220
|
+
resetDialogWidgets,
|
|
10221
|
+
dialogSessionId,
|
|
10222
|
+
seedDialogReduxValues,
|
|
10223
|
+
]);
|
|
9788
10224
|
const openEditDialog = useCallback((rowIndex) => {
|
|
9789
10225
|
resetDialogWidgets(dialogSessionId);
|
|
9790
|
-
beginDialogSession();
|
|
10226
|
+
const sessionId = beginDialogSession();
|
|
9791
10227
|
const row = rows[rowIndex] || {};
|
|
9792
10228
|
const nextFormData = buildEmptyRow();
|
|
9793
10229
|
columns.forEach((col) => {
|
|
@@ -9795,44 +10231,71 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9795
10231
|
if (row[key] !== undefined)
|
|
9796
10232
|
nextFormData[key] = row[key];
|
|
9797
10233
|
});
|
|
10234
|
+
seedDialogReduxValues(sessionId, nextFormData);
|
|
9798
10235
|
setDialogMode('edit');
|
|
9799
10236
|
setActiveRowIndex(rowIndex);
|
|
9800
|
-
setFormData(nextFormData);
|
|
9801
10237
|
setDialogOpen(true);
|
|
9802
|
-
}, [
|
|
10238
|
+
}, [
|
|
10239
|
+
rows,
|
|
10240
|
+
columns,
|
|
10241
|
+
buildEmptyRow,
|
|
10242
|
+
resetDialogWidgets,
|
|
10243
|
+
dialogSessionId,
|
|
10244
|
+
beginDialogSession,
|
|
10245
|
+
seedDialogReduxValues,
|
|
10246
|
+
]);
|
|
9803
10247
|
const closeDialog = useCallback(() => {
|
|
9804
10248
|
const sessionToClear = dialogSessionId;
|
|
9805
10249
|
setDialogOpen(false);
|
|
9806
10250
|
setActiveRowIndex(null);
|
|
9807
|
-
setFormData({});
|
|
9808
10251
|
resetDialogWidgets(sessionToClear);
|
|
9809
10252
|
setDialogSessionId(0);
|
|
9810
10253
|
}, [dialogSessionId, resetDialogWidgets]);
|
|
9811
|
-
const
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
const
|
|
10254
|
+
const dialogStoreValues = useSelector((state) => {
|
|
10255
|
+
if (dialogSessionId <= 0) {
|
|
10256
|
+
return {};
|
|
10257
|
+
}
|
|
10258
|
+
const values = state.widget?.values ?? {};
|
|
10259
|
+
const row = {};
|
|
9816
10260
|
columns.forEach((col) => {
|
|
9817
10261
|
const k = col['column-key'];
|
|
9818
|
-
const wid = dialogFieldWidgetId(k);
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
10262
|
+
const wid = dialogFieldWidgetId(dialogSessionId, k);
|
|
10263
|
+
if (values[wid] !== undefined) {
|
|
10264
|
+
row[k] = values[wid];
|
|
10265
|
+
}
|
|
10266
|
+
});
|
|
10267
|
+
return row;
|
|
10268
|
+
});
|
|
10269
|
+
const dialogRowValues = dialogStoreValues;
|
|
10270
|
+
const collectMergedRowPayload = useCallback(() => dialogStoreValues, [dialogStoreValues]);
|
|
10271
|
+
const finalizeDialogRowPayload = useCallback((raw) => {
|
|
10272
|
+
const result = {};
|
|
10273
|
+
columns.forEach((col) => {
|
|
10274
|
+
const key = col['column-key'];
|
|
10275
|
+
if (!shouldShowWidget(col['widget-data-options'], raw)) {
|
|
10276
|
+
return;
|
|
10277
|
+
}
|
|
10278
|
+
const val = raw[key];
|
|
10279
|
+
if (!isUnsetRowValue(val)) {
|
|
10280
|
+
result[key] = val;
|
|
10281
|
+
}
|
|
9822
10282
|
});
|
|
9823
|
-
return
|
|
9824
|
-
}, [
|
|
10283
|
+
return result;
|
|
10284
|
+
}, [columns]);
|
|
9825
10285
|
const saveDialog = useCallback(() => {
|
|
9826
10286
|
const payload = collectMergedRowPayload();
|
|
9827
10287
|
let hasErrors = false;
|
|
9828
10288
|
columns.forEach((col) => {
|
|
9829
10289
|
const key = col['column-key'];
|
|
9830
|
-
const cellWidgetId = dialogFieldWidgetId(key);
|
|
10290
|
+
const cellWidgetId = dialogFieldWidgetId(dialogSessionId, key);
|
|
9831
10291
|
const isColReadonly = isReadonly || col['widget-readonly'] === true;
|
|
9832
10292
|
if (isColReadonly)
|
|
9833
10293
|
return;
|
|
10294
|
+
if (!shouldShowWidget(col['widget-data-options'], payload))
|
|
10295
|
+
return;
|
|
9834
10296
|
const cellValue = payload[key];
|
|
9835
|
-
const
|
|
10297
|
+
const isRequired = shouldRequireWidget(col['widget-data-options'], payload, col['widget-required']);
|
|
10298
|
+
const validationErrors = validateWidget(cellValue, col['widget-data-validation'], isRequired);
|
|
9836
10299
|
if (validationErrors && validationErrors.length > 0) {
|
|
9837
10300
|
hasErrors = true;
|
|
9838
10301
|
dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
|
|
@@ -9845,8 +10308,9 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9845
10308
|
if (hasErrors) {
|
|
9846
10309
|
return;
|
|
9847
10310
|
}
|
|
10311
|
+
const cleaned = finalizeDialogRowPayload(payload);
|
|
9848
10312
|
if (dialogMode === 'add') {
|
|
9849
|
-
const savedRow = { ...
|
|
10313
|
+
const savedRow = { ...cleaned, edit_action: 'ADD' };
|
|
9850
10314
|
onChange([...rows, savedRow]);
|
|
9851
10315
|
closeDialog();
|
|
9852
10316
|
return;
|
|
@@ -9856,15 +10320,43 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9856
10320
|
const currentRow = newRows[activeRowIndex] || {};
|
|
9857
10321
|
const wasDeleted = currentRow.edit_action === 'DELETE';
|
|
9858
10322
|
const editAction = wasDeleted ? 'UPDATE' : (currentRow.edit_action ?? 'UPDATE');
|
|
9859
|
-
|
|
10323
|
+
const merged = { ...currentRow, ...cleaned, edit_action: editAction };
|
|
10324
|
+
columns.forEach((col) => {
|
|
10325
|
+
const key = col['column-key'];
|
|
10326
|
+
if (!(key in cleaned)) {
|
|
10327
|
+
delete merged[key];
|
|
10328
|
+
}
|
|
10329
|
+
});
|
|
10330
|
+
newRows[activeRowIndex] = merged;
|
|
9860
10331
|
onChange(newRows);
|
|
9861
10332
|
closeDialog();
|
|
9862
10333
|
}
|
|
9863
|
-
}, [
|
|
10334
|
+
}, [
|
|
10335
|
+
collectMergedRowPayload,
|
|
10336
|
+
finalizeDialogRowPayload,
|
|
10337
|
+
dialogMode,
|
|
10338
|
+
onChange,
|
|
10339
|
+
rows,
|
|
10340
|
+
closeDialog,
|
|
10341
|
+
activeRowIndex,
|
|
10342
|
+
columns,
|
|
10343
|
+
dialogSessionId,
|
|
10344
|
+
dialogFieldWidgetId,
|
|
10345
|
+
isReadonly,
|
|
10346
|
+
dispatch,
|
|
10347
|
+
]);
|
|
9864
10348
|
const deleteRow = useCallback((rowIndex) => {
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
10349
|
+
if (shouldSoftDeleteOnRemove) {
|
|
10350
|
+
const newRows = [...rows];
|
|
10351
|
+
newRows[rowIndex] = {
|
|
10352
|
+
...newRows[rowIndex],
|
|
10353
|
+
edit_action: 'DELETE',
|
|
10354
|
+
};
|
|
10355
|
+
onChange(newRows);
|
|
10356
|
+
return;
|
|
10357
|
+
}
|
|
10358
|
+
onChange(rows.filter((_, i) => i !== rowIndex));
|
|
10359
|
+
}, [rows, onChange, shouldSoftDeleteOnRemove]);
|
|
9868
10360
|
const getDisplayValue = useCallback((rowIndex, column) => {
|
|
9869
10361
|
const key = column['column-key'];
|
|
9870
10362
|
const cellValue = rows[rowIndex]?.[key];
|
|
@@ -9872,11 +10364,12 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9872
10364
|
if (cellValue === null || cellValue === undefined || cellValue === '')
|
|
9873
10365
|
return '-';
|
|
9874
10366
|
if (widgetType === 'select')
|
|
9875
|
-
return null;
|
|
10367
|
+
return null;
|
|
9876
10368
|
if (column['widget-data-format'])
|
|
9877
10369
|
return formatValue(cellValue, column['widget-data-format'], column.widget);
|
|
9878
10370
|
return String(cellValue);
|
|
9879
10371
|
}, [rows]);
|
|
10372
|
+
const visibleDialogColumns = useMemo(() => columns.filter((col) => shouldShowWidget(col['widget-data-options'], dialogRowValues)), [columns, dialogRowValues]);
|
|
9880
10373
|
const tableWidgetId = `dialog-table-widget-${widgetConfig['widget-id']}`;
|
|
9881
10374
|
const columnSpan = widgetConfig['widget-column-span'] || 2;
|
|
9882
10375
|
const minWidth = columnSpan * 200;
|
|
@@ -9904,37 +10397,40 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9904
10397
|
}, children: translate('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
|
|
9905
10398
|
borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
|
|
9906
10399
|
borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
|
|
9907
|
-
}, 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) =>
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
:
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
const
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
10400
|
+
}, 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) => {
|
|
10401
|
+
const cellStyle = getRowCellStyle(row?.edit_action);
|
|
10402
|
+
return (jsxRuntimeExports.jsxs("tr", { style: {
|
|
10403
|
+
borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
|
|
10404
|
+
backgroundColor: row?.edit_action === 'DELETE'
|
|
10405
|
+
? 'var(--owt-widget-table-deleted-row-bg, #FEE2E2)'
|
|
10406
|
+
: undefined,
|
|
10407
|
+
}, children: [visibleColumns.map((col) => {
|
|
10408
|
+
const key = col['column-key'];
|
|
10409
|
+
const widgetType = col.widget || 'text';
|
|
10410
|
+
const displayValue = getDisplayValue(rowIndex, col);
|
|
10411
|
+
if (widgetType === 'select' && displayValue === null) {
|
|
10412
|
+
const displayConfig = {
|
|
10413
|
+
...col,
|
|
10414
|
+
'widget-id': `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`,
|
|
10415
|
+
'widget-label': '',
|
|
10416
|
+
'widget-readonly': true,
|
|
10417
|
+
'widget-data-path': undefined,
|
|
10418
|
+
};
|
|
10419
|
+
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));
|
|
10420
|
+
}
|
|
10421
|
+
return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: displayValue }) }, key));
|
|
10422
|
+
}), ((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: {
|
|
10423
|
+
borderRadius: 'var(--owt-btn-border-radius, 10px)',
|
|
10424
|
+
color: 'var(--owt-color-primary-dark, #F07B1A)',
|
|
10425
|
+
backgroundColor: 'transparent',
|
|
10426
|
+
border: 'none',
|
|
10427
|
+
}, 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: {
|
|
10428
|
+
borderRadius: 'var(--owt-btn-border-radius, 10px)',
|
|
10429
|
+
color: 'var(--owt-color-error, #B91C1C)',
|
|
10430
|
+
backgroundColor: 'transparent',
|
|
10431
|
+
border: 'none',
|
|
10432
|
+
}, children: translate('common.remove') || 'Delete' }))] }) }))] }, rowIndex));
|
|
10433
|
+
})] })] }) }), 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: {
|
|
9938
10434
|
maxWidth: '900px',
|
|
9939
10435
|
backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
|
|
9940
10436
|
borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
|
|
@@ -9945,22 +10441,10 @@ const DialogTableWidget = ({ config }) => {
|
|
|
9945
10441
|
cursor: 'pointer',
|
|
9946
10442
|
fontSize: '20px',
|
|
9947
10443
|
lineHeight: 1,
|
|
9948
|
-
}, "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:
|
|
10444
|
+
}, "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: visibleDialogColumns.map((col) => {
|
|
9949
10445
|
const key = col['column-key'];
|
|
9950
|
-
const
|
|
9951
|
-
|
|
9952
|
-
const initialValue = formData[key] ?? col['widget-data-default'] ?? '';
|
|
9953
|
-
const fieldConfig = {
|
|
9954
|
-
...col,
|
|
9955
|
-
widget: widgetType,
|
|
9956
|
-
'widget-type': col['widget-type'] || 'input',
|
|
9957
|
-
'widget-id': cellWidgetId,
|
|
9958
|
-
'widget-label': col['widget-label'],
|
|
9959
|
-
'widget-readonly': isReadonly || col['widget-readonly'] === true,
|
|
9960
|
-
'widget-data-path': undefined,
|
|
9961
|
-
'widget-data-default': initialValue,
|
|
9962
|
-
};
|
|
9963
|
-
return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig, schemaData: { [cellWidgetId]: initialValue }, onValueChange: (_widgetId, newValue) => updateField(key, newValue) }) }, `${dialogSessionId}-${key}`));
|
|
10446
|
+
const cellWidgetId = dialogFieldWidgetId(dialogSessionId, key);
|
|
10447
|
+
return (jsxRuntimeExports.jsx(DialogTableField, { col: col, cellWidgetId: cellWidgetId, dialogRowValues: dialogRowValues, isReadonly: isReadonly }, `${dialogSessionId}-${key}`));
|
|
9964
10448
|
}) }, `dialog-fields-${dialogSessionId}`), 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: {
|
|
9965
10449
|
borderRadius: 'var(--owt-btn-border-radius, 10px)',
|
|
9966
10450
|
border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
|
|
@@ -10150,13 +10634,13 @@ const ProfileWidget = ({ config }) => {
|
|
|
10150
10634
|
if (placeholder) {
|
|
10151
10635
|
placeholder.style.display = 'flex';
|
|
10152
10636
|
}
|
|
10153
|
-
} })) : null, jsxRuntimeExports.jsx("div", { className: "profile-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$
|
|
10637
|
+
} })) : null, jsxRuntimeExports.jsx("div", { className: "profile-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$h, alt: "Profile Placeholder" }) })] }), jsxRuntimeExports.jsxs("div", { className: "profile-info", children: [displayName && (jsxRuntimeExports.jsx("div", { className: "profile-name", children: displayName })), idValue && (jsxRuntimeExports.jsxs("div", { className: "profile-id", children: [showIdLabel && (jsxRuntimeExports.jsx("span", { className: "profile-id-label", children: "ID :" })), jsxRuntimeExports.jsx("span", { className: "profile-id-value", children: idValue })] }))] })] })] }));
|
|
10154
10638
|
};
|
|
10155
10639
|
|
|
10156
10640
|
const TextAreaWidget = ({ config }) => {
|
|
10157
10641
|
// Check readonly early from original config
|
|
10158
10642
|
const isReadonly = config['widget-readonly'] || false;
|
|
10159
|
-
const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
10643
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
10160
10644
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
10161
10645
|
const formatConfig = widgetConfig['widget-data-format'] || {};
|
|
10162
10646
|
const validationConfig = widgetConfig['widget-data-validation'] || {};
|
|
@@ -10204,7 +10688,6 @@ const TextAreaWidget = ({ config }) => {
|
|
|
10204
10688
|
? translateConfig(widgetConfig['widget-label'])
|
|
10205
10689
|
: '';
|
|
10206
10690
|
// Check if required
|
|
10207
|
-
const isRequired = widgetConfig['widget-required'] || false;
|
|
10208
10691
|
// Error display
|
|
10209
10692
|
const hasError = touched && error && error.length > 0;
|
|
10210
10693
|
const errorMessage = hasError ? error[0] : '';
|
|
@@ -10222,7 +10705,7 @@ const TextAreaWidget = ({ config }) => {
|
|
|
10222
10705
|
border: 'none',
|
|
10223
10706
|
}, children: displayValue }) })] }));
|
|
10224
10707
|
}
|
|
10225
|
-
return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.
|
|
10708
|
+
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: label, required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { style: { position: 'relative' }, children: [jsxRuntimeExports.jsx("textarea", { id: widgetConfig['widget-id'], rows: rows, value: getStringValue(), onChange: handleChange, onBlur: onBlur, disabled: !isEnabled, placeholder: placeholder, className: `w-full px-3 py-2 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
|
|
10226
10709
|
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
10227
10710
|
: 'border-gray-300'} ${!isEnabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: {
|
|
10228
10711
|
borderRadius: '10px',
|
|
@@ -10368,17 +10851,16 @@ const HeaderSectionWidget = ({ config }) => {
|
|
|
10368
10851
|
result = searchIn(schemaData);
|
|
10369
10852
|
return result;
|
|
10370
10853
|
}, [paths, values, schemaData]);
|
|
10371
|
-
const imageVal = findValue('image');
|
|
10372
10854
|
const imageUrlVal = findValue('imageUrl');
|
|
10373
10855
|
const [previewUrl, setPreviewUrl] = useState(null);
|
|
10374
10856
|
useEffect(() => {
|
|
10375
|
-
if (
|
|
10376
|
-
const url = URL.createObjectURL(
|
|
10857
|
+
if (imageUrlVal instanceof File) {
|
|
10858
|
+
const url = URL.createObjectURL(imageUrlVal);
|
|
10377
10859
|
setPreviewUrl(url);
|
|
10378
10860
|
return () => URL.revokeObjectURL(url);
|
|
10379
10861
|
}
|
|
10380
10862
|
setPreviewUrl(null);
|
|
10381
|
-
}, [
|
|
10863
|
+
}, [imageUrlVal]);
|
|
10382
10864
|
const displayImageUrl = previewUrl || (typeof imageUrlVal === 'string' && imageUrlVal ? imageUrlVal : null);
|
|
10383
10865
|
const displayName = findValue('name') || '';
|
|
10384
10866
|
const functionalId = findValue('functionalId') || '';
|
|
@@ -10497,14 +10979,16 @@ const HeaderSectionWidget = ({ config }) => {
|
|
|
10497
10979
|
const fileInputRef = useRef(null);
|
|
10498
10980
|
const handleImageUpload = useCallback((e) => {
|
|
10499
10981
|
const file = e.target.files?.[0];
|
|
10500
|
-
if (!file)
|
|
10982
|
+
if (!file || !paths.imageUrl)
|
|
10501
10983
|
return;
|
|
10502
|
-
|
|
10984
|
+
dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, file)));
|
|
10503
10985
|
e.target.value = '';
|
|
10504
|
-
}, [
|
|
10986
|
+
}, [paths.imageUrl, values, dispatch]);
|
|
10505
10987
|
const handleImageDelete = useCallback(() => {
|
|
10506
|
-
|
|
10507
|
-
|
|
10988
|
+
if (!paths.imageUrl)
|
|
10989
|
+
return;
|
|
10990
|
+
dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, null)));
|
|
10991
|
+
}, [paths.imageUrl, values, dispatch]);
|
|
10508
10992
|
// ── Scoped class for CSS isolation ────────────────────────────
|
|
10509
10993
|
const cls = `header-section-widget-${widgetConfig['widget-id']}`;
|
|
10510
10994
|
// ── RENDER ────────────────────────────────────────────────────
|
|
@@ -10813,7 +11297,7 @@ const HeaderSectionWidget = ({ config }) => {
|
|
|
10813
11297
|
.parentElement?.querySelector('.hdr-avatar-placeholder');
|
|
10814
11298
|
if (placeholder)
|
|
10815
11299
|
placeholder.style.display = 'flex';
|
|
10816
|
-
} })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$
|
|
11300
|
+
} })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$h, 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", title: `${getLabel('functionalId')} :`, children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: functionalId || '-', children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", title: getLabel('status'), children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, title: statusLabel, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: "-", 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", title: `${getLabel('statusReason')} :`, children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: statusReason || '-', 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: () => {
|
|
10817
11301
|
if (isReasonMissing)
|
|
10818
11302
|
setShowReasonRequired(true);
|
|
10819
11303
|
}, onChange: (e) => {
|
|
@@ -11558,6 +12042,621 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
|
|
|
11558
12042
|
}, 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] })] })] }) })] })] }));
|
|
11559
12043
|
};
|
|
11560
12044
|
|
|
12045
|
+
/**
|
|
12046
|
+
* Register lookup widget — searchable popup to select a record from any register.
|
|
12047
|
+
* Reads ID from widget-data-path, finds matching register row by internal_record_id, shows display fields.
|
|
12048
|
+
*
|
|
12049
|
+
* Example (individual → household link):
|
|
12050
|
+
*
|
|
12051
|
+
* {
|
|
12052
|
+
* "widget": "register-lookup",
|
|
12053
|
+
* "widget-id": "link_internal_record_id",
|
|
12054
|
+
* "widget-type": "input",
|
|
12055
|
+
* "widget-label": "Household",
|
|
12056
|
+
* "widget-required": true,
|
|
12057
|
+
* "widget-data-path": "<section_register_ids>.link_internal_record_id",
|
|
12058
|
+
* "widget-data-source": {
|
|
12059
|
+
* "type": "api",
|
|
12060
|
+
* "method": "POST",
|
|
12061
|
+
* "params": {
|
|
12062
|
+
* "register_id": "<target_register_id>"
|
|
12063
|
+
* },
|
|
12064
|
+
* "service": "register",
|
|
12065
|
+
* "endpoint": "records"
|
|
12066
|
+
* },
|
|
12067
|
+
* "widget-lookup-config": {
|
|
12068
|
+
* "page_size": 10,
|
|
12069
|
+
* "action_label": "Click to Search Household",
|
|
12070
|
+
* "search_placeholder": "Search by name or ID...",
|
|
12071
|
+
* "select_record_label": "Select Household"
|
|
12072
|
+
* }
|
|
12073
|
+
* }
|
|
12074
|
+
*/
|
|
12075
|
+
const normalizeDisplayFields = (row) => {
|
|
12076
|
+
if (!Array.isArray(row.display_fields))
|
|
12077
|
+
return [];
|
|
12078
|
+
return [...row.display_fields]
|
|
12079
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
12080
|
+
.map((f) => ({
|
|
12081
|
+
label: String(f.field_name ?? ''),
|
|
12082
|
+
value: f.value !== null && f.value !== undefined ? String(f.value) : '-',
|
|
12083
|
+
}));
|
|
12084
|
+
};
|
|
12085
|
+
const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
|
|
12086
|
+
const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
|
|
12087
|
+
const totalPages = typeof pagination.number_of_pages === 'number'
|
|
12088
|
+
? Math.max(1, pagination.number_of_pages)
|
|
12089
|
+
: totalItems > 0
|
|
12090
|
+
? Math.max(1, Math.ceil(totalItems / size))
|
|
12091
|
+
: 1;
|
|
12092
|
+
return { totalItems, totalPages, currentPage: pagination.current_page ?? fallbackPage };
|
|
12093
|
+
};
|
|
12094
|
+
const RecordDisplayPanel = ({ row, widgetIdPrefix, className = '', }) => {
|
|
12095
|
+
const { translateConfig } = useWidgetTranslation();
|
|
12096
|
+
const columnFields = normalizeDisplayFields(row).filter((f) => f.label !== 'record_name' && f.label !== 'functional_record_id' && f.label !== 'internal_record_id');
|
|
12097
|
+
const fieldSlot = (widgetId, label, value) => (jsxRuntimeExports.jsx("div", { className: "min-w-0 overflow-hidden", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: {
|
|
12098
|
+
widget: 'display',
|
|
12099
|
+
'widget-type': 'input',
|
|
12100
|
+
'widget-id': widgetId,
|
|
12101
|
+
'widget-label': label,
|
|
12102
|
+
'widget-readonly': true,
|
|
12103
|
+
'widget-data-default': value,
|
|
12104
|
+
}, schemaData: { [widgetId]: value } }) }, widgetId));
|
|
12105
|
+
const optionalSlot = (field, slot) => field
|
|
12106
|
+
? fieldSlot(`${widgetIdPrefix}-${field.label}`, translateConfig(field.label), field.value)
|
|
12107
|
+
: jsxRuntimeExports.jsx("div", { className: "mb-[10px] invisible text-base", children: "\u00A0" }, slot);
|
|
12108
|
+
const column = (showDivider, isFirst, children) => (jsxRuntimeExports.jsxs("div", { className: "relative min-w-0 overflow-hidden", style: { paddingRight: showDivider ? '40px' : undefined, paddingLeft: isFirst ? undefined : '40px' }, children: [children, showDivider && (jsxRuntimeExports.jsx("div", { className: "absolute right-0 top-0 bottom-[5px] w-px", style: { backgroundColor: 'var(--owt-panel-divider-color, #C4C4C4)' } }))] }));
|
|
12109
|
+
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
|
|
12110
|
+
.register-lookup-record-panel .DisplayFieldWidget,
|
|
12111
|
+
.register-lookup-record-panel .widget-container {
|
|
12112
|
+
min-width: 0 !important;
|
|
12113
|
+
overflow: hidden !important;
|
|
12114
|
+
}
|
|
12115
|
+
.register-lookup-record-panel .DisplayFieldWidget > .flex-1 {
|
|
12116
|
+
min-width: 0 !important;
|
|
12117
|
+
overflow: hidden !important;
|
|
12118
|
+
}
|
|
12119
|
+
.register-lookup-record-panel .DisplayFieldWidget > .text-base.text-gray-600 {
|
|
12120
|
+
width: 50% !important;
|
|
12121
|
+
min-width: 50% !important;
|
|
12122
|
+
max-width: 50% !important;
|
|
12123
|
+
flex-shrink: 0 !important;
|
|
12124
|
+
overflow: hidden !important;
|
|
12125
|
+
text-overflow: ellipsis !important;
|
|
12126
|
+
white-space: nowrap !important;
|
|
12127
|
+
}
|
|
12128
|
+
.register-lookup-record-panel .DisplayFieldWidget > .flex-1 > .text-gray-900 {
|
|
12129
|
+
overflow: hidden;
|
|
12130
|
+
text-overflow: ellipsis;
|
|
12131
|
+
white-space: nowrap;
|
|
12132
|
+
}
|
|
12133
|
+
` }), jsxRuntimeExports.jsxs("div", { className: `register-lookup-record-panel grid w-full min-w-0 ${className}`, style: { gridTemplateColumns: 'repeat(3, minmax(200px, 1fr))' }, children: [column(true, true, (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [fieldSlot(`${widgetIdPrefix}-record_name`, translateConfig('record_name'), row.record_name == null || row.record_name === '' ? '-' : String(row.record_name)), fieldSlot(`${widgetIdPrefix}-functional_record_id`, translateConfig('functional_record_id'), row.functional_record_id == null || row.functional_record_id === '' ? '-' : String(row.functional_record_id))] }))), column(true, false, [0, 1, 2].map((i) => optionalSlot(columnFields[i], `mid-${i}`))), column(false, false, [0, 1, 2].map((i) => optionalSlot(columnFields[i + 3], `right-${i}`)))] })] }));
|
|
12134
|
+
};
|
|
12135
|
+
const PaginationFooter = ({ currentPage, totalPages, totalCount, pageSize, onPageChange, onPrev, onNext, translate, embedded, }) => {
|
|
12136
|
+
const [pageInput, setPageInput] = useState(String(currentPage));
|
|
12137
|
+
useEffect(() => {
|
|
12138
|
+
setPageInput(String(currentPage));
|
|
12139
|
+
}, [currentPage]);
|
|
12140
|
+
const pageStart = totalCount === 0 ? 0 : (currentPage - 1) * pageSize + 1;
|
|
12141
|
+
const pageEnd = totalCount === 0 ? 0 : Math.min(currentPage * pageSize, totalCount);
|
|
12142
|
+
const commitPage = () => {
|
|
12143
|
+
const parsed = parseInt(pageInput, 10);
|
|
12144
|
+
if (!Number.isFinite(parsed)) {
|
|
12145
|
+
setPageInput(String(currentPage));
|
|
12146
|
+
return;
|
|
12147
|
+
}
|
|
12148
|
+
const page = Math.min(Math.max(1, parsed), totalPages);
|
|
12149
|
+
setPageInput(String(page));
|
|
12150
|
+
if (page !== currentPage)
|
|
12151
|
+
onPageChange(page);
|
|
12152
|
+
};
|
|
12153
|
+
return (jsxRuntimeExports.jsxs("div", { className: embedded
|
|
12154
|
+
? 'flex flex-wrap items-center gap-3 flex-1 min-w-0'
|
|
12155
|
+
: 'flex flex-wrap items-center justify-between gap-3 px-5 py-3 flex-shrink-0 border-t border-gray-200', children: [jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-600", children: [totalCount === 1
|
|
12156
|
+
? translate('common.record', { count: totalCount, defaultValue: `${totalCount} record` })
|
|
12157
|
+
: translate('common.records', { count: totalCount, defaultValue: `${totalCount} records` }), totalCount > 0 && ` · ${pageStart}-${pageEnd}`] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: onPrev, 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: translate('common.previous', { defaultValue: 'Prev' }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-sm text-gray-600", children: [jsxRuntimeExports.jsx("span", { children: translate('common.page', { defaultValue: 'Page' }) }), jsxRuntimeExports.jsx("input", { type: "text", inputMode: "numeric", value: pageInput, onChange: (e) => setPageInput(e.target.value.replace(/\D/g, '')), onKeyDown: (e) => {
|
|
12158
|
+
if (e.key === 'Enter') {
|
|
12159
|
+
e.preventDefault();
|
|
12160
|
+
commitPage();
|
|
12161
|
+
}
|
|
12162
|
+
}, onBlur: commitPage, className: "w-10 h-8 text-center text-sm text-gray-900 outline-none rounded-[10px] border border-gray-300 bg-white", "aria-label": translate('common.pageNumber', { defaultValue: 'Page number' }) }), jsxRuntimeExports.jsx("span", { children: translate('common.ofPages', { total: totalPages, defaultValue: `of ${totalPages}` }) })] }), jsxRuntimeExports.jsx("button", { type: "button", onClick: onNext, 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: translate('common.next', { defaultValue: 'Next' }) })] })] }));
|
|
12163
|
+
};
|
|
12164
|
+
const ResultsTable = ({ rows, selectedRowKey, onRowClick, onRowDoubleClick, }) => {
|
|
12165
|
+
const { translateConfig } = useWidgetTranslation();
|
|
12166
|
+
const columns = rows.length === 0
|
|
12167
|
+
? []
|
|
12168
|
+
: [
|
|
12169
|
+
{ key: 'record_name', header: 'record_name' },
|
|
12170
|
+
...normalizeDisplayFields(rows[0]).map((f) => ({ key: f.label, header: f.label })),
|
|
12171
|
+
];
|
|
12172
|
+
return (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.jsx("tr", { children: columns.map((col) => (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: translateConfig(col.header) }, col.key))) }) }), jsxRuntimeExports.jsx("tbody", { children: rows.map((row, idx) => {
|
|
12173
|
+
const tableRowKey = row.internal_record_id ?? idx;
|
|
12174
|
+
const isSelected = selectedRowKey != null && tableRowKey === selectedRowKey;
|
|
12175
|
+
return (jsxRuntimeExports.jsx("tr", { onClick: () => onRowClick(row), onDoubleClick: () => onRowDoubleClick?.(row), className: `cursor-pointer border-b border-gray-100 transition-colors ${isSelected ? 'bg-blue-100' : 'hover:bg-blue-50'}`, children: columns.map((col) => {
|
|
12176
|
+
const cellValue = col.key === 'record_name'
|
|
12177
|
+
? row.record_name != null
|
|
12178
|
+
? String(row.record_name)
|
|
12179
|
+
: '-'
|
|
12180
|
+
: normalizeDisplayFields(row).find((f) => f.label === col.key)?.value ?? '-';
|
|
12181
|
+
return (jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: cellValue }, col.key));
|
|
12182
|
+
}) }, tableRowKey));
|
|
12183
|
+
}) })] }) }));
|
|
12184
|
+
};
|
|
12185
|
+
const RegisterLookupWidget = ({ config }) => {
|
|
12186
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
12187
|
+
const { translate, translateConfig } = useWidgetTranslation();
|
|
12188
|
+
const { dataSourceRequestHandler } = useWidgetContext();
|
|
12189
|
+
const dataSource = widgetConfig['widget-data-source'];
|
|
12190
|
+
const lookupConfig = widgetConfig['widget-lookup-config'];
|
|
12191
|
+
const pageSize = lookupConfig?.page_size ?? 10;
|
|
12192
|
+
const widgetIdPrefix = `${widgetConfig['widget-id'] || 'register-lookup'}-record`;
|
|
12193
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12194
|
+
const [searchText, setSearchText] = useState('');
|
|
12195
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
12196
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
12197
|
+
const [totalPages, setTotalPages] = useState(1);
|
|
12198
|
+
const [totalCount, setTotalCount] = useState(null);
|
|
12199
|
+
const [pendingRow, setPendingRow] = useState(null);
|
|
12200
|
+
const [appliedRecord, setAppliedRecord] = useState(null);
|
|
12201
|
+
const [isHydrating, setIsHydrating] = useState(false);
|
|
12202
|
+
const [modalPos, setModalPos] = useState({ x: 80, y: 80 });
|
|
12203
|
+
const [modalSize, setModalSize] = useState({ w: 860, h: 520 });
|
|
12204
|
+
const isDragging = useRef(false);
|
|
12205
|
+
const dragOrigin = useRef({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
|
|
12206
|
+
const searchInputRef = useRef(null);
|
|
12207
|
+
const hydratedValueRef = useRef(null);
|
|
12208
|
+
const fetchRecords = useCallback(async (text, page, size) => {
|
|
12209
|
+
if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler) {
|
|
12210
|
+
return { rows: [], pagination: {} };
|
|
12211
|
+
}
|
|
12212
|
+
const result = await dataSourceRequestHandler(dataSource.service, dataSource.endpoint, dataSource.method, {
|
|
12213
|
+
...(dataSource.params || {}),
|
|
12214
|
+
search_text: text,
|
|
12215
|
+
current_page: page,
|
|
12216
|
+
page_size: size,
|
|
12217
|
+
}, { headers: dataSource.headers });
|
|
12218
|
+
return {
|
|
12219
|
+
rows: (result?.records ?? []),
|
|
12220
|
+
pagination: (result?.pagination ?? {}),
|
|
12221
|
+
};
|
|
12222
|
+
}, [dataSource, dataSourceRequestHandler]);
|
|
12223
|
+
const findRecordByValue = useCallback(async (recordValue) => {
|
|
12224
|
+
const target = String(recordValue).trim();
|
|
12225
|
+
if (!target)
|
|
12226
|
+
return null;
|
|
12227
|
+
const hydratePageSize = lookupConfig?.hydrate_page_size ?? 50;
|
|
12228
|
+
let page = 1;
|
|
12229
|
+
let totalPages = 1;
|
|
12230
|
+
while (page <= totalPages) {
|
|
12231
|
+
const { rows, pagination } = await fetchRecords('', page, hydratePageSize);
|
|
12232
|
+
const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
|
|
12233
|
+
if (match)
|
|
12234
|
+
return match;
|
|
12235
|
+
totalPages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
|
|
12236
|
+
if (page >= totalPages)
|
|
12237
|
+
break;
|
|
12238
|
+
page += 1;
|
|
12239
|
+
}
|
|
12240
|
+
return null;
|
|
12241
|
+
}, [fetchRecords, lookupConfig?.hydrate_page_size]);
|
|
12242
|
+
const runSearch = useCallback(async (text, page = 1) => {
|
|
12243
|
+
try {
|
|
12244
|
+
const { rows, pagination } = await fetchRecords(text, page, pageSize);
|
|
12245
|
+
const parsed = parsePagination(pagination, rows.length, pageSize, page);
|
|
12246
|
+
setSearchResults(rows);
|
|
12247
|
+
setTotalCount(parsed.totalItems);
|
|
12248
|
+
setTotalPages(parsed.totalPages);
|
|
12249
|
+
setCurrentPage(parsed.currentPage);
|
|
12250
|
+
}
|
|
12251
|
+
catch {
|
|
12252
|
+
setSearchResults([]);
|
|
12253
|
+
setTotalCount(null);
|
|
12254
|
+
setTotalPages(1);
|
|
12255
|
+
setCurrentPage(1);
|
|
12256
|
+
}
|
|
12257
|
+
}, [fetchRecords, pageSize]);
|
|
12258
|
+
useEffect(() => {
|
|
12259
|
+
const onMove = (e) => {
|
|
12260
|
+
if (!isDragging.current)
|
|
12261
|
+
return;
|
|
12262
|
+
setModalPos({
|
|
12263
|
+
x: dragOrigin.current.posX + (e.clientX - dragOrigin.current.mouseX),
|
|
12264
|
+
y: dragOrigin.current.posY + (e.clientY - dragOrigin.current.mouseY),
|
|
12265
|
+
});
|
|
12266
|
+
};
|
|
12267
|
+
const onUp = () => { isDragging.current = false; };
|
|
12268
|
+
document.addEventListener('mousemove', onMove);
|
|
12269
|
+
document.addEventListener('mouseup', onUp);
|
|
12270
|
+
return () => {
|
|
12271
|
+
document.removeEventListener('mousemove', onMove);
|
|
12272
|
+
document.removeEventListener('mouseup', onUp);
|
|
12273
|
+
};
|
|
12274
|
+
}, []);
|
|
12275
|
+
const hasValue = value !== null && value !== undefined && value !== '';
|
|
12276
|
+
const applySelection = (row) => {
|
|
12277
|
+
hydratedValueRef.current = row.internal_record_id;
|
|
12278
|
+
onChange(row.internal_record_id);
|
|
12279
|
+
setAppliedRecord(row);
|
|
12280
|
+
setIsOpen(false);
|
|
12281
|
+
};
|
|
12282
|
+
const openLookup = () => {
|
|
12283
|
+
const w = Math.min(Math.round(window.innerWidth * 0.82), 940);
|
|
12284
|
+
const h = Math.min(Math.round(window.innerHeight * 0.72), 560);
|
|
12285
|
+
setModalPos({ x: Math.round((window.innerWidth - w) / 2), y: Math.round((window.innerHeight - h) / 2) });
|
|
12286
|
+
setModalSize({ w, h });
|
|
12287
|
+
setIsOpen(true);
|
|
12288
|
+
setSearchText('');
|
|
12289
|
+
setSearchResults([]);
|
|
12290
|
+
setTotalCount(null);
|
|
12291
|
+
setCurrentPage(1);
|
|
12292
|
+
setTotalPages(1);
|
|
12293
|
+
setPendingRow(appliedRecord);
|
|
12294
|
+
setTimeout(() => searchInputRef.current?.focus(), 50);
|
|
12295
|
+
runSearch('', 1);
|
|
12296
|
+
};
|
|
12297
|
+
useEffect(() => {
|
|
12298
|
+
if (!hasValue) {
|
|
12299
|
+
hydratedValueRef.current = null;
|
|
12300
|
+
setAppliedRecord(null);
|
|
12301
|
+
setIsHydrating(false);
|
|
12302
|
+
return;
|
|
12303
|
+
}
|
|
12304
|
+
if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler)
|
|
12305
|
+
return;
|
|
12306
|
+
if (hydratedValueRef.current === value)
|
|
12307
|
+
return;
|
|
12308
|
+
let cancelled = false;
|
|
12309
|
+
setIsHydrating(true);
|
|
12310
|
+
setAppliedRecord(null);
|
|
12311
|
+
(async () => {
|
|
12312
|
+
try {
|
|
12313
|
+
const match = await findRecordByValue(value);
|
|
12314
|
+
if (cancelled)
|
|
12315
|
+
return;
|
|
12316
|
+
hydratedValueRef.current = value;
|
|
12317
|
+
setAppliedRecord(match);
|
|
12318
|
+
}
|
|
12319
|
+
catch {
|
|
12320
|
+
if (!cancelled) {
|
|
12321
|
+
hydratedValueRef.current = value;
|
|
12322
|
+
setAppliedRecord(null);
|
|
12323
|
+
}
|
|
12324
|
+
}
|
|
12325
|
+
finally {
|
|
12326
|
+
if (!cancelled)
|
|
12327
|
+
setIsHydrating(false);
|
|
12328
|
+
}
|
|
12329
|
+
})();
|
|
12330
|
+
return () => {
|
|
12331
|
+
cancelled = true;
|
|
12332
|
+
setIsHydrating(false);
|
|
12333
|
+
};
|
|
12334
|
+
}, [hasValue, value, dataSource, dataSourceRequestHandler, findRecordByValue]);
|
|
12335
|
+
const isReadonly = !!widgetConfig['widget-readonly'];
|
|
12336
|
+
const label = translateConfig(widgetConfig['widget-label']);
|
|
12337
|
+
const hasError = (touched && error.length > 0) ||
|
|
12338
|
+
(widgetConfig['widget-required'] && !hasValue);
|
|
12339
|
+
const actionLabel = translateConfig(String(lookupConfig?.action_label ?? `Select ${label}`));
|
|
12340
|
+
const searchPlaceholder = translateConfig(String(lookupConfig?.search_placeholder ?? 'Search...'));
|
|
12341
|
+
const selectRecordLabel = translateConfig(String(lookupConfig?.select_record_label ?? `Select ${label}`));
|
|
12342
|
+
const hydratedPanel = isHydrating ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading', { defaultValue: 'Loading...' }) })) : appliedRecord ? (jsxRuntimeExports.jsx(RecordDisplayPanel, { row: appliedRecord, widgetIdPrefix: `${widgetIdPrefix}-${isReadonly ? 'readonly' : 'applied'}` })) : null;
|
|
12343
|
+
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] w-full", children: [hasValue ? (jsxRuntimeExports.jsxs("div", { className: "w-full", children: [hydratedPanel, !isReadonly && isEnabled && (jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: openLookup, className: "text-sm underline p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 rounded", style: { color: 'var(--owt-color-info, #2563eb)' }, children: translate('common.change', { defaultValue: 'Change' }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: (e) => {
|
|
12344
|
+
e.stopPropagation();
|
|
12345
|
+
hydratedValueRef.current = null;
|
|
12346
|
+
onChange(null);
|
|
12347
|
+
setAppliedRecord(null);
|
|
12348
|
+
setPendingRow(null);
|
|
12349
|
+
}, className: "text-sm underline text-red-500 p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1 rounded", children: translate('common.remove', { defaultValue: 'Remove' }) })] })), !isReadonly && touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : !isReadonly ? (jsxRuntimeExports.jsxs("div", { className: "w-full min-w-0", children: [jsxRuntimeExports.jsxs("button", { type: "button", disabled: !isEnabled, onClick: openLookup, title: actionLabel, className: `flex items-center gap-2 w-full sm:w-[180px] max-w-full px-3 h-[30px] text-sm border rounded-[10px] shadow-sm transition-colors ${hasError ? 'border-red-500 text-gray-700' : 'border-gray-300 text-gray-700'} ${!isEnabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white cursor-pointer'}`, children: [jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-50 flex-shrink-0" }), jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: actionLabel }), isRequired && jsxRuntimeExports.jsx("span", { className: "shrink-0 text-red-500", children: "*" })] }), touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : null, !isReadonly && isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => { setIsOpen(false); onBlur(); } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
|
|
12350
|
+
position: 'fixed',
|
|
12351
|
+
top: modalPos.y,
|
|
12352
|
+
left: modalPos.x,
|
|
12353
|
+
width: modalSize.w,
|
|
12354
|
+
height: modalSize.h,
|
|
12355
|
+
zIndex: 51,
|
|
12356
|
+
resize: 'both',
|
|
12357
|
+
minWidth: 340,
|
|
12358
|
+
minHeight: 260,
|
|
12359
|
+
maxWidth: '96vw',
|
|
12360
|
+
maxHeight: '92vh',
|
|
12361
|
+
backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
|
|
12362
|
+
borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
|
|
12363
|
+
boxShadow: '0 24px 64px rgba(0,0,0,0.28)',
|
|
12364
|
+
}, onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsxs("div", { onMouseDown: (e) => {
|
|
12365
|
+
e.preventDefault();
|
|
12366
|
+
isDragging.current = true;
|
|
12367
|
+
dragOrigin.current = { mouseX: e.clientX, mouseY: e.clientY, posX: modalPos.x, posY: modalPos.y };
|
|
12368
|
+
}, 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: translate('common.selectTitle', { label, defaultValue: `Select ${label}` }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => { setIsOpen(false); onBlur(); }, onMouseDown: (e) => e.stopPropagation(), className: "p-0 border-0 bg-transparent cursor-pointer", "aria-label": translate('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) => {
|
|
12369
|
+
if (e.key === 'Enter') {
|
|
12370
|
+
e.preventDefault();
|
|
12371
|
+
runSearch(searchText, 1);
|
|
12372
|
+
}
|
|
12373
|
+
}, 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": translate('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
|
|
12374
|
+
? translate('common.noResults', { defaultValue: 'No results found' })
|
|
12375
|
+
: translate('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), translate: translate })), 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 })] })] })] }))] }));
|
|
12376
|
+
};
|
|
12377
|
+
|
|
12378
|
+
const MultiSelectWidget = ({ config }) => {
|
|
12379
|
+
const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
|
|
12380
|
+
const { translate, translateConfig } = useWidgetTranslation();
|
|
12381
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12382
|
+
const [isListPopupOpen, setIsListPopupOpen] = useState(false);
|
|
12383
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
12384
|
+
const [dropdownPosition, setDropdownPosition] = useState(null);
|
|
12385
|
+
const [listPopupPosition, setListPopupPosition] = useState(null);
|
|
12386
|
+
const [mounted, setMounted] = useState(false);
|
|
12387
|
+
const containerRef = useRef(null);
|
|
12388
|
+
const triggerRef = useRef(null);
|
|
12389
|
+
const dropdownRef = useRef(null);
|
|
12390
|
+
const listPopupRef = useRef(null);
|
|
12391
|
+
const moreButtonRef = useRef(null);
|
|
12392
|
+
const searchInputRef = useRef(null);
|
|
12393
|
+
const formatConfig = widgetConfig['widget-data-format'];
|
|
12394
|
+
const sortOptions = formatConfig?.sortOptions ?? false;
|
|
12395
|
+
useEffect(() => {
|
|
12396
|
+
setMounted(true);
|
|
12397
|
+
}, []);
|
|
12398
|
+
const updateDropdownPosition = useCallback(() => {
|
|
12399
|
+
const trigger = triggerRef.current;
|
|
12400
|
+
if (!trigger)
|
|
12401
|
+
return;
|
|
12402
|
+
const rect = trigger.getBoundingClientRect();
|
|
12403
|
+
const gap = 4;
|
|
12404
|
+
const spaceBelow = window.innerHeight - rect.bottom - gap;
|
|
12405
|
+
const spaceAbove = rect.top - gap;
|
|
12406
|
+
const openDown = spaceBelow >= 160 || spaceBelow >= spaceAbove;
|
|
12407
|
+
const availableSpace = openDown ? spaceBelow : spaceAbove;
|
|
12408
|
+
const maxHeight = Math.min(320, Math.max(160, availableSpace - 8));
|
|
12409
|
+
setDropdownPosition(openDown
|
|
12410
|
+
? {
|
|
12411
|
+
top: rect.bottom + gap,
|
|
12412
|
+
left: rect.left,
|
|
12413
|
+
width: rect.width,
|
|
12414
|
+
maxHeight,
|
|
12415
|
+
placement: 'bottom',
|
|
12416
|
+
}
|
|
12417
|
+
: {
|
|
12418
|
+
bottom: window.innerHeight - rect.top + gap,
|
|
12419
|
+
left: rect.left,
|
|
12420
|
+
width: rect.width,
|
|
12421
|
+
maxHeight,
|
|
12422
|
+
placement: 'top',
|
|
12423
|
+
});
|
|
12424
|
+
}, []);
|
|
12425
|
+
const updateListPopupPosition = useCallback(() => {
|
|
12426
|
+
const anchor = moreButtonRef.current;
|
|
12427
|
+
if (!anchor)
|
|
12428
|
+
return;
|
|
12429
|
+
const rect = anchor.getBoundingClientRect();
|
|
12430
|
+
const gap = 4;
|
|
12431
|
+
const spaceBelow = window.innerHeight - rect.bottom - gap;
|
|
12432
|
+
const spaceAbove = rect.top - gap;
|
|
12433
|
+
const openDown = spaceBelow >= 120 || spaceBelow >= spaceAbove;
|
|
12434
|
+
const availableSpace = openDown ? spaceBelow : spaceAbove;
|
|
12435
|
+
const maxHeight = Math.min(280, Math.max(120, availableSpace - 8));
|
|
12436
|
+
setListPopupPosition(openDown
|
|
12437
|
+
? {
|
|
12438
|
+
top: rect.bottom + gap,
|
|
12439
|
+
left: rect.left,
|
|
12440
|
+
width: Math.max(rect.width, 220),
|
|
12441
|
+
maxHeight,
|
|
12442
|
+
placement: 'bottom',
|
|
12443
|
+
}
|
|
12444
|
+
: {
|
|
12445
|
+
bottom: window.innerHeight - rect.top + gap,
|
|
12446
|
+
left: rect.left,
|
|
12447
|
+
width: Math.max(rect.width, 220),
|
|
12448
|
+
maxHeight,
|
|
12449
|
+
placement: 'top',
|
|
12450
|
+
});
|
|
12451
|
+
}, []);
|
|
12452
|
+
useEffect(() => {
|
|
12453
|
+
if (!isOpen) {
|
|
12454
|
+
setDropdownPosition(null);
|
|
12455
|
+
setSearchQuery('');
|
|
12456
|
+
return;
|
|
12457
|
+
}
|
|
12458
|
+
updateDropdownPosition();
|
|
12459
|
+
const handleResize = () => updateDropdownPosition();
|
|
12460
|
+
window.addEventListener('resize', handleResize);
|
|
12461
|
+
return () => {
|
|
12462
|
+
window.removeEventListener('resize', handleResize);
|
|
12463
|
+
};
|
|
12464
|
+
}, [isOpen, updateDropdownPosition]);
|
|
12465
|
+
useEffect(() => {
|
|
12466
|
+
if (!isListPopupOpen) {
|
|
12467
|
+
setListPopupPosition(null);
|
|
12468
|
+
return;
|
|
12469
|
+
}
|
|
12470
|
+
updateListPopupPosition();
|
|
12471
|
+
const handleResize = () => updateListPopupPosition();
|
|
12472
|
+
window.addEventListener('resize', handleResize);
|
|
12473
|
+
return () => {
|
|
12474
|
+
window.removeEventListener('resize', handleResize);
|
|
12475
|
+
};
|
|
12476
|
+
}, [isListPopupOpen, updateListPopupPosition]);
|
|
12477
|
+
useEffect(() => {
|
|
12478
|
+
if (!isOpen && !isListPopupOpen)
|
|
12479
|
+
return;
|
|
12480
|
+
const handleScroll = (event) => {
|
|
12481
|
+
const target = event.target;
|
|
12482
|
+
if (dropdownRef.current?.contains(target))
|
|
12483
|
+
return;
|
|
12484
|
+
if (listPopupRef.current?.contains(target))
|
|
12485
|
+
return;
|
|
12486
|
+
if (isOpen)
|
|
12487
|
+
setIsOpen(false);
|
|
12488
|
+
if (isListPopupOpen)
|
|
12489
|
+
setIsListPopupOpen(false);
|
|
12490
|
+
};
|
|
12491
|
+
window.addEventListener('scroll', handleScroll, true);
|
|
12492
|
+
return () => window.removeEventListener('scroll', handleScroll, true);
|
|
12493
|
+
}, [isOpen, isListPopupOpen]);
|
|
12494
|
+
useEffect(() => {
|
|
12495
|
+
if (!isOpen && !isListPopupOpen)
|
|
12496
|
+
return;
|
|
12497
|
+
const handleClickOutside = (event) => {
|
|
12498
|
+
const target = event.target;
|
|
12499
|
+
if (isOpen) {
|
|
12500
|
+
if (containerRef.current?.contains(target))
|
|
12501
|
+
return;
|
|
12502
|
+
if (dropdownRef.current?.contains(target))
|
|
12503
|
+
return;
|
|
12504
|
+
setIsOpen(false);
|
|
12505
|
+
}
|
|
12506
|
+
if (isListPopupOpen) {
|
|
12507
|
+
if (listPopupRef.current?.contains(target))
|
|
12508
|
+
return;
|
|
12509
|
+
if (moreButtonRef.current?.contains(target))
|
|
12510
|
+
return;
|
|
12511
|
+
setIsListPopupOpen(false);
|
|
12512
|
+
}
|
|
12513
|
+
};
|
|
12514
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
12515
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
12516
|
+
}, [isOpen, isListPopupOpen]);
|
|
12517
|
+
useEffect(() => {
|
|
12518
|
+
if (isOpen && searchInputRef.current) {
|
|
12519
|
+
searchInputRef.current.focus();
|
|
12520
|
+
}
|
|
12521
|
+
}, [isOpen]);
|
|
12522
|
+
const processedOptions = useMemo(() => {
|
|
12523
|
+
let options = dataSourceOptions.map((opt) => {
|
|
12524
|
+
const rawLabel = String(opt.label ?? opt.value ?? '');
|
|
12525
|
+
return {
|
|
12526
|
+
value: opt.value,
|
|
12527
|
+
label: translateConfig(rawLabel),
|
|
12528
|
+
rawLabel,
|
|
12529
|
+
};
|
|
12530
|
+
});
|
|
12531
|
+
if (sortOptions) {
|
|
12532
|
+
options.sort((a, b) => a.label.localeCompare(b.label));
|
|
12533
|
+
}
|
|
12534
|
+
return options;
|
|
12535
|
+
}, [dataSourceOptions, sortOptions, translateConfig]);
|
|
12536
|
+
const filteredOptions = useMemo(() => {
|
|
12537
|
+
if (!searchQuery.trim())
|
|
12538
|
+
return processedOptions;
|
|
12539
|
+
const q = searchQuery.trim().toLowerCase();
|
|
12540
|
+
return processedOptions.filter((opt) => opt.label.toLowerCase().includes(q) ||
|
|
12541
|
+
opt.rawLabel.toLowerCase().includes(q));
|
|
12542
|
+
}, [processedOptions, searchQuery]);
|
|
12543
|
+
const selectedValues = useMemo(() => {
|
|
12544
|
+
if (value === null || value === undefined)
|
|
12545
|
+
return [];
|
|
12546
|
+
if (Array.isArray(value))
|
|
12547
|
+
return value;
|
|
12548
|
+
return [value];
|
|
12549
|
+
}, [value]);
|
|
12550
|
+
const allFilteredSelected = useMemo(() => {
|
|
12551
|
+
if (filteredOptions.length === 0)
|
|
12552
|
+
return false;
|
|
12553
|
+
return filteredOptions.every((opt) => selectedValues.includes(opt.value));
|
|
12554
|
+
}, [filteredOptions, selectedValues]);
|
|
12555
|
+
const handleToggle = useCallback((optionValue, checked) => {
|
|
12556
|
+
if (checked) {
|
|
12557
|
+
onChange([...selectedValues, optionValue]);
|
|
12558
|
+
}
|
|
12559
|
+
else {
|
|
12560
|
+
onChange(selectedValues.filter((v) => v !== optionValue));
|
|
12561
|
+
}
|
|
12562
|
+
}, [selectedValues, onChange]);
|
|
12563
|
+
const handleSelectAll = useCallback(() => {
|
|
12564
|
+
const filteredVals = filteredOptions.map((o) => o.value);
|
|
12565
|
+
const merged = Array.from(new Set([...selectedValues, ...filteredVals]));
|
|
12566
|
+
onChange(merged);
|
|
12567
|
+
}, [filteredOptions, selectedValues, onChange]);
|
|
12568
|
+
const handleClearAll = useCallback(() => {
|
|
12569
|
+
onChange([]);
|
|
12570
|
+
}, [onChange]);
|
|
12571
|
+
const selectedLabels = useMemo(() => {
|
|
12572
|
+
return selectedValues.map((val) => {
|
|
12573
|
+
const opt = processedOptions.find((o) => o.value === val);
|
|
12574
|
+
return opt ? opt.label : translateConfig(String(val));
|
|
12575
|
+
});
|
|
12576
|
+
}, [selectedValues, processedOptions, translateConfig]);
|
|
12577
|
+
const fullSelectionText = selectedLabels.join(', ');
|
|
12578
|
+
const visibleLabels = selectedLabels.slice(0, 5);
|
|
12579
|
+
const overflowCount = Math.max(0, selectedLabels.length - 10);
|
|
12580
|
+
const disabled = !isEnabled || loading || widgetConfig['widget-readonly'];
|
|
12581
|
+
const renderSelectedLabels = (options) => {
|
|
12582
|
+
if (selectedLabels.length === 0)
|
|
12583
|
+
return null;
|
|
12584
|
+
const readonly = options?.readonly ?? false;
|
|
12585
|
+
return (jsxRuntimeExports.jsxs("div", { className: `flex flex-wrap gap-1 ${readonly ? '' : 'mt-1.5'}`, children: [visibleLabels.map((label, index) => (jsxRuntimeExports.jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded-md bg-blue-50 px-2 py-0.5 text-xs text-blue-800", title: label, children: [jsxRuntimeExports.jsx("span", { className: "truncate", children: label }), !readonly && !disabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleToggle(selectedValues[index], false), className: "shrink-0 text-blue-600 hover:text-blue-900 focus:outline-none", "aria-label": translate('common.removeItem', {
|
|
12586
|
+
label,
|
|
12587
|
+
defaultValue: `Remove ${label}`,
|
|
12588
|
+
}), children: "\u00D7" }))] }, `${selectedValues[index]}-${label}`))), overflowCount > 0 && (jsxRuntimeExports.jsx("button", { ref: moreButtonRef, type: "button", onClick: () => setIsListPopupOpen((prev) => !prev), className: "inline-flex items-center rounded-md bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-1 focus:ring-blue-500", children: translate('common.moreSelected', {
|
|
12589
|
+
count: overflowCount,
|
|
12590
|
+
defaultValue: `+${overflowCount} more`,
|
|
12591
|
+
}) }))] }));
|
|
12592
|
+
};
|
|
12593
|
+
const listPopupPanel = isListPopupOpen && listPopupPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: listPopupRef, className: "fixed z-[201] bg-white border border-gray-300 shadow-lg", style: {
|
|
12594
|
+
...(listPopupPosition.placement === 'bottom'
|
|
12595
|
+
? { top: listPopupPosition.top }
|
|
12596
|
+
: { bottom: listPopupPosition.bottom }),
|
|
12597
|
+
left: listPopupPosition.left,
|
|
12598
|
+
width: listPopupPosition.width,
|
|
12599
|
+
maxWidth: '320px',
|
|
12600
|
+
maxHeight: listPopupPosition.maxHeight,
|
|
12601
|
+
borderRadius: '10px',
|
|
12602
|
+
display: 'flex',
|
|
12603
|
+
flexDirection: 'column',
|
|
12604
|
+
}, children: [jsxRuntimeExports.jsx("div", { className: "px-3 py-2 text-xs font-semibold text-gray-500 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: translate('common.allSelected', {
|
|
12605
|
+
count: selectedLabels.length,
|
|
12606
|
+
defaultValue: `All selected (${selectedLabels.length})`,
|
|
12607
|
+
}) }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", children: selectedLabels.map((label, index) => (jsxRuntimeExports.jsx("div", { className: "px-3 py-1.5 text-sm text-gray-700", title: label, children: label }, `${selectedValues[index]}-${label}`))) })] })) : null;
|
|
12608
|
+
if (widgetConfig['widget-readonly']) {
|
|
12609
|
+
const fieldLabel = widgetConfig['widget-label'];
|
|
12610
|
+
return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] MultiSelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [fieldLabel && (jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", label: fieldLabel })), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [selectedLabels.length === 0 ? (jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", children: "-" })) : (renderSelectedLabels({ readonly: true })), mounted && listPopupPanel && typeof document !== 'undefined'
|
|
12611
|
+
? createPortal(listPopupPanel, document.body)
|
|
12612
|
+
: null] })] }));
|
|
12613
|
+
}
|
|
12614
|
+
const optionsMaxHeight = dropdownPosition
|
|
12615
|
+
? Math.min(280, dropdownPosition.maxHeight - 100)
|
|
12616
|
+
: 280;
|
|
12617
|
+
const dropdownPanel = isOpen && dropdownPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: dropdownRef, className: "fixed z-[200] bg-white border border-gray-300 shadow-lg", style: {
|
|
12618
|
+
...(dropdownPosition.placement === 'bottom'
|
|
12619
|
+
? { top: dropdownPosition.top }
|
|
12620
|
+
: { bottom: dropdownPosition.bottom }),
|
|
12621
|
+
left: dropdownPosition.left,
|
|
12622
|
+
width: dropdownPosition.width,
|
|
12623
|
+
maxWidth: '280px',
|
|
12624
|
+
maxHeight: dropdownPosition.maxHeight,
|
|
12625
|
+
borderRadius: '10px',
|
|
12626
|
+
display: 'flex',
|
|
12627
|
+
flexDirection: 'column',
|
|
12628
|
+
}, children: [jsxRuntimeExports.jsx("div", { className: "px-3 pt-2 pb-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", placeholder: translate('common.searchPlaceholder', { defaultValue: 'Search...' }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full h-[28px] px-2 text-sm border border-gray-300 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500", style: { borderRadius: '6px' } }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 py-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: allFilteredSelected ? () => {
|
|
12629
|
+
const filteredVals = new Set(filteredOptions.map((o) => o.value));
|
|
12630
|
+
onChange(selectedValues.filter((v) => !filteredVals.has(v)));
|
|
12631
|
+
} : handleSelectAll, className: "text-xs font-medium text-blue-600 hover:text-blue-800 focus:outline-none", children: allFilteredSelected
|
|
12632
|
+
? translate('common.deselectAll', { defaultValue: 'Deselect All' })
|
|
12633
|
+
: translate('common.selectAll', { defaultValue: 'Select All' }) }), selectedValues.length > 0 && (jsxRuntimeExports.jsx("button", { type: "button", onClick: handleClearAll, className: "text-xs font-medium text-red-500 hover:text-red-700 focus:outline-none", children: translate('common.clearAll', { defaultValue: 'Clear All' }) }))] }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", style: { maxHeight: `${Math.max(80, optionsMaxHeight)}px` }, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 px-3 py-2", children: translate('common.loading') })) : filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-400 px-3 py-2", children: translate('common.noOptionsFound', { defaultValue: 'No options found' }) })) : (filteredOptions.map((option) => {
|
|
12634
|
+
const isChecked = selectedValues.includes(option.value);
|
|
12635
|
+
return (jsxRuntimeExports.jsxs("label", { className: `flex items-center gap-2 px-3 py-1 cursor-pointer hover:bg-blue-50 ${isChecked ? 'bg-blue-50/60' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => handleToggle(option.value, e.target.checked), className: "h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700 leading-normal select-none", children: option.label })] }, option.value));
|
|
12636
|
+
})) })] })) : null;
|
|
12637
|
+
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: widgetConfig['widget-label'] ?? '', required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", ref: containerRef, children: [jsxRuntimeExports.jsxs("button", { ref: triggerRef, type: "button", onClick: () => {
|
|
12638
|
+
if (!disabled)
|
|
12639
|
+
setIsOpen((prev) => !prev);
|
|
12640
|
+
}, onBlur: () => {
|
|
12641
|
+
if (!isOpen)
|
|
12642
|
+
onBlur();
|
|
12643
|
+
}, disabled: disabled, className: `w-full sm:w-[280px] max-w-full h-[30px] px-3 border shadow-sm text-left flex items-center justify-between gap-2 ${(touched && error.length > 0) ||
|
|
12644
|
+
(widgetConfig['widget-required'] && selectedValues.length === 0)
|
|
12645
|
+
? 'border-red-500 focus:ring-red-500 focus:border-red-500'
|
|
12646
|
+
: 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white cursor-pointer'} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500`, style: { borderRadius: '10px' }, title: selectedValues.length > 0
|
|
12647
|
+
? fullSelectionText
|
|
12648
|
+
: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("span", { className: `truncate text-sm ${selectedValues.length === 0 ? 'text-gray-400' : 'text-gray-900'}`, children: selectedLabels.length === 0
|
|
12649
|
+
? translate('common.select', { defaultValue: 'Select...' })
|
|
12650
|
+
: translate('common.selectedCount', {
|
|
12651
|
+
count: selectedLabels.length,
|
|
12652
|
+
defaultValue: `${selectedLabels.length} selected`,
|
|
12653
|
+
}) }), jsxRuntimeExports.jsx("svg", { className: `w-4 h-4 flex-shrink-0 text-gray-500 transition-transform ${isOpen ? 'rotate-180' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), mounted && dropdownPanel && typeof document !== 'undefined'
|
|
12654
|
+
? createPortal(dropdownPanel, document.body)
|
|
12655
|
+
: null, selectedLabels.length > 0 && renderSelectedLabels(), mounted && listPopupPanel && typeof document !== 'undefined'
|
|
12656
|
+
? createPortal(listPopupPanel, document.body)
|
|
12657
|
+
: null, touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') }))] })] }) }));
|
|
12658
|
+
};
|
|
12659
|
+
|
|
11561
12660
|
/**
|
|
11562
12661
|
* Register all default/generic widgets
|
|
11563
12662
|
* This is called automatically when the package is imported
|
|
@@ -11605,6 +12704,10 @@ const registerDefaultWidgets = () => {
|
|
|
11605
12704
|
widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
|
|
11606
12705
|
// ID Authentication widget for OIDC-based foundational ID authentication (view-only + action)
|
|
11607
12706
|
widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
|
|
12707
|
+
// Register lookup widget — searchable popup to select a record from any register
|
|
12708
|
+
widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
|
|
12709
|
+
// Multi-select widget — searchable dropdown with checkbox-style options, select all, and clear all
|
|
12710
|
+
widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
|
|
11608
12711
|
};
|
|
11609
12712
|
// Auto-register on import
|
|
11610
12713
|
registerDefaultWidgets();
|
|
@@ -11667,6 +12770,27 @@ var enTranslations = {
|
|
|
11667
12770
|
"common.sectionSaved": "Saved",
|
|
11668
12771
|
"common.sectionModified": "Modified and not saved",
|
|
11669
12772
|
"common.supportedDocuments": "Supported Documents",
|
|
12773
|
+
"common.searchPlaceholder": "Search...",
|
|
12774
|
+
"common.selectAll": "Select All",
|
|
12775
|
+
"common.deselectAll": "Deselect All",
|
|
12776
|
+
"common.clearAll": "Clear All",
|
|
12777
|
+
"common.noOptionsFound": "No options found",
|
|
12778
|
+
"common.allSelected": "All selected ({{count}})",
|
|
12779
|
+
"common.moreSelected": "+{{count}} more",
|
|
12780
|
+
"common.selectedCount": "{{count}} selected",
|
|
12781
|
+
"common.removeItem": "Remove {{label}}",
|
|
12782
|
+
"common.selectAction": "Select {{label}}",
|
|
12783
|
+
"common.selectTitle": "Select {{label}}",
|
|
12784
|
+
"common.change": "Change",
|
|
12785
|
+
"common.noResults": "No results found",
|
|
12786
|
+
"common.searchHint": "Type and press Enter or click search",
|
|
12787
|
+
"common.record": "{{count}} record",
|
|
12788
|
+
"common.records": "{{count}} records",
|
|
12789
|
+
"common.page": "Page",
|
|
12790
|
+
"common.ofPages": "of {{total}}",
|
|
12791
|
+
"common.pageNumber": "Page number",
|
|
12792
|
+
"common.close": "Close",
|
|
12793
|
+
"common.search": "Search",
|
|
11670
12794
|
"table.addRecord": "Add New Record",
|
|
11671
12795
|
"table.confirm": "Confirm Action",
|
|
11672
12796
|
"table.discard": "Discard & Continue",
|
|
@@ -11907,13 +13031,10 @@ const translateWidgetConfig = (widgetConfig, translate) => {
|
|
|
11907
13031
|
...dataSource,
|
|
11908
13032
|
options: dataSource.options.map((option) => {
|
|
11909
13033
|
if (option.label && typeof option.label === 'string') {
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
label: translate(optionLabel, { defaultValue: optionLabel }),
|
|
11915
|
-
};
|
|
11916
|
-
}
|
|
13034
|
+
return {
|
|
13035
|
+
...option,
|
|
13036
|
+
label: translate(option.label, { defaultValue: option.label }),
|
|
13037
|
+
};
|
|
11917
13038
|
}
|
|
11918
13039
|
return option;
|
|
11919
13040
|
}),
|
|
@@ -11966,5 +13087,5 @@ const translateUISchema = (schema, translate) => {
|
|
|
11966
13087
|
};
|
|
11967
13088
|
};
|
|
11968
13089
|
|
|
11969
|
-
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, GEO_LEVEL_CLEARED, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, applySharedGeoHierarchyToValues, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, initI18n, isAllowedKey, isUpstreamGeoAncestor, normalizeNumericDefault, parseDataPath, parseNumber, registerDefaultWidgets, registerGeoWidgetParent, removeMask, resetAll, resetAndSeedGeoHierarchyFromValues, resetWidget, resolveGeoWidgetLevelValue, resolveTheme, resolveWidgetIdValue, seedGeoHierarchyFromValues, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, unregisterGeoWidgetParent, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
13090
|
+
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, GEO_LEVEL_CLEARED, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, applySharedGeoHierarchyToValues, collectGeoWidgetRegistrationsFromWidgets, createGeoLevelMnemonicResolver, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getCachedApiDataSource, getFormattedNumberLength, getGeoDescendantWidgetIds, getGeoGroupId, getGeoWidgetRegistrationsInGroup, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, initI18n, isAllowedKey, isUpstreamGeoAncestor, normalizeNumericDefault, normalizeOptionRules, orderGeoWidgetRegistrations, parseDataPath, parseNumber, rebuildGeoHierarchyFromRegistrations, reconcileGeoHierarchiesInValues, registerDefaultWidgets, registerGeoWidget, registerGeoWidgetParent, removeMask, resetAll, resetAndSeedGeoHierarchyFromValues, resetWidget, resolveGeoWidgetLevelLabel, resolveGeoWidgetLevelValue, resolveTheme, resolveWidgetIdValue, seedGeoHierarchyFromValues, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, unregisterGeoWidget, unregisterGeoWidgetParent, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
11970
13091
|
//# sourceMappingURL=index.esm.js.map
|