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