@openg2p/registry-widgets 1.1.2-dev.0 → 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.
Files changed (56) hide show
  1. package/dist/assets/index.d.ts +3 -1
  2. package/dist/assets/index.d.ts.map +1 -1
  3. package/dist/components/SectionBuilder/schemas.d.ts +204 -0
  4. package/dist/components/SectionBuilder/schemas.d.ts.map +1 -1
  5. package/dist/components/SectionRenderer.d.ts.map +1 -1
  6. package/dist/components/WidgetFieldLabel.d.ts +11 -0
  7. package/dist/components/WidgetFieldLabel.d.ts.map +1 -0
  8. package/dist/hooks/useBaseWidget.d.ts +2 -0
  9. package/dist/hooks/useBaseWidget.d.ts.map +1 -1
  10. package/dist/hooks/useGeoWidgetCascade.d.ts.map +1 -1
  11. package/dist/index.d.ts +117 -28
  12. package/dist/index.esm.js +2203 -883
  13. package/dist/index.esm.js.map +1 -1
  14. package/dist/index.js +2228 -881
  15. package/dist/index.js.map +1 -1
  16. package/dist/registry/defaultWidgets.d.ts.map +1 -1
  17. package/dist/types/index.d.ts +11 -1
  18. package/dist/types/index.d.ts.map +1 -1
  19. package/dist/utils/conditions.d.ts +17 -11
  20. package/dist/utils/conditions.d.ts.map +1 -1
  21. package/dist/utils/dataSource.d.ts +6 -0
  22. package/dist/utils/dataSource.d.ts.map +1 -1
  23. package/dist/utils/geoHierarchy.d.ts +65 -0
  24. package/dist/utils/geoHierarchy.d.ts.map +1 -1
  25. package/dist/utils/pathUtils.d.ts +5 -0
  26. package/dist/utils/pathUtils.d.ts.map +1 -1
  27. package/dist/utils/schemaNamespace.d.ts.map +1 -1
  28. package/dist/utils/schemaTranslation.d.ts.map +1 -1
  29. package/dist/utils/sectionRevert.d.ts +24 -0
  30. package/dist/utils/sectionRevert.d.ts.map +1 -0
  31. package/dist/utils/sectionValidate.d.ts.map +1 -1
  32. package/dist/widgets/ArrayWidget.d.ts.map +1 -1
  33. package/dist/widgets/BooleanWidget.d.ts.map +1 -1
  34. package/dist/widgets/CheckboxWidget.d.ts.map +1 -1
  35. package/dist/widgets/CurrencyInputWidget.d.ts.map +1 -1
  36. package/dist/widgets/DateInputWidget.d.ts.map +1 -1
  37. package/dist/widgets/DateTimeInputWidget.d.ts.map +1 -1
  38. package/dist/widgets/DialogTableWidget.d.ts +0 -13
  39. package/dist/widgets/DialogTableWidget.d.ts.map +1 -1
  40. package/dist/widgets/FileInputWidget.d.ts.map +1 -1
  41. package/dist/widgets/HeaderSectionWidget.d.ts.map +1 -1
  42. package/dist/widgets/IterableAccordionWidget.d.ts.map +1 -1
  43. package/dist/widgets/MultiSelectWidget.d.ts +7 -0
  44. package/dist/widgets/MultiSelectWidget.d.ts.map +1 -0
  45. package/dist/widgets/NumberInputWidget.d.ts.map +1 -1
  46. package/dist/widgets/PhoneInputWidget.d.ts.map +1 -1
  47. package/dist/widgets/RadioWidget.d.ts.map +1 -1
  48. package/dist/widgets/RegisterLookupWidget.d.ts +5 -0
  49. package/dist/widgets/RegisterLookupWidget.d.ts.map +1 -0
  50. package/dist/widgets/SelectWidget.d.ts.map +1 -1
  51. package/dist/widgets/TableWidget.d.ts.map +1 -1
  52. package/dist/widgets/TextAreaWidget.d.ts.map +1 -1
  53. package/dist/widgets/TextInputWidget.d.ts.map +1 -1
  54. package/dist/widgets/index.d.ts +2 -0
  55. package/dist/widgets/index.d.ts.map +1 -1
  56. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -174,15 +174,33 @@ const parseDataPath = (dataPath) => {
174
174
  /**
175
175
  * Get value from widget state using data path
176
176
  */
177
+ /**
178
+ * Resolve a widget-id reference in Redux values.
179
+ * Supports namespaced ids (e.g. "rv-section-0__region_code" when ref is "region_code").
180
+ */
181
+ const resolveWidgetIdValue = (values, ref) => {
182
+ if (!ref) {
183
+ return undefined;
184
+ }
185
+ if (ref.includes('.')) {
186
+ return getValueByPath(values, ref);
187
+ }
188
+ if (Object.prototype.hasOwnProperty.call(values, ref)) {
189
+ return values[ref];
190
+ }
191
+ const suffix = `__${ref}`;
192
+ for (const [key, val] of Object.entries(values)) {
193
+ if (key.endsWith(suffix)) {
194
+ return val;
195
+ }
196
+ }
197
+ return undefined;
198
+ };
177
199
  const getWidgetValue = (values, dataPath, widgetId) => {
178
200
  if (!dataPath) {
179
201
  // Fallback to widget-id if no data path
180
202
  return values[widgetId];
181
203
  }
182
- if (widgetId == "user-profile") {
183
- console.log('values', values);
184
- console.log('dataPath', dataPath);
185
- }
186
204
  if (typeof dataPath === 'string') {
187
205
  return getValueByPath(values, dataPath);
188
206
  }
@@ -385,6 +403,18 @@ const createZodSchema = (validation, required = false) => {
385
403
  return schema;
386
404
  };
387
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
+ };
388
418
  /**
389
419
  * Evaluate condition against field value
390
420
  */
@@ -393,6 +423,9 @@ const evaluateCondition = (condition, allValues) => {
393
423
  const { operator, value } = condition;
394
424
  switch (operator) {
395
425
  case 'equals':
426
+ if (typeof value === 'boolean' || typeof fieldValue === 'boolean') {
427
+ return normalizeBooleanLike(fieldValue) === normalizeBooleanLike(value);
428
+ }
396
429
  return fieldValue === value;
397
430
  case 'notEquals':
398
431
  return fieldValue !== value;
@@ -425,37 +458,62 @@ const evaluateCondition = (condition, allValues) => {
425
458
  }
426
459
  };
427
460
  /**
428
- * Check if widget should be visible based on conditions
461
+ * Normalize widget-data-options into a sequential list of action rules.
462
+ * Supports legacy single { action, condition } and new { actions: [...] }.
429
463
  */
430
- const shouldShowWidget = (options, allValues) => {
431
- if (!options?.condition) {
432
- return true;
464
+ const normalizeOptionRules = (options) => {
465
+ if (!options) {
466
+ return [];
433
467
  }
434
- const conditionResult = evaluateCondition(options.condition, allValues);
435
- if (options.action === 'show') {
436
- return conditionResult;
468
+ if (Array.isArray(options.actions) && options.actions.length > 0) {
469
+ return options.actions.filter((rule) => !!rule?.action);
437
470
  }
438
- if (options.action === 'hide') {
439
- return !conditionResult;
471
+ if (options.action && options.condition) {
472
+ return [{ action: options.action, condition: options.condition }];
440
473
  }
441
- return true;
474
+ return [];
475
+ };
476
+ const hasVisibilityRules = (options) => {
477
+ return normalizeOptionRules(options).some((rule) => rule.action === 'show' || rule.action === 'hide');
442
478
  };
443
479
  /**
444
- * Check if widget should be enabled based on conditions
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.
445
483
  */
446
- const shouldEnableWidget = (options, allValues) => {
447
- if (!options?.condition) {
448
- return true;
449
- }
450
- const conditionResult = evaluateCondition(options.condition, allValues);
451
- if (options.action === 'enable') {
452
- return conditionResult;
453
- }
454
- if (options.action === 'disable') {
455
- return !conditionResult;
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
+ }
456
511
  }
457
- return true;
512
+ return { visible, enabled, required };
458
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;
459
517
 
460
518
  /**
461
519
  * Format number with thousand and decimal separators
@@ -1025,6 +1083,67 @@ const formatValue = (value, format, widgetType) => {
1025
1083
  return value?.toString() || '';
1026
1084
  };
1027
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
+ }
1028
1147
  /**
1029
1148
  * Get static data source options
1030
1149
  */
@@ -1042,111 +1161,33 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
1042
1161
  return [];
1043
1162
  }
1044
1163
  try {
1045
- // Get dependency value if exists
1046
- // dependsOn can be either a data path (e.g., "person.address") or a widget-id
1047
- let depValue = null;
1048
- if (dataSource.dependsOn) {
1049
- // First try as data path
1050
- depValue = getValueByPath(allValues, dataSource.dependsOn);
1051
- // If not found and doesn't contain dots, try as widget-id
1052
- if ((depValue === null || depValue === undefined) && !dataSource.dependsOn.includes('.')) {
1053
- depValue = allValues[dataSource.dependsOn];
1054
- // Smart resolution: If not found at top level, try to find the dependency in the same nested object
1055
- // by looking for other keys in allValues that might contain the dependency.
1056
- // We look for objects that contain both the current widget's path (if we can guess it) and the dependency.
1057
- // But since we don't know the current widget's path here, we search for any object that has this dependency key.
1058
- if (depValue === null || depValue === undefined || depValue === '') {
1059
- for (const val of Object.values(allValues)) {
1060
- if (val && typeof val === 'object' && !Array.isArray(val) && dataSource.dependsOn in val) {
1061
- depValue = val[dataSource.dependsOn];
1062
- if (depValue !== null && depValue !== undefined && depValue !== '')
1063
- break;
1064
- }
1065
- }
1066
- }
1067
- }
1068
- if (depValue === null || depValue === undefined || depValue === '') {
1069
- // If dependency is empty, return empty array
1070
- return [];
1071
- }
1072
- }
1073
- // Build request parameters
1074
- const method = dataSource.method || 'GET';
1075
- // Extract static params from dataSource
1076
- // Include explicit params object and any additional fields (like level_id)
1077
- const staticParams = { ...dataSource.params };
1078
- // Extract additional fields that aren't part of the standard ApiDataSource interface
1079
- // These are fields like level_id that might be directly on the dataSource
1080
- // BUT: level_id should come from widget-geo-config.level, not from dataSource
1081
- const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
1082
- for (const [key, value] of Object.entries(dataSource)) {
1083
- if (!standardFields.includes(key) && value !== undefined && value !== null) {
1084
- staticParams[key] = value;
1085
- }
1086
- }
1087
- // If levelId is provided (from widget-geo-config.level), use it instead of any level_id in dataSource
1088
- if (levelId) {
1089
- staticParams.level_id = levelId;
1090
- }
1091
- // Build request params object
1092
- const requestParams = { ...staticParams };
1093
- // Add dependency value to params
1094
- if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
1095
- // Extract the actual value ID if depValue is an object
1096
- const parentValueId = typeof depValue === 'object' && depValue !== null
1097
- ? (depValue.level_value_id || depValue.id || depValue.value || depValue)
1098
- : depValue;
1099
- // For geo APIs, use parent_level_value_id
1100
- if (staticParams.level_id) {
1101
- requestParams.parent_level_value_id = parentValueId;
1102
- }
1103
- else {
1104
- // For other APIs, use the dependency field name as param key
1105
- const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
1106
- requestParams[paramKey] = parentValueId;
1107
- }
1108
- }
1109
- else if (staticParams.level_id) {
1110
- // First level has no parent, send empty string as many OpenG2P APIs expect it
1111
- requestParams.parent_level_value_id = "";
1112
- }
1113
- // Get service mnemonic and endpoint (required)
1114
- const service = dataSource.service;
1115
- const endpoint = dataSource.endpoint;
1116
- if (!service) {
1117
- console.error('[getApiDataSource] API data source missing service mnemonic. Use "service" field instead of "url"');
1164
+ const context = buildApiRequestContext(dataSource, allValues, levelId);
1165
+ if (!context) {
1118
1166
  return [];
1119
1167
  }
1120
- if (!endpoint) {
1121
- console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
1122
- return [];
1123
- }
1124
- // Call handler — let any throw propagate to the outer catch so it is logged once
1125
- // by useBaseWidget rather than double-logged here (which can cascade when
1126
- // intercept-console-error.js converts console.error calls into thrown errors).
1127
- const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, {
1128
- headers: dataSource.headers,
1129
- });
1130
- // Handle OpenG2P response format (response_body.response_payload)
1131
- if (response && typeof response === 'object') {
1132
- if (response.response_body?.response_payload && Array.isArray(response.response_body.response_payload)) {
1133
- return response.response_body.response_payload;
1134
- }
1135
- }
1136
- // Handle array response
1137
- if (Array.isArray(response)) {
1138
- return response;
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;
1139
1187
  }
1140
- // Handle object response (extract array from common keys)
1141
- if (response && typeof response === 'object') {
1142
- if (response.data && Array.isArray(response.data)) {
1143
- return response.data;
1144
- }
1145
- if (response.results && Array.isArray(response.results)) {
1146
- return response.results;
1147
- }
1188
+ finally {
1189
+ apiDataSourceInflight.delete(cacheKey);
1148
1190
  }
1149
- return [];
1150
1191
  }
1151
1192
  catch (error) {
1152
1193
  // Rethrow so useBaseWidget's catch can log it with full widget context
@@ -1932,85 +1973,588 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
1932
1973
  return content;
1933
1974
  };
1934
1975
 
1935
- // Define stable empty arrays to avoid selector reference issues
1936
- const EMPTY_ERRORS = [];
1937
- const EMPTY_DATA_SOURCE$1 = [];
1938
- const useBaseWidget = (options) => {
1939
- const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
1940
- const dispatch = reactRedux.useDispatch();
1941
- const context = useWidgetContext();
1942
- const eventBus = useWidgetEventBus();
1943
- const widgetId = config['widget-id'];
1944
- // Fall back to WidgetContext for dataSourceRequestHandler
1945
- const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
1946
- // Get state from Redux
1947
- const values = reactRedux.useSelector((state) => state.widget.values);
1948
- const errors = reactRedux.useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
1949
- const touched = reactRedux.useSelector((state) => state.widget.touched[widgetId] || false);
1950
- const loading = reactRedux.useSelector((state) => state.widget.loading[widgetId] || false);
1951
- const dataSourceOptions = reactRedux.useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE$1);
1952
- // Skip value handling for layout widgets (they don't store data values)
1953
- // Infer layout from widget-type
1954
- const isLayoutWidget = config['widget-type'] === 'layout';
1955
- // Track if user has explicitly set a value to prevent default from overwriting
1956
- const userHasSetValueRef = React.useRef(false);
1957
- // Use ref for values to avoid stale closures in handleChange
1958
- const valuesRef = React.useRef(values);
1959
- const loadingRef = React.useRef(loading);
1960
- const dataSourceOptionsRef = React.useRef(dataSourceOptions);
1961
- React.useEffect(() => {
1962
- valuesRef.current = values;
1963
- loadingRef.current = loading;
1964
- dataSourceOptionsRef.current = dataSourceOptions;
1965
- }, [values, loading, dataSourceOptions]);
1966
- // Track last dispatched value to prevent duplicate dispatches
1967
- const lastDispatchedValueRef = React.useRef(null);
1968
- // Helper to extract displayable value from object (especially geo hierarchy objects)
1969
- const extractValueFromObject = React.useCallback((obj) => {
1970
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
1971
- return obj;
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 || '';
1972
2001
  }
1973
- // Check for geo hierarchy structure first
1974
- const geoConfig = config['widget-geo-config'];
1975
- if (geoConfig) {
1976
- // If we have a geo hierarchy object, extract the value for this specific level
1977
- const hierarchy = obj.hierarchy || obj.geo_code_hierarchy_json?.hierarchy;
1978
- if (Array.isArray(hierarchy)) {
1979
- const levelData = hierarchy.find((l) => l.level === geoConfig.level);
1980
- if (levelData) {
1981
- return levelData.level_value_id;
1982
- }
1983
- }
2002
+ // Use the provided translation function or fallback to the key
2003
+ if (translateFunction) {
2004
+ return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
1984
2005
  }
1985
- if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj || 'hierarchy' in obj) {
1986
- if ('geo_lowest_level_value_id' in obj) {
1987
- return obj.geo_lowest_level_value_id;
1988
- }
1989
- if ('lowest_level_value_id' in obj) {
1990
- return obj.lowest_level_value_id;
1991
- }
1992
- // Fallback for nested geo_code_hierarchy_json
1993
- if (obj.geo_code_hierarchy_json?.lowest_level_value_id) {
1994
- return obj.geo_code_hierarchy_json.lowest_level_value_id;
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;
1995
2036
  }
1996
- if (obj.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
1997
- return obj.geo_code_hierarchy_json.geo_lowest_level_value_id;
2037
+ catch (error) {
2038
+ // If translation throws an error (e.g., missing key warning), return original value
2039
+ return value;
1998
2040
  }
1999
- // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
2000
- return undefined;
2001
2041
  }
2002
- // Try common value fields
2003
- if ('value' in obj) {
2004
- return obj.value;
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
+
2068
+ /**
2069
+ * Geo Hierarchy Builder
2070
+ * Manages geo hierarchy state and builds hierarchy JSON structure
2071
+ */
2072
+ function extractLevelValueFromStored(value, geoConfig) {
2073
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
2074
+ return value;
2075
+ }
2076
+ const hierarchy = value.hierarchy || value.geo_code_hierarchy_json?.hierarchy;
2077
+ if (Array.isArray(hierarchy)) {
2078
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2079
+ if (levelData) {
2080
+ return levelData.level_value_id;
2005
2081
  }
2006
- if ('id' in obj) {
2007
- return obj.id;
2082
+ // Level absent from hierarchy (e.g. cleared by upstream cascade) — do not use lowest-level fallback
2083
+ return undefined;
2084
+ }
2085
+ if ('geo_lowest_level_value_id' in value) {
2086
+ return value.geo_lowest_level_value_id;
2087
+ }
2088
+ if ('lowest_level_value_id' in value) {
2089
+ return value.lowest_level_value_id;
2090
+ }
2091
+ if (value.geo_code_hierarchy_json?.lowest_level_value_id) {
2092
+ return value.geo_code_hierarchy_json.lowest_level_value_id;
2093
+ }
2094
+ if (value.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2095
+ return value.geo_code_hierarchy_json.geo_lowest_level_value_id;
2096
+ }
2097
+ return undefined;
2098
+ }
2099
+ /**
2100
+ * Resolve the display value for a geo level widget.
2101
+ * When widgetId is explicitly set in Redux (including cleared undefined/null), do not
2102
+ * fall back to shared hierarchy dataPath — that stale path was keeping grandchildren visible.
2103
+ */
2104
+ function resolveGeoWidgetLevelValue(values, widgetId, dataPath, geoConfig) {
2105
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
2106
+ let value = values[widgetId];
2107
+ if (value === undefined || value === null || value === '') {
2108
+ return value;
2008
2109
  }
2009
- if ('label' in obj) {
2010
- return obj.label;
2110
+ if (typeof value === 'object' && !Array.isArray(value)) {
2111
+ return extractLevelValueFromStored(value, geoConfig);
2011
2112
  }
2012
- if ('name' in obj) {
2013
- return obj.name;
2113
+ return value;
2114
+ }
2115
+ if (!dataPath) {
2116
+ return undefined;
2117
+ }
2118
+ let value = getWidgetValue(values, dataPath, widgetId);
2119
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
2120
+ value = extractLevelValueFromStored(value, geoConfig);
2121
+ }
2122
+ return value;
2123
+ }
2124
+ /**
2125
+ * Write the in-memory geo hierarchy builder state into Redux at the shared dataPath.
2126
+ */
2127
+ function applySharedGeoHierarchyToValues(baseValues, groupId, dataPath, widgetId) {
2128
+ const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2129
+ if (!dataPath || typeof dataPath !== 'string') {
2130
+ return baseValues;
2131
+ }
2132
+ const inner = hierarchyJson?.geo_code_hierarchy_json;
2133
+ const lowestId = hierarchyJson?.geo_lowest_level_value_id;
2134
+ if (dataPath.endsWith('.geo_code_hierarchy_json')) {
2135
+ const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2136
+ let finalUpdatedValues = setWidgetValue(baseValues, dataPath, widgetId, inner);
2137
+ finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, lowestId);
2138
+ return finalUpdatedValues;
2139
+ }
2140
+ return setWidgetValue(baseValues, dataPath, widgetId, inner);
2141
+ }
2142
+ class GeoHierarchyBuilder {
2143
+ constructor() {
2144
+ this.hierarchies = new Map();
2145
+ }
2146
+ /**
2147
+ * Get or create hierarchy state for a group
2148
+ */
2149
+ getHierarchy(groupId = 'default') {
2150
+ if (!this.hierarchies.has(groupId)) {
2151
+ this.hierarchies.set(groupId, {
2152
+ levels: new Map(),
2153
+ order: [],
2154
+ });
2155
+ }
2156
+ return this.hierarchies.get(groupId);
2157
+ }
2158
+ /**
2159
+ * Add a level to the hierarchy
2160
+ */
2161
+ addLevel(level, level_value_id, level_value_mnemonic, groupId = 'default') {
2162
+ const hierarchy = this.getHierarchy(groupId);
2163
+ // If level already exists, remove it and everything after it
2164
+ const existingIndex = hierarchy.order.indexOf(level);
2165
+ if (existingIndex >= 0) {
2166
+ // Remove this level and all subsequent levels
2167
+ const levelsToRemove = hierarchy.order.slice(existingIndex);
2168
+ levelsToRemove.forEach((l) => {
2169
+ hierarchy.levels.delete(l);
2170
+ hierarchy.order = hierarchy.order.filter((o) => o !== l);
2171
+ });
2172
+ }
2173
+ // Add new level
2174
+ hierarchy.levels.set(level, {
2175
+ level,
2176
+ level_value_id,
2177
+ level_value_mnemonic,
2178
+ });
2179
+ hierarchy.order.push(level);
2180
+ }
2181
+ /**
2182
+ * Remove a level and all levels below it
2183
+ */
2184
+ removeLevelAndBelow(level, groupId = 'default') {
2185
+ const hierarchy = this.getHierarchy(groupId);
2186
+ const index = hierarchy.order.indexOf(level);
2187
+ if (index >= 0) {
2188
+ // Remove this level and all subsequent levels
2189
+ const levelsToRemove = hierarchy.order.slice(index);
2190
+ levelsToRemove.forEach((l) => {
2191
+ hierarchy.levels.delete(l);
2192
+ hierarchy.order = hierarchy.order.filter((o) => o !== l);
2193
+ });
2194
+ }
2195
+ }
2196
+ /**
2197
+ * Build hierarchy JSON structure
2198
+ */
2199
+ buildHierarchyJson(groupId = 'default') {
2200
+ const hierarchy = this.getHierarchy(groupId);
2201
+ if (hierarchy.order.length === 0) {
2202
+ return null;
2203
+ }
2204
+ const hierarchyArray = hierarchy.order.map((level) => {
2205
+ const data = hierarchy.levels.get(level);
2206
+ return {
2207
+ level: data.level,
2208
+ level_value_id: data.level_value_id,
2209
+ level_value_mnemonic: data.level_value_mnemonic,
2210
+ };
2211
+ });
2212
+ const lowestLevel = hierarchy.order[hierarchy.order.length - 1];
2213
+ const lowestLevelData = hierarchy.levels.get(lowestLevel);
2214
+ return {
2215
+ geo_lowest_level_value_id: lowestLevelData.level_value_id,
2216
+ geo_code_hierarchy_json: {
2217
+ hierarchy: hierarchyArray,
2218
+ lowest_level_value_id: lowestLevelData.level_value_id,
2219
+ },
2220
+ };
2221
+ }
2222
+ /**
2223
+ * Clear hierarchy for a group
2224
+ */
2225
+ clear(groupId = 'default') {
2226
+ this.hierarchies.delete(groupId);
2227
+ }
2228
+ /**
2229
+ * Clear all hierarchies
2230
+ */
2231
+ clearAll() {
2232
+ this.hierarchies.clear();
2233
+ }
2234
+ /**
2235
+ * Get current levels for a group
2236
+ */
2237
+ getLevels(groupId = 'default') {
2238
+ const hierarchy = this.getHierarchy(groupId);
2239
+ return hierarchy.order.map((level) => hierarchy.levels.get(level));
2240
+ }
2241
+ }
2242
+ // Singleton instance
2243
+ const geoHierarchyBuilder = new GeoHierarchyBuilder();
2244
+ /** Sentinel written to Redux when a geo level is cleared by cascade (distinct from "never set"). */
2245
+ const GEO_LEVEL_CLEARED = null;
2246
+ const geoWidgetParentRegistry = new Map();
2247
+ const geoWidgetConfigRegistry = new Map();
2248
+ function registerGeoWidgetParent(widgetId, parentWidgetId) {
2249
+ geoWidgetParentRegistry.set(widgetId, parentWidgetId || null);
2250
+ }
2251
+ function unregisterGeoWidgetParent(widgetId) {
2252
+ geoWidgetParentRegistry.delete(widgetId);
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
+ }
2386
+ /** True when changedWidgetId is any upstream geo parent of widgetId (not only immediate parent). */
2387
+ function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetId) {
2388
+ if (changedWidgetId === widgetId) {
2389
+ return false;
2390
+ }
2391
+ let cursor = immediateParentWidgetId;
2392
+ while (cursor) {
2393
+ if (cursor === changedWidgetId) {
2394
+ return true;
2395
+ }
2396
+ cursor = geoWidgetParentRegistry.get(cursor) ?? null;
2397
+ }
2398
+ return false;
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
+ }
2436
+ function readStoredHierarchyLevels(values, dataPath, widgetId) {
2437
+ const stored = getWidgetValue(values, dataPath, widgetId);
2438
+ const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
2439
+ if (!Array.isArray(hierarchy)) {
2440
+ return [];
2441
+ }
2442
+ return hierarchy.filter((entry) => entry?.level && entry.level_value_id);
2443
+ }
2444
+ function builderMatchesStored(groupId, storedLevels) {
2445
+ const builderLevels = geoHierarchyBuilder.getLevels(groupId);
2446
+ if (builderLevels.length !== storedLevels.length) {
2447
+ return false;
2448
+ }
2449
+ return storedLevels.every((stored, index) => {
2450
+ const built = builderLevels[index];
2451
+ return (built.level === stored.level &&
2452
+ String(built.level_value_id) === String(stored.level_value_id));
2453
+ });
2454
+ }
2455
+ /** Seed in-memory builder from persisted hierarchy JSON (edit-mode rehydration). */
2456
+ function seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId) {
2457
+ if (!dataPath || typeof dataPath !== 'string') {
2458
+ return;
2459
+ }
2460
+ const storedLevels = readStoredHierarchyLevels(values, dataPath, widgetId);
2461
+ if (storedLevels.length === 0) {
2462
+ return;
2463
+ }
2464
+ if (builderMatchesStored(groupId, storedLevels)) {
2465
+ return;
2466
+ }
2467
+ geoHierarchyBuilder.clear(groupId);
2468
+ storedLevels.forEach((entry) => {
2469
+ geoHierarchyBuilder.addLevel(entry.level, String(entry.level_value_id), entry.level_value_mnemonic || String(entry.level_value_id), groupId);
2470
+ });
2471
+ }
2472
+ /** Force-clear builder for a group, then seed from Redux/schema values (e.g. after Cancel). */
2473
+ function resetAndSeedGeoHierarchyFromValues(values, dataPath, widgetId, groupId) {
2474
+ geoHierarchyBuilder.clear(groupId);
2475
+ seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId);
2476
+ }
2477
+
2478
+ // Define stable empty arrays to avoid selector reference issues
2479
+ const EMPTY_ERRORS = [];
2480
+ const EMPTY_DATA_SOURCE = [];
2481
+ const useBaseWidget = (options) => {
2482
+ const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
2483
+ const dispatch = reactRedux.useDispatch();
2484
+ const context = useWidgetContext();
2485
+ const eventBus = useWidgetEventBus();
2486
+ const { translateConfig } = useWidgetTranslation();
2487
+ const widgetId = config['widget-id'];
2488
+ // Fall back to WidgetContext for dataSourceRequestHandler
2489
+ const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
2490
+ // Get state from Redux
2491
+ const values = reactRedux.useSelector((state) => state.widget.values);
2492
+ const errors = reactRedux.useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
2493
+ const touched = reactRedux.useSelector((state) => state.widget.touched[widgetId] || false);
2494
+ const loading = reactRedux.useSelector((state) => state.widget.loading[widgetId] || false);
2495
+ const dataSourceOptions = reactRedux.useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
2496
+ // Skip value handling for layout widgets (they don't store data values)
2497
+ // Infer layout from widget-type
2498
+ const isLayoutWidget = config['widget-type'] === 'layout';
2499
+ // Track if user has explicitly set a value to prevent default from overwriting
2500
+ const userHasSetValueRef = React.useRef(false);
2501
+ // Use ref for values to avoid stale closures in handleChange
2502
+ const valuesRef = React.useRef(values);
2503
+ const loadingRef = React.useRef(loading);
2504
+ const dataSourceOptionsRef = React.useRef(dataSourceOptions);
2505
+ React.useEffect(() => {
2506
+ valuesRef.current = values;
2507
+ loadingRef.current = loading;
2508
+ dataSourceOptionsRef.current = dataSourceOptions;
2509
+ }, [values, loading, dataSourceOptions]);
2510
+ // Track last dispatched value to prevent duplicate dispatches
2511
+ const lastDispatchedValueRef = React.useRef(null);
2512
+ // Helper to extract displayable value from object (especially geo hierarchy objects)
2513
+ const extractValueFromObject = React.useCallback((obj) => {
2514
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
2515
+ return obj;
2516
+ }
2517
+ // Check for geo hierarchy structure first
2518
+ const geoConfig = config['widget-geo-config'];
2519
+ if (geoConfig) {
2520
+ // If we have a geo hierarchy object, extract the value for this specific level
2521
+ const hierarchy = obj.hierarchy || obj.geo_code_hierarchy_json?.hierarchy;
2522
+ if (Array.isArray(hierarchy)) {
2523
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2524
+ if (levelData) {
2525
+ return levelData.level_value_id;
2526
+ }
2527
+ }
2528
+ }
2529
+ if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj || 'hierarchy' in obj) {
2530
+ if ('geo_lowest_level_value_id' in obj) {
2531
+ return obj.geo_lowest_level_value_id;
2532
+ }
2533
+ if ('lowest_level_value_id' in obj) {
2534
+ return obj.lowest_level_value_id;
2535
+ }
2536
+ // Fallback for nested geo_code_hierarchy_json
2537
+ if (obj.geo_code_hierarchy_json?.lowest_level_value_id) {
2538
+ return obj.geo_code_hierarchy_json.lowest_level_value_id;
2539
+ }
2540
+ if (obj.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2541
+ return obj.geo_code_hierarchy_json.geo_lowest_level_value_id;
2542
+ }
2543
+ // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
2544
+ return undefined;
2545
+ }
2546
+ // Try common value fields
2547
+ if ('value' in obj) {
2548
+ return obj.value;
2549
+ }
2550
+ if ('id' in obj) {
2551
+ return obj.id;
2552
+ }
2553
+ if ('label' in obj) {
2554
+ return obj.label;
2555
+ }
2556
+ if ('name' in obj) {
2557
+ return obj.name;
2014
2558
  }
2015
2559
  // If no extractable value found, return undefined to avoid rendering object as React child
2016
2560
  // This prevents "Objects are not valid as a React child" errors
@@ -2021,6 +2565,17 @@ const useBaseWidget = (options) => {
2021
2565
  if (isLayoutWidget) {
2022
2566
  return undefined; // Layout widgets don't have values
2023
2567
  }
2568
+ const geoConfig = config['widget-geo-config'];
2569
+ if (geoConfig) {
2570
+ const value = resolveGeoWidgetLevelValue(values, widgetId, config['widget-data-path'], geoConfig);
2571
+ if (userHasSetValueRef.current) {
2572
+ return value;
2573
+ }
2574
+ if (value === null) {
2575
+ return null;
2576
+ }
2577
+ return value !== undefined ? value : config['widget-data-default'];
2578
+ }
2024
2579
  // Try to get value from widgetId first (this should have the actual selected value)
2025
2580
  // For geo widgets with dataPath, widgetId stores the actual ID, while dataPath stores the hierarchy object
2026
2581
  let value = values[widgetId];
@@ -2099,6 +2654,15 @@ const useBaseWidget = (options) => {
2099
2654
  }
2100
2655
  // eslint-disable-next-line react-hooks/exhaustive-deps
2101
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]);
2102
2666
  // Handle value change
2103
2667
  // CRITICAL: Don't include 'values' in dependency array - it causes the callback to be recreated
2104
2668
  // every time values change, which can lead to stale closures and double dispatches
@@ -2115,13 +2679,16 @@ const useBaseWidget = (options) => {
2115
2679
  // This prevents data disappearance when switching to Edit mode and components
2116
2680
  // incorrectly clear values before options load or if handler is temporarily missing.
2117
2681
  if (newValue === '' || newValue === null || newValue === undefined) {
2118
- if (loadingRef.current) {
2119
- console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
2120
- return;
2121
- }
2122
- if (config['widget-data-source']?.type === 'api' && dataSourceOptionsRef.current.length === 0) {
2123
- console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because API options are empty`);
2124
- return;
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
+ }
2125
2692
  }
2126
2693
  }
2127
2694
  // Mark that user has set a value (unless this is the default initialization)
@@ -2139,29 +2706,26 @@ const useBaseWidget = (options) => {
2139
2706
  lastDispatchedValueRef.current = newValue;
2140
2707
  dispatch(setValue({ widgetId, value: newValue }));
2141
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
+ }
2142
2717
  else {
2143
- // Has dataPath: update both widgetId and dataPath
2144
- // CRITICAL: For geo widgets, we do NOT want to overwrite the shared hierarchy dataPath
2145
- // with a primitive value (the selected ID). The hierarchy object is managed by useGeoWidgetCascade.
2146
- if (config['widget-geo-config']) {
2147
- dispatch(setValue({ widgetId, value: newValue }));
2148
- return;
2149
- }
2150
- // For non-geo widgets, update both widgetId and dataPath
2151
- // CRITICAL: Create updated values object with newValue already set
2152
- // This prevents setWidgetValue from reading stale values
2718
+ // Non-geo widgets: update both widgetId and dataPath
2153
2719
  const currentValuesWithUpdate = {
2154
2720
  ...valuesRef.current,
2155
- [widgetId]: newValue, // Ensure widgetId has the new value
2721
+ [widgetId]: newValue,
2156
2722
  };
2157
2723
  const updatedValues = setWidgetValue(currentValuesWithUpdate, config['widget-data-path'], widgetId, newValue);
2158
- // setWidgetValue returns the complete updated structure with all existing data preserved
2159
- // Use setValues to update the entire state with deep merge
2160
2724
  dispatch(setValues(updatedValues));
2161
2725
  }
2162
2726
  // Validate if needed
2163
2727
  if (validate) {
2164
- const validationErrors = validateWidget(newValue, config['widget-data-validation'], config['widget-required']);
2728
+ const validationErrors = validateWidget(newValue, config['widget-data-validation'], resolveIsRequired(currentValues));
2165
2729
  dispatch(setError({ widgetId, errors: validationErrors }));
2166
2730
  }
2167
2731
  // Call custom onChange if provided
@@ -2180,13 +2744,12 @@ const useBaseWidget = (options) => {
2180
2744
  timestamp: Date.now(),
2181
2745
  });
2182
2746
  }
2183
- }, [config, widgetId, dispatch, onValueChange, eventBus] // Removed 'values' to prevent stale closures
2747
+ }, [config, widgetId, dispatch, onValueChange, eventBus, resolveIsRequired] // Removed 'values' to prevent stale closures
2184
2748
  );
2185
2749
  // Handle blur
2186
2750
  const handleBlur = React.useCallback(() => {
2187
2751
  dispatch(setTouched({ widgetId, touched: true }));
2188
- // Validate on blur
2189
- const validationErrors = validateWidget(currentValue, config['widget-data-validation'], config['widget-required']);
2752
+ const validationErrors = validateWidget(currentValue, config['widget-data-validation'], resolveIsRequired(valuesRef.current));
2190
2753
  dispatch(setError({ widgetId, errors: validationErrors }));
2191
2754
  // Publish widget:blur event
2192
2755
  if (eventBus) {
@@ -2197,7 +2760,7 @@ const useBaseWidget = (options) => {
2197
2760
  timestamp: Date.now(),
2198
2761
  });
2199
2762
  }
2200
- }, [currentValue, config, widgetId, dispatch, eventBus]);
2763
+ }, [currentValue, config, widgetId, dispatch, eventBus, resolveIsRequired]);
2201
2764
  // Get field value helper
2202
2765
  const getFieldValue = React.useCallback((path) => {
2203
2766
  return getWidgetValue(values, path, '');
@@ -2205,7 +2768,7 @@ const useBaseWidget = (options) => {
2205
2768
  // Conditional visibility and enablement
2206
2769
  const isVisible = React.useMemo(() => {
2207
2770
  // Layout widgets are always visible unless explicitly hidden
2208
- if (isLayoutWidget && !config['widget-data-options']?.condition) {
2771
+ if (isLayoutWidget && !hasVisibilityRules(config['widget-data-options'])) {
2209
2772
  return true;
2210
2773
  }
2211
2774
  return shouldShowWidget(config['widget-data-options'], values);
@@ -2220,6 +2783,7 @@ const useBaseWidget = (options) => {
2220
2783
  }
2221
2784
  return shouldEnableWidget(config['widget-data-options'], values);
2222
2785
  }, [config['widget-readonly'], config['widget-data-options'], values, isLayoutWidget]);
2786
+ const isRequired = React.useMemo(() => resolveIsRequired(values), [resolveIsRequired, values]);
2223
2787
  // Format value for display
2224
2788
  const formattedValue = React.useMemo(() => {
2225
2789
  if (!config['widget-data-format']) {
@@ -2230,6 +2794,14 @@ const useBaseWidget = (options) => {
2230
2794
  // Track readonly state explicitly to detect changes
2231
2795
  // Use JSON.stringify to create a stable reference for the dependency array
2232
2796
  const isReadonly = config['widget-readonly'] ?? false;
2797
+ // Leaving edit mode (Cancel): allow mirror/rehydration on next Edit
2798
+ React.useEffect(() => {
2799
+ if (config['widget-readonly']) {
2800
+ userHasSetValueRef.current = false;
2801
+ lastMirroredValueRef.current = null;
2802
+ lastDispatchedValueRef.current = null;
2803
+ }
2804
+ }, [config['widget-readonly']]);
2233
2805
  const dataSource = config['widget-data-source'];
2234
2806
  const geoConfig = config['widget-geo-config'];
2235
2807
  // Use ref to store handler to avoid stale closures
@@ -2242,6 +2814,8 @@ const useBaseWidget = (options) => {
2242
2814
  const apiService = dataSource?.type === 'api' ? dataSource.service : '';
2243
2815
  const apiEndpoint = dataSource?.type === 'api' ? dataSource.endpoint : '';
2244
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]);
2245
2819
  // Extract dependency value using a granular selector to prevent unnecessary re-renders
2246
2820
  // and infinite loops when other unrelated values in the state change.
2247
2821
  const dependencyValue = reactRedux.useSelector((state) => {
@@ -2258,10 +2832,9 @@ const useBaseWidget = (options) => {
2258
2832
  if (!dataSource) {
2259
2833
  return;
2260
2834
  }
2261
- // For API data sources, check if widget is readonly
2262
- // According to PRD: "Level 1 geo widgets load on widget mount or when entering edit mode"
2263
- // So we should only load API data sources when widget is NOT readonly
2264
- 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) {
2265
2838
  return;
2266
2839
  }
2267
2840
  // For widgets with dependencies, check if dependency value exists
@@ -2303,6 +2876,30 @@ const useBaseWidget = (options) => {
2303
2876
  // React will call this effect again when the handler is ready
2304
2877
  return;
2305
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
+ }
2306
2903
  dispatch(setLoading({ widgetId, loading: true }));
2307
2904
  let data = [];
2308
2905
  if (dataSource.type === 'static') {
@@ -2315,31 +2912,13 @@ const useBaseWidget = (options) => {
2315
2912
  dispatch(setDataSource({ widgetId, data: [] }));
2316
2913
  return;
2317
2914
  }
2318
- // Extract level_id from widget-geo-config.level if available
2319
2915
  const levelId = geoConfig?.level;
2320
2916
  data = await getApiDataSource(dataSource, valuesRef.current, currentHandler, levelId);
2321
2917
  }
2322
2918
  else if (dataSource.type === 'schema') {
2323
2919
  data = getSchemaDataSource(dataSource, schemaData || {});
2324
2920
  }
2325
- // Transform to { value, label } format
2326
- // For geo widgets, default to level_value_id and level_value_mnemonic
2327
- let valueKey;
2328
- let labelKey;
2329
- if (dataSource.type === 'static') {
2330
- valueKey = undefined;
2331
- labelKey = undefined;
2332
- }
2333
- else if (geoConfig) {
2334
- // Geo widgets: default to level_value_id and level_value_mnemonic
2335
- valueKey = dataSource.valueKey || 'level_value_id';
2336
- labelKey = dataSource.labelKey || 'level_value_mnemonic';
2337
- }
2338
- else {
2339
- // Non-geo widgets: use specified keys or undefined
2340
- valueKey = dataSource.valueKey;
2341
- labelKey = dataSource.labelKey;
2342
- }
2921
+ const { valueKey, labelKey } = resolveOptionKeys();
2343
2922
  const transformed = transformDataSourceOptions(data, valueKey, labelKey);
2344
2923
  dispatch(setDataSource({ widgetId, data: transformed }));
2345
2924
  }
@@ -2354,16 +2933,25 @@ const useBaseWidget = (options) => {
2354
2933
  loadDataSource();
2355
2934
  // Use configKey and dependencyValue to ensure effect runs only when relevant state changes
2356
2935
  // eslint-disable-next-line react-hooks/exhaustive-deps
2357
- }, [configKey, dependencyValue, dataSourceRequestHandler, schemaData, widgetId, dispatch]);
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]);
2358
2944
  return {
2359
2945
  widgetId,
2360
2946
  value: currentValue,
2947
+ geoDisplayLabel,
2361
2948
  formattedValue,
2362
2949
  error: errors,
2363
2950
  touched,
2364
2951
  loading,
2365
2952
  isVisible,
2366
2953
  isEnabled,
2954
+ isRequired,
2367
2955
  onChange: handleChange,
2368
2956
  onBlur: handleBlur,
2369
2957
  setError: (errors) => dispatch(setError({ widgetId, errors })),
@@ -2434,115 +3022,6 @@ const useWidgetCascade = (options) => {
2434
3022
  }, [cascadeConfig, eventBus, dataSource, widgetId, dispatch]);
2435
3023
  };
2436
3024
 
2437
- /**
2438
- * Geo Hierarchy Builder
2439
- * Manages geo hierarchy state and builds hierarchy JSON structure
2440
- */
2441
- class GeoHierarchyBuilder {
2442
- constructor() {
2443
- this.hierarchies = new Map();
2444
- }
2445
- /**
2446
- * Get or create hierarchy state for a group
2447
- */
2448
- getHierarchy(groupId = 'default') {
2449
- if (!this.hierarchies.has(groupId)) {
2450
- this.hierarchies.set(groupId, {
2451
- levels: new Map(),
2452
- order: [],
2453
- });
2454
- }
2455
- return this.hierarchies.get(groupId);
2456
- }
2457
- /**
2458
- * Add a level to the hierarchy
2459
- */
2460
- addLevel(level, level_value_id, level_value_mnemonic, groupId = 'default') {
2461
- const hierarchy = this.getHierarchy(groupId);
2462
- // If level already exists, remove it and everything after it
2463
- const existingIndex = hierarchy.order.indexOf(level);
2464
- if (existingIndex >= 0) {
2465
- // Remove this level and all subsequent levels
2466
- const levelsToRemove = hierarchy.order.slice(existingIndex);
2467
- levelsToRemove.forEach((l) => {
2468
- hierarchy.levels.delete(l);
2469
- hierarchy.order = hierarchy.order.filter((o) => o !== l);
2470
- });
2471
- }
2472
- // Add new level
2473
- hierarchy.levels.set(level, {
2474
- level,
2475
- level_value_id,
2476
- level_value_mnemonic,
2477
- });
2478
- hierarchy.order.push(level);
2479
- }
2480
- /**
2481
- * Remove a level and all levels below it
2482
- */
2483
- removeLevelAndBelow(level, groupId = 'default') {
2484
- const hierarchy = this.getHierarchy(groupId);
2485
- const index = hierarchy.order.indexOf(level);
2486
- if (index >= 0) {
2487
- // Remove this level and all subsequent levels
2488
- const levelsToRemove = hierarchy.order.slice(index);
2489
- levelsToRemove.forEach((l) => {
2490
- hierarchy.levels.delete(l);
2491
- hierarchy.order = hierarchy.order.filter((o) => o !== l);
2492
- });
2493
- }
2494
- }
2495
- /**
2496
- * Build hierarchy JSON structure
2497
- */
2498
- buildHierarchyJson(groupId = 'default') {
2499
- const hierarchy = this.getHierarchy(groupId);
2500
- if (hierarchy.order.length === 0) {
2501
- return null;
2502
- }
2503
- const hierarchyArray = hierarchy.order.map((level) => {
2504
- const data = hierarchy.levels.get(level);
2505
- return {
2506
- level: data.level,
2507
- level_value_id: data.level_value_id,
2508
- level_value_mnemonic: data.level_value_mnemonic,
2509
- };
2510
- });
2511
- const lowestLevel = hierarchy.order[hierarchy.order.length - 1];
2512
- const lowestLevelData = hierarchy.levels.get(lowestLevel);
2513
- return {
2514
- geo_lowest_level_value_id: lowestLevelData.level_value_id,
2515
- geo_code_hierarchy_json: {
2516
- hierarchy: hierarchyArray,
2517
- lowest_level_value_id: lowestLevelData.level_value_id,
2518
- },
2519
- };
2520
- }
2521
- /**
2522
- * Clear hierarchy for a group
2523
- */
2524
- clear(groupId = 'default') {
2525
- this.hierarchies.delete(groupId);
2526
- }
2527
- /**
2528
- * Clear all hierarchies
2529
- */
2530
- clearAll() {
2531
- this.hierarchies.clear();
2532
- }
2533
- /**
2534
- * Get current levels for a group
2535
- */
2536
- getLevels(groupId = 'default') {
2537
- const hierarchy = this.getHierarchy(groupId);
2538
- return hierarchy.order.map((level) => hierarchy.levels.get(level));
2539
- }
2540
- }
2541
- // Singleton instance
2542
- const geoHierarchyBuilder = new GeoHierarchyBuilder();
2543
-
2544
- // Define stable empty array to avoid selector reference issues
2545
- const EMPTY_DATA_SOURCE = [];
2546
3025
  /**
2547
3026
  * Hook for geo widget cascade functionality
2548
3027
  * Handles geo hierarchy building and cascade behavior
@@ -2560,236 +3039,151 @@ const useGeoWidgetCascade = (options) => {
2560
3039
  : 'default';
2561
3040
  const valuesRef = React.useRef(values);
2562
3041
  const handlerRef = React.useRef(dataSourceRequestHandler);
3042
+ const lastCascadePublishRef = React.useRef(undefined);
3043
+ const lastDirectParentValueRef = React.useRef(undefined);
2563
3044
  // Keep refs updated
2564
3045
  React.useEffect(() => {
2565
3046
  valuesRef.current = values;
2566
3047
  handlerRef.current = dataSourceRequestHandler;
2567
3048
  }, [values, dataSourceRequestHandler]);
2568
3049
  // Get current value and data source options
2569
- const currentValue = reactRedux.useSelector((state) => {
2570
- // Try to get value from widgetId first (most recent selection)
2571
- let value = state.widget.values[widgetId];
2572
- // If not found in widgetId, try dataPath
2573
- if (value === undefined && dataPath) {
2574
- value = getWidgetValue(state.widget.values, dataPath, widgetId);
2575
- }
2576
- // Extract value if it's a geo hierarchy object
2577
- if (value && typeof value === 'object' && !Array.isArray(value) && geoConfig) {
2578
- const hierarchy = value.hierarchy || value.geo_code_hierarchy_json?.hierarchy;
2579
- if (Array.isArray(hierarchy)) {
2580
- const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2581
- if (levelData) {
2582
- return levelData.level_value_id;
2583
- }
2584
- }
2585
- // Extended fallbacks (matching useBaseWidget)
2586
- if ('geo_lowest_level_value_id' in value) {
2587
- return value.geo_lowest_level_value_id;
2588
- }
2589
- if ('lowest_level_value_id' in value) {
2590
- return value.lowest_level_value_id;
2591
- }
2592
- if (value.geo_code_hierarchy_json?.lowest_level_value_id) {
2593
- return value.geo_code_hierarchy_json.lowest_level_value_id;
2594
- }
2595
- if (value.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2596
- return value.geo_code_hierarchy_json.geo_lowest_level_value_id;
2597
- }
2598
- }
2599
- return value;
2600
- });
3050
+ const currentValue = reactRedux.useSelector((state) => geoConfig
3051
+ ? resolveGeoWidgetLevelValue(state.widget.values, widgetId, dataPath, geoConfig)
3052
+ : state.widget.values[widgetId]);
2601
3053
  // Memoize selector to avoid returning new array reference
2602
- const dataSourceOptions = reactRedux.useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
3054
+ const allDataSources = reactRedux.useSelector((state) => state.widget.dataSources);
3055
+ // Register parent chain for upstream-ancestor detection (grandparent → grandchild reset)
3056
+ React.useEffect(() => {
3057
+ if (!geoConfig || typeof dataPath !== 'string') {
3058
+ return;
3059
+ }
3060
+ registerGeoWidget(widgetId, geoConfig, dataPath);
3061
+ return () => unregisterGeoWidget(widgetId);
3062
+ }, [widgetId, geoConfig, dataPath]);
3063
+ // Keep in-memory builder in sync with persisted hierarchy (reload, cancel→re-edit, etc.)
3064
+ React.useEffect(() => {
3065
+ if (!geoConfig || typeof dataPath !== 'string') {
3066
+ return;
3067
+ }
3068
+ seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId);
3069
+ }, [geoConfig, dataPath, widgetId, groupId, values]);
2603
3070
  React.useEffect(() => {
2604
3071
  if (!geoConfig || !eventBus || !dataSource || dataSource.type !== 'api') {
2605
3072
  return;
2606
3073
  }
2607
- const { level, isLastLevel, parentWidgetId } = geoConfig;
2608
- // Listen to parent widget changes
2609
- if (parentWidgetId) {
2610
- const handleParentChange = async (event) => {
2611
- if (event.widgetId !== parentWidgetId) {
2612
- return;
2613
- }
2614
- // CRITICAL: Use a small delay to ensure Redux state has been updated
2615
- // This prevents reading stale values from valuesRef
2616
- await new Promise(resolve => setTimeout(resolve, 0));
2617
- const currentValues = valuesRef.current;
2618
- const currentHandler = handlerRef.current;
2619
- // CRITICAL: Try to get parent value from event first, then from Redux
2620
- let parentValue = event.value;
2621
- if (parentValue === undefined || parentValue === null) {
2622
- parentValue = currentValues[parentWidgetId];
2623
- // If not found in top-level values, try to find it via dataPath or dependsOn
2624
- if (parentValue === undefined && dataSource.dependsOn) {
2625
- parentValue = getWidgetValue(currentValues, dataSource.dependsOn, '');
2626
- }
2627
- }
2628
- // Remove this level and all below from hierarchy
2629
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2630
- // Clear this widget's value
2631
- // CRITICAL: Only dispatch setValue for THIS widget, not for parent or other widgets
2632
- // setWidgetValue returns the entire updated state, but we only want to update this widget
2633
- if (dataPath) {
2634
- const updatedValues = setWidgetValue(currentValues, dataPath, widgetId, undefined);
2635
- // Only dispatch setValue for this widget's widgetId, not for parent or other widgets
2636
- // This prevents accidentally overwriting the parent widget's value
2637
- // The setWidgetValue function updates the nested structure, but we only want to
2638
- // update the top-level widgetId key, not other keys that might be in updatedValues
2639
- const newWidgetValue = updatedValues[widgetId];
2640
- if (newWidgetValue !== undefined) {
2641
- dispatch(setValue({ widgetId, value: newWidgetValue }));
2642
- }
2643
- else {
2644
- // If widgetId is not in updatedValues, the value was set in a nested path
2645
- // In this case, we need to use setValues to update the entire structure
2646
- // But we need to be careful not to overwrite the parent widget's value
2647
- // Only update keys that are related to this widget's dataPath
2648
- const dataPathStr = typeof dataPath === 'string' ? dataPath : '';
2649
- if (dataPathStr && !dataPathStr.startsWith(parentWidgetId + '.')) {
2650
- // Only update if dataPath doesn't start with parentWidgetId
2651
- // This ensures we don't accidentally overwrite the parent widget's value
2652
- dispatch(setValue({ widgetId, value: undefined }));
2653
- }
2654
- }
2655
- }
2656
- else {
2657
- dispatch(setValue({ widgetId, value: undefined }));
3074
+ const { level, isLastLevel, parentWidgetId: rawParentWidgetId } = geoConfig;
3075
+ const parentWidgetId = rawParentWidgetId || null;
3076
+ if (!parentWidgetId) {
3077
+ return;
3078
+ }
3079
+ const clearThisLevel = (baseValues) => {
3080
+ geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
3081
+ if (dataPath) {
3082
+ dispatch(setValues(applySharedGeoHierarchyToValues(baseValues, groupId, dataPath, widgetId)));
3083
+ }
3084
+ dispatch(setValue({ widgetId, value: GEO_LEVEL_CLEARED }));
3085
+ if (!isLastLevel) {
3086
+ eventBus.publish({
3087
+ type: 'widget:change',
3088
+ widgetId,
3089
+ value: GEO_LEVEL_CLEARED,
3090
+ timestamp: Date.now(),
3091
+ });
3092
+ }
3093
+ dispatch(setDataSource({ widgetId, data: [] }));
3094
+ };
3095
+ const handleParentChange = async (event) => {
3096
+ const isDirectParent = event.widgetId === parentWidgetId;
3097
+ const isAncestor = isUpstreamGeoAncestor(event.widgetId, widgetId, parentWidgetId);
3098
+ if (!isDirectParent && !isAncestor) {
3099
+ return;
3100
+ }
3101
+ await new Promise(resolve => setTimeout(resolve, 0));
3102
+ const currentValues = valuesRef.current;
3103
+ const currentHandler = handlerRef.current;
3104
+ // Grandparent (or higher) changed: clear this level; only immediate parent drives reload
3105
+ if (isAncestor && !isDirectParent) {
3106
+ clearThisLevel(currentValues);
3107
+ return;
3108
+ }
3109
+ const parentCleared = event.value === undefined ||
3110
+ event.value === null ||
3111
+ event.value === '' ||
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
+ }
3120
+ let parentValue = event.value;
3121
+ if (!parentCleared && (parentValue === undefined || parentValue === null)) {
3122
+ parentValue = currentValues[parentWidgetId];
3123
+ if (parentValue === undefined && dataSource.dependsOn) {
3124
+ parentValue = getWidgetValue(currentValues, dataSource.dependsOn, '');
2658
3125
  }
2659
- // Reload data source with new parent value
2660
- // CRITICAL: Use parentValue from Redux, not event.value
2661
- if (currentHandler && parentValue !== null && parentValue !== undefined) {
2662
- try {
2663
- // Merge the new parent value into current values for the API call
2664
- // This ensures getApiDataSource can find the dependency value
2665
- const updatedValues = {
2666
- ...currentValues,
2667
- [parentWidgetId]: parentValue, // Use Redux value, not event.value
2668
- };
2669
- // Extract level_id from widget-geo-config.level
2670
- const levelId = geoConfig.level;
2671
- const data = await getApiDataSource(dataSource, updatedValues, currentHandler, levelId);
2672
- // Transform to { value, label } format
2673
- const valueKey = dataSource.valueKey || 'level_value_id';
2674
- const labelKey = dataSource.labelKey || 'level_value_mnemonic';
2675
- const transformed = transformDataSourceOptions(data, valueKey, labelKey);
2676
- dispatch(setDataSource({ widgetId, data: transformed }));
2677
- }
2678
- catch (error) {
2679
- console.error('Error reloading geo data source:', error);
2680
- dispatch(setDataSource({ widgetId, data: [] }));
2681
- }
3126
+ }
3127
+ clearThisLevel(currentValues);
3128
+ if (currentHandler && parentValue !== null && parentValue !== undefined && parentValue !== '') {
3129
+ try {
3130
+ const updatedValues = {
3131
+ ...currentValues,
3132
+ [parentWidgetId]: parentValue,
3133
+ };
3134
+ const levelId = geoConfig.level;
3135
+ const data = await getApiDataSource(dataSource, updatedValues, currentHandler, levelId);
3136
+ const valueKey = dataSource.valueKey || 'level_value_id';
3137
+ const labelKey = dataSource.labelKey || 'level_value_mnemonic';
3138
+ const transformed = transformDataSourceOptions(data, valueKey, labelKey);
3139
+ dispatch(setDataSource({ widgetId, data: transformed }));
2682
3140
  }
2683
- else {
2684
- // If parent value is cleared, clear the data source and hierarchy
2685
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
3141
+ catch (error) {
3142
+ console.error('Error reloading geo data source:', error);
2686
3143
  dispatch(setDataSource({ widgetId, data: [] }));
2687
3144
  }
2688
- };
2689
- const unsubscribe = eventBus.subscribe('widget:change', handleParentChange);
2690
- return () => {
2691
- unsubscribe();
2692
- };
2693
- }
2694
- }, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch]);
3145
+ }
3146
+ };
3147
+ const unsubscribe = eventBus.subscribe('widget:change', handleParentChange);
3148
+ return () => {
3149
+ unsubscribe();
3150
+ };
3151
+ }, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch, groupId]);
2695
3152
  // Handle value changes to build hierarchy
2696
3153
  React.useEffect(() => {
2697
- if (!geoConfig) {
3154
+ if (!geoConfig || typeof dataPath !== 'string') {
2698
3155
  return;
2699
3156
  }
2700
- // Skip if value is undefined (it might still be loading or rehydrating)
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
+ };
2701
3164
  // ONLY clear hierarchy if the value is explicitly null or empty string (user action)
2702
3165
  if (currentValue === null || currentValue === '') {
2703
- const { level } = geoConfig;
2704
3166
  geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2705
- // If we have a dataPath, we need to update Redux with the cleared hierarchy
2706
- if (dataPath) {
2707
- const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2708
- let finalUpdatedValues = valuesRef.current;
2709
- // Use logic similar to the build section below to update the dataPath
2710
- if (typeof dataPath === 'string' && dataPath.endsWith('.geo_code_hierarchy_json')) {
2711
- const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2712
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson?.geo_code_hierarchy_json);
2713
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, hierarchyJson?.geo_lowest_level_value_id);
2714
- }
2715
- else {
2716
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson?.geo_code_hierarchy_json);
2717
- }
2718
- dispatch(setValues(finalUpdatedValues));
3167
+ applyGroupRebuild();
3168
+ if (!isLastLevel && eventBus && lastCascadePublishRef.current !== GEO_LEVEL_CLEARED) {
3169
+ lastCascadePublishRef.current = GEO_LEVEL_CLEARED;
3170
+ eventBus.publish({
3171
+ type: 'widget:change',
3172
+ widgetId,
3173
+ value: GEO_LEVEL_CLEARED,
3174
+ timestamp: Date.now(),
3175
+ });
2719
3176
  }
2720
3177
  return;
2721
3178
  }
2722
3179
  if (currentValue === undefined) {
2723
- return; // Skip if undefined (still initializing)
2724
- }
2725
- const { level, isLastLevel } = geoConfig;
2726
- // Check if hierarchy is already built to prevent endless loops
2727
- if (dataPath) {
2728
- const currentHierarchy = getWidgetValue(valuesRef.current, dataPath, widgetId);
2729
- // If hierarchy JSON is already set and matches current value, skip rebuilding
2730
- if (currentHierarchy && typeof currentHierarchy === 'object') {
2731
- // Check if this specific level's value matches the hierarchy
2732
- const hierarchyArray = currentHierarchy.hierarchy || currentHierarchy.geo_code_hierarchy_json?.hierarchy;
2733
- if (Array.isArray(hierarchyArray)) {
2734
- const currentLevelValue = typeof currentValue === 'object'
2735
- ? (currentValue.level_value_id || currentValue.id || currentValue.value)
2736
- : currentValue;
2737
- const levelData = hierarchyArray.find((l) => l.level === geoConfig.level);
2738
- // If this level is already correctly represented in the hierarchy, skip rebuilding
2739
- // String conversion ensures comparison works for mixed types
2740
- if (levelData && String(levelData.level_value_id) === String(currentLevelValue)) {
2741
- return;
2742
- }
2743
- }
2744
- }
2745
- }
2746
- // Extract level_value_id and level_value_mnemonic from current value
2747
- // The value could be the ID itself or an object with id/name
2748
- let level_value_id;
2749
- let level_value_mnemonic;
2750
- if (typeof currentValue === 'string' || typeof currentValue === 'number') {
2751
- // Value is just the ID, need to find mnemonic from data source
2752
- level_value_id = String(currentValue);
2753
- // Try to get mnemonic from data source options
2754
- const option = dataSourceOptions.find((opt) => opt.value === currentValue);
2755
- level_value_mnemonic = option?.label || String(currentValue);
2756
- }
2757
- else if (currentValue && typeof currentValue === 'object') {
2758
- level_value_id = currentValue.level_value_id || currentValue.id || currentValue.value;
2759
- level_value_mnemonic = currentValue.level_value_mnemonic || currentValue.name || currentValue.label;
2760
- }
2761
- else {
2762
- return;
2763
- }
2764
- // When a widget's own value changes, remove this level and all below from hierarchy first
2765
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2766
- // Add level to hierarchy
2767
- geoHierarchyBuilder.addLevel(level, level_value_id, level_value_mnemonic, groupId);
2768
- // Build and store hierarchy JSON on every change
2769
- if (dataPath) {
2770
- const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2771
- if (hierarchyJson) {
2772
- // Fix: Avoid double nesting of geo_code_hierarchy_json
2773
- // If dataPath ends with .geo_code_hierarchy_json, we want to save the content directly to it
2774
- // and save the lowest level ID as a sibling
2775
- let finalUpdatedValues = valuesRef.current;
2776
- if (typeof dataPath === 'string' && dataPath.endsWith('.geo_code_hierarchy_json')) {
2777
- const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2778
- // Save hierarchy JSON content directly to dataPath (avoiding double nesting)
2779
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson.geo_code_hierarchy_json);
2780
- // Save lowest level ID as sibling
2781
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, hierarchyJson.geo_lowest_level_value_id);
2782
- }
2783
- else {
2784
- // Fallback if path doesn't follow the naming convention
2785
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson.geo_code_hierarchy_json);
2786
- }
2787
- // CRITICAL: Use setValues for deep merge instead of replacing root keys with setValue
2788
- // setWidgetValue returns the complete updated state object with all keys preserved
2789
- dispatch(setValues(finalUpdatedValues));
3180
+ const hasOwnValue = Object.prototype.hasOwnProperty.call(valuesRef.current, widgetId);
3181
+ if (!hasOwnValue) {
3182
+ return;
2790
3183
  }
2791
3184
  }
2792
- }, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions]);
3185
+ applyGroupRebuild();
3186
+ }, [geoConfig, currentValue, widgetId, dataPath, dispatch, allDataSources, groupId, eventBus]);
2793
3187
  };
2794
3188
 
2795
3189
  class WidgetRegistry {
@@ -2914,98 +3308,6 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
2914
3308
  return (jsxRuntimeExports.jsx("div", { className: "widget-container", "data-widget-id": widgetContext.widgetId, style: { marginBottom: 0 }, children: widgetRegistry.render(config, widgetContext, defaultComponent) }));
2915
3309
  };
2916
3310
 
2917
- /**
2918
- * Custom hook for widget translations
2919
- * Provides translation function with widget-specific namespace and fallback support
2920
- */
2921
- const useWidgetTranslation = () => {
2922
- const { translate: translateFunction } = useWidgetContext();
2923
- /**
2924
- * Translate a key with flexible namespace support
2925
- * Supports translation keys in various formats and direct strings
2926
- *
2927
- * Translation key formats supported:
2928
- * - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
2929
- * - "Name" - Direct string (will be looked up in flat translation structure)
2930
- * - "sections.personalDetails" - Nested key (for backward compatibility)
2931
- *
2932
- * With flat translation structure, direct strings like "Name" are automatically
2933
- * translated by looking them up in the translation resources.
2934
- *
2935
- * @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
2936
- * @param options - Translation options (interpolation values, default value, etc.)
2937
- * @returns Translated string or original string if translation not found
2938
- */
2939
- const translate = (keyOrString, options) => {
2940
- if (!keyOrString) {
2941
- return options?.defaultValue || '';
2942
- }
2943
- // Use the provided translation function or fallback to the key
2944
- if (translateFunction) {
2945
- return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
2946
- }
2947
- // Fallback to key if no translation function available
2948
- return options?.defaultValue || keyOrString;
2949
- };
2950
- /**
2951
- * Translate widget config property
2952
- * Attempts to translate the value, but if translation is not found,
2953
- * returns the original value as-is (graceful fallback)
2954
- *
2955
- * This function will:
2956
- * - Try to translate any string value
2957
- * - If translation exists, use the translated value
2958
- * - If translation doesn't exist (returns same value or throws), use original value
2959
- * - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
2960
- */
2961
- const translateConfig = (value, fallback) => {
2962
- if (!value) {
2963
- return fallback || '';
2964
- }
2965
- // Try to translate the value
2966
- if (translateFunction) {
2967
- try {
2968
- // Pass defaultValue to ensure we get the original value if translation fails
2969
- const translated = translateFunction(value, { defaultValue: value });
2970
- // If translation returns empty, null, undefined, or the exact same value,
2971
- // it means no translation was found - return the original value
2972
- if (!translated || translated === value) {
2973
- return value;
2974
- }
2975
- // Translation found, return it
2976
- return translated;
2977
- }
2978
- catch (error) {
2979
- // If translation throws an error (e.g., missing key warning), return original value
2980
- return value;
2981
- }
2982
- }
2983
- // No translation function available, return value as-is
2984
- return value;
2985
- };
2986
- // No need of this getLanguage and changeLanguage functions
2987
- /**
2988
- * Get current language
2989
- */
2990
- // const getLanguage = (): string => {
2991
- // return i18n.language || 'en';
2992
- // };
2993
- /**
2994
- * Change language
2995
- */
2996
- // const changeLanguage = (lng: string): Promise<void> => {
2997
- // return i18n.changeLanguage(lng).then(() => undefined);
2998
- // };
2999
- return {
3000
- t: translate,
3001
- translate,
3002
- translateConfig,
3003
- // getLanguage,
3004
- // changeLanguage,
3005
- // i18n: null,
3006
- };
3007
- };
3008
-
3009
3311
  /**
3010
3312
  * Renders a panel with its nested panels or widgets
3011
3313
  *
@@ -3124,6 +3426,16 @@ const PanelRenderer = ({ panel, dataSourceRequestHandler, schemaData, onValueCha
3124
3426
  return (jsxRuntimeExports.jsx("div", { className: `panel panel-${orientation}`, "data-panel-id": panel['panel-id'], style: orientation === 'horizontal' ? { width: '100%' } : {}, children: content }));
3125
3427
  };
3126
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
+
3127
3439
  /**
3128
3440
  * Utility functions for file preview functionality
3129
3441
  */
@@ -3167,7 +3479,11 @@ const canPreviewInWeb = (file) => {
3167
3479
  return previewableExtensions.includes(extension.toLowerCase());
3168
3480
  };
3169
3481
 
3170
- var img$f = "data:image/png;base64,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";
3482
+ var img$h = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQQAAAEECAIAAABBat1dAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABBKADAAQAAAABAAABBAAAAADhYptGAABAAElEQVR4Ae3dh5cbx7Ug/MmJHGaRoiIpybKPfd6x95zd9///D971+z7b+2SZkpg5DJPj/m5doDmZwADdAGZQGoHV1dVVN99boasn//p//vfEOI0pMKbAxMTUmAhjCowpkBQYK8NYEsYUaFFgrAxjURhTYKwMYxkYU+AoBcae4Sg9xldXmAJjZbjCzB+jfpQCY2U4So/x1RWmwFgZrjDzx6gfpcBYGY7SY3x1hSkwVoYrzPwx6kcpMFaGo/QYX11hCoyV4Qozf4z6UQqMleEoPcZXV5gCY2W4wswfo36UAmNlOEqP8dUVpsBYGa4w88eoH6XAWBmO0mN8dYUpMFaGK8z8MepHKTBWhqP0GF9dYQqMleEKM3+M+lEKzBy9HF91S4GDiQl/x9P+/r6iyXbK/MHBQZbL5AMy0uzsrIqZz3KXMgcH8TtOjVFgrAy1kJp8a5d8k/5M8kRcuYxbKe6pA1kh4VCSqTyeZePfhigwVoZaCL23t5ftpmRPTU3JEPqtrS0ZtyqJr7o/WVLdGmeaocBYGXqi8+Tk6YOunZ2dFG6Snxn+YHp61t/J/ihJ+g018256j1MDsJOPj0v6RYGxMvSLkkfamZlB2CLZ5LqdCD0XoZ4Cvy4jv38wPTMtpSa068a/lW4caXp8URsFxsrQE2lDqk8b5hZJDmGejPgoEjXgAHZ3w2PQBqoyVbzKwZSBxcTewUGJpOhPuXtASfYnp3uCbfxwtxQYK0O3FOuofol3yDPp3+UBDCGKH2DswzOQ+6mp6SL9oSvq+OUcZmYUhgZ43N1xmNQRrftXaawMXdAyjHoJcjKGcUm49/cY+gz6c5wcv9vbRg3bGxuba2trm5ubW1ubqRXUI3VgepoaRE0NXr9+fX5+gSbMzs7Nzc0KsdyQJibFUbpKCMOZ6D/+j9L4KZkIqGRSzUrGT6ZQJzfLE61H2g9GI+2m4nHduczIrfX01ftnrAyd8pxYFRGdItYsPdGZnZ0hWyL/ItZT5JswbW7Sg+1Xr15RgY2NdZqQM0sp4iltbYlsdf327QfyyjNQhfn5+WvXri0sLMgvXYv1hxhPlBFFSH15MoWYMBdZbw0tWjoSgh1yf/iy1IyS8ni2ET5KO9Fiu00Qbm9vd0qOy1hvcnwKd+dsJT2VABHrCPbJ8JQ5ohj+kqR37969fPlyZWUl7HpJKWqf7KLIZBr4kH6P+72+vEA3+A3qwWmQ5tKpaqEG2izgtCIuGqowgAo35WY06Z8ylI/+tVkKUk9az7bqtR+Ielc4jT1Dp8wnnRnqpKS6JJozM3MH+1MrK28owIcPHywjqKPFnFpVR3IZElrklPge68+tXKHjQCSX+axqq2srdICXoAxUYmlpiW7wGORba1K2zBF5SjntoIMcWHahUHcqy6jst2gvjYpyF21dagVIek9oj0F4dS7HytApr4swfbTHHiNbb9++ffdu9cP79zShxBgHhsG0RSJqfku1eEpSkpljvynWCvMptWIAIHDaPyCgWn7//j01SGW4efMmxSDiVMivR1LQs/EUcb9VF201iBKyntU0q9yvS4UJ51ngVU1d+sxYGTplMZNPblJ0GG8yurr64dXLlQ/v10Lqp6bIK4EnY+4K/imL0CVVKFWDtFVyX/WqsEQ4rYJi2qMihUhZz6doWlG2CS6Io6AYy8vLMhyCCnoxUk8R10WOarJr7YKN5qgJQkkmscgKfj2iEdUqqK5mZqwMnfI9ZYWcEUqmmlD63dmxjja9uxdD6r29CPRJFcFjeSlDJfq5gEbsTu1My9l43i0yGn5ndXV1hvWfDieQIqvBohTb6+vr7s7Nz02XqVh3TVi5KwQCR1sZ4lKbGqcAwq3FkujD4qLRSAxIQKuyJAP4syA8FezLVzgeQEf08kkhUGdxYWFza/Pdu/c5RDY8IPnT0wuTB1NiDi2kQBNA4sioe0QiMW5Jyl1EUH8iRXmJjQ7XLw/u7Bujhzz7P6Z/ootoymxRKcumMi+4apW6zqTbyJT+Y4Tg8eJtJq8vL9+5c9sfDdnaiukvN83qFh1uPXwF/7m6noFwSIxiiGk7haBNRgytgBSFkE8chPWdnHz+8rURgrHy+vpGBPQzc6ywiGRqcjZkmUZ5JMVPPuW3CGMRyBDmVuaElEVlvkPlojwussrk1OxkABCapiSqgFTfFKoUgU15abdSgNSJohdulYa0XPA42DuY2NuJYffO7rs1w/OVd/fu3rt95/b1aze3d7Y3Nza5No/EJHGBBGWk6LCsh5TGLvPP1VWGttyGHJN+QQI+l9hZ2LMv7hehCLMJknDcAPn169fra2viE5UFGH63togK3WmJZkuItSbe0FZ2UAlPEa+WmFeFhzL5yKECuliqx4LAx+JSzWVb6D/eKZrhMlQk1eTQvaj/sRlwb2xsxcpgLA7u3LlzB0ZLS9fkOQdBX1lSoRLT9pqcbOtwu5cpf6WVQTxDB0TkJJvoSqEZmYowExbSbwVNEheRoY9qU/wJeQnpG7UExanpSejzdRuWBtfXP//881u3bhU8dgxLWqQoNiL8ykT4k0ufrq4yYC3JlkgGKScZRdYjWjE9SkOIyLNnz968eZMTNeRDwE15PBJ6Q3NoQgwERi+J/0rwE6vplAGOcJChD7DjJRLBwLG1UD16OF4A4qurDOkAkuvUQIYckPWdEiPxA0Tk+fPnplCVG2girkxqjvoyKhOWfZH4yKWImALsQOHggCsQBMbk1cxMztsyDXRDOaNwxisbI4fzpwG+6sqA64iUakAsBPq7Ozsrb2NXBTVQYjbS3dQWvyrnLzFqjVIJ1ai5hxgQH0xwC4m7X3mkgLXfjBtpeyJ7ZMii6PKmq6sMeErWJVwn7i4ZQkPk19aU31lCeE8sOAQxA0GR0ohW9dsa0tKI0ZKQmF2yjaQ9z+si4ecf0ulZ5HaXVsQtf6Om7Rdjx5VWhlSHJJyxstHkmzevX71e2duPrT6kgSgc3nbqUlKewRINCacyiYajFSlN8gL8mkVpiBRN34UXOqQmJII3btwQNZlfcuvjPNTFpGxEnroSyjA7G3vsMiJi4DEb1y3UGilYaeIYNjbWGEVTRu/fv7P6ZOMPCcj6xIVM4GZeHs5ohzKE4RyxFBuo2HpLLAVwg5/Wm9nWQkwbGC9Bn0tM/4BwxYfG3CzqeQT1ioZcNn9xJZTB7DmJNxVEoIsE7GMqCbBMTO5NLb548drEkR0NdirsT1CVkZPvfqkjPYkdJQbTJg+4Bf5hbnqO20wvmq5DhX71N1TtXAllwMLkMS4yackAJdyFFWUOgVvY3Nwo4cHUmQvFQ8W3eoBBG+MEJoMyGEGhG/+wuLSAVjpUXilDetd6oBhYq1dCGUo80wqIDQYQm0pgtpHy06dPaQIlyZBAhBBb7C6p5fuklGXwQ/TJvalVg6jZmdl79+/SB2PuNB9pTSqb8sk2R6jClVAGQT8nkP5BHqdFxvlWGpbTBIXuYps8RbiyQRIKpD4giAx7IXo8mNz//OEDG7TYlNSHrDZCUt4hqFdCGZIWuCuJg60ov1mxpPbGWzM25Njhb5cOrVDNXGooQzuU6pCIl6laBkIlYoyF+fWNdZ7TcIvntB7HVNAH5VL628uE+6VShiLEFg0+DgwKq6yjxeqSFwNUWN/YeP36zYvnzzkHC2pu7e3GMGFuds5dl6ZOR28VrU8iWUhX4kkkiBnkCZZifW39+fNnk5MPkau18tCn7oatmcujDEQZq/h5G/TTgAmHQgdm5oRI7JkKq3yCpYTXb9c3ticmbTSY29mO7XfxBo23mcvyszcIYtPRZU9nhIJTucbml4WIvdtT3tbYXf2w9mrmNQW5e/fuwvyCsrAaBxPesEBqldEQweTj8gDZR5KAl0cZivSS50hEX8rZD0HPzOz8zs7u6uqaiSPKYArVe/YqbJtydbaFpYKJA3kcNAF7FfYsn7WkXMLDj7tMXJpA8kbQwf7uypt3uzu2JU7du3eP0eE+rWET+nykvGMx8tMOl0cZ2CQDPipRLR4Td67e0Hliespb9S9evDA9YsxgdOgWnSnca5nINifPsJiX3VGchR8LH2ODyRgnmH1GQ/S8ffu28QP6ybM4KlAP9JcvlyPpFlDg8igDZHCFoFtfM0OKT2Jc2/a393Zev3xlwxFNUF40JLYuU4Zg8zh9mgKxfUtia5gSCek8lDt581aWUJiSGSvDp2laew3CTdZxhKxjkksrztaPnj1/Tg3K2CDOVsGwjHFrB+gydBDnfXhvww4O2NAH9ERkKiFesiSX6zbpE5Lmhm0jivel8gxYQsppBKPFRbh882bl2dPn3tbEHqyiBqkn7tKKzI8o5xoCe3LCHi4/jvxIO4Kq9IFxQU+HOFEMxER2KbzupCF4zFuMYrpMytCSddvqmKuiCfF2juho36BhqnUOSjp0jMSt/B1FtjUHc5xSgFgxPKAMaUTQljKYjVAIEksQqqQmjK5bgMhlUobWJjPizomzXkUTHHvKpzvMK04dTTOGlyoE8mWdtTnBGsmevOw6bd4IxUg8ipWpJE51x8lRSJ2vfMTwrAygxaij+2bcqCpDBjmYQb4lnOCmcahMa0x5Y+vXX3+10mzgYNYPO1VQmRrIkEmP+FUykvLZLNCIxO6LK9ObmkBSYgLpzp1FFufnn39GVecJoDy48KWcpB8gIi+mIHg6avlmAe+6t1GdTskIlaFCa846/TV+KLfpSGhkqOeWlPyoCDNWgIoUXWVQMpfSKgOC5qkAqC1RCeygApb0U+49Qjf8ujUSZB9Vz4DcxQmErEv4iujsPVv122+/eWlTIU6wTcG8UZ3r60pcm67M3JhNQmfWB/GZf4drhNCX/5L+hQWtSQt1mgaxy/5G1TOgMnIjPRIzSNRA3najfDlBBh3cUsUMR5c0GVfvjgIY4fAEoWn4h9jbEuzQhHK/qQ/dtTig2qMqKEjM+fLUJD5dhC/l0ATpcICEqhgz7BZpQLzvsVuuAKlJvDG0X5R/8eKlzb+UQcIXDEp9kO+xr2YeH9UwCXUyDPWL4vkmCn6sr6+Z8OANFBa/wT6N46RaZImIo7BE9Mm9yVaDNd/fWlycpx7pEHAhjNGIKMOoegbUJ/HYgBk0QdhKEwwYeAmcQH3HH7llCMd7jOeM6tAGk63oT9bDFZfdGZji5QenM7uFOyMXoA67MhBr6SQvjdPigN8Z00cHTjnyFrMzgQ0TrAuFTcrpPabLRoI41X1Y1AEmp/3F8b4n/2BtLgY6HqHYLjPkI3M5bVm0PSq4dfLxc0pO0vP8En2f/HOgDg4YmXklxODAEjVQ1tYdVvnWVx7t/DKjAa7YEh/nhld4Z1dVe+f33OjdYQ+TKk1IaUCbLDFqpgwioA9r60+fvfBOv4/Z+CDH9vbWzl5Mqtqk7RFDODsqh+RE1CIOIdMnUkrG8WLn3hNo/4VKQCl0Io64mZuJ/SYmCSC7uLjktBvjJePV48+fdx324rz7h+6dVXPaxvg9Ojw5M7cAyM3tsoYzNft2ZfXa0vvZGTti5kyz8su+lOJobyf4azWX5HCm3UOnYLTr1/jvsHsGAi1VBEhNcI3ILKYAyaZiYkE41LHoFjJTPMnHpw49XrUzEhl4iPEc08K5CTngJSC5eXP5j3/84+9+97vPPvsMFnE3FrYGgZAoNLsNCud++Nj3Ak5nUJlZArPvzpnmCNEfhUNHht0z4DSCp3znb1xGSezTLq+tvZahDGpKGTMMQjRq6TMn8iFIDXxF4dGjR1988cWDBw+QAsrK7Ynw6XV9l8nMWmDoqlGA4YIhHPBu3bppm4agTkmJs7pqaQCVh10ZGPhjmpBEMnKz9UjKYVz6gUpbBkDIfncJlxL/bEKQYN2//+Cbb76mBtSD8aXzn3/+wPFnPIcVxmILhmKVNxhRXLGvPzJVVFf4WpZ9Yl9Gv4nU5/aGXRkqdJESoaViZuJoRNNHLJDZbjNIDKeaRESF6pGRzkDERwy5QF9e+7Ykh9sJOeiGhBqM7sOHn5fPaq2V146HAt1wzmVmSfyGQfZ4e20aZGX1c6wMvfGIfGO8lM2gteRdwxev3hgwVG2bZlF+aTQBXnCh14++/fb7H3747N49pxZtb22zunFMqvUsEzj7B969ZHp9i+3lq1dtClUkGVgGq3DN5m4bJc20MlVlqSFwGhhMnXU87ANoWBRdaP0UXQih/+XJE0WsoyEmt2DCW82YcxyddNKHJZKwY/th+pf/8ecffvz+/v27s3POLtja3vU+jYccYjAxPTu1tb3hU1S/+933f/qPP965cxPuoT2DTkyXMTSmAAQKhtFGNZRBGitDT8zBXURE02yF0AuOScy/f/7ZrVhNKxXcTeozSD31V/PDEU3HxsH4Y8iLxYythKZLKwS5O6b0u++++8///F8//PCdMagPDm5tUwNvX/huYsytxspJuIh4lg7cvXvnf/7n/7p//74Hc7SKRNlg+tXooJiJvIw6tamNrg3y9KgvyTIoZfDutOIECQmCCsWNq1AzybtrfrigOQZ7xdQkn0tENBp7//5DKolLhVnuWZljLQzfZUxBkgzyaezrl2AQYmKR+ry8vCzyefz48aNHj0uESIHUoS05dxlaFIsPE6SNm9jb3NrQwq2by3/60x+9VJDtoIM2kyxJIuUukRFBkE6+bspkFxTPlJc9MgyaCcDsNMFQIevUDUnn7Q/7AJqIIBmaoqBYyO5ICwvF4dbOzs6J2GlNsmhyLOQyRD9RI7VQgyAZNUT+sqRbN29B1reZD7VchUBHMkWqw0maaHLIPs+ZJ+IwGaYWNOJjK2UCKvY1SohpB4V1tJp8A+Dasl784P4+f26e49o1p/E58TY+HAoGrx9CDTzGF4dwHHB22JWBYcNUzEZiRDSNaMJOhhsYMOW67x7YEozIfQ5ytCFPcP2S5q+//ppb4BxICSEuAtNpN+hz795dB6J6qeC///u/c9I523dLqhoKIGhldd3fTOkoemh3QBmMHO7epd6Oag1rQF94KiBBU6a//ffS2rArA2JlIkNUwgSFqNrUdS84D+pZImKFwNl+AKhCI7Ii79gVoRFNYNqhmQNo2026AjXVTLBEnZye/eTJEyaZmpE5bSJjdhq2u9aJBnhGyKqbCFz1buTw4cOaL8wzATXqYVfEOq3ysCtDsTGtz6hxC2lET0NkFMqKMhQzHYENiZcnH48ePXr48KHtFSbH6IYgW+Hi4sLO7lrnDnBjczMOjLUfaM4m6kX2QtDFP/gARS5gUxU0ohLGGD513dokVA/ZCtc03TqmjT5Qy/n52Rs3brEH5oWBUU/PPbU6AsqAlMwbXpZ5iXjVMJz+6EVJEUPvHYQcSKkSuVDw/fc/3LixrJAmJLLwDRw7RpIZ3tnenrs2hzh0jPRrwejDOZDOcVlf99mRFaYk2yej9jj2JDXnPEwPwjFAlu55ETcwxTvKsLS0uLx804Ap6DBkAVIiNOzKAErHMTB4yGf0LMYIfltvHgVlSBjbwbNp0YPd/V3vHpkkhZSgxersjz/+7vbtO7AjqcTIejqtgGaMgGPzYUdJF/nxBGEkn1P8w2zS6i9/+bMv+b54+eLZ02eWwOiJIawhmOlVTRvMh8oVzeuopw4qaVLbJUaK9g0SaKZzWqWiIaHnGUGl2nTQZENVhkcZ8CbFBomYkzJ/GBMOB3Ozi3OzC29Xnm1u7M3OLG1tcg5L22VZpyEind1NEaVTbhNuEpkGPuMTMre7t7V/sOPgoe3t+FzQ4+++/fHH39++fXN3V/n+TJy0Qka3y3e0Jubmp7sb5E7uWY9j8efinW+L9FtmMoH3YfXtzOzk118//OqrB07kf/7suekmMTy1sf1vZnbBewg722K2mLUrbyAcRifUuWLM4Rtn5h3YHQhEIvF0PvM03DBaMk9gWcV+1vm5Jc5qNj4qNixpeJThdIrkSXhcP4PHruAWjrXF6/RHhqGU1Wf9CszFDBaYYvw6PVkChqUfSjLSDVNd5xHIpBAY6YhuLN9Yvr78+PFjQkklHCPiiH6bXmdn5tuBWV3EwzVM5O44QGZCNy7r6uyi7Q7jOOYQLrEC7RIF8+PkaOpy+JWhwBlqAF7imMklqTRfZNbIGrMZJNjViot+c7VB15wV0pUIas6M0/fff29QIeZULphRMyscIn4/swgCd64AH5OnLnXazz56bmuoPUNGlsQF4aRKbrA2pu6GJWV0dwSaMmgkfMLloHDKmRlOu+x+//sfHz16ZFwrUIFRSsaRh/t3cVgHtJpja5SkIXTSDg6eQWEKJe9bfR29fyC0WtIFZKHMOeTZrPotuHc3fdx3wA43OFyqeRiydh5DYzpCkknP0L41vP8G0GWWPS0ic0gEyd8333wjPiKFQFdCGkrNU9SpL7ghmjAsVZE4plsw66pr7Zt7NYsFBkGUVLed1hEFsOYAquRjxkt9wbQvjQy1MiAZJiXtsMpliBh7O0Ru4RNcADzJAzYRFJb84Q9/ME4gE8rphihFRvpEKxe9jWicQKpcyHvZ1eJSauvGPGLmlKuNgxft59PP6TpFnzKYVvKAfqVPP9lgjaFWBnQowh9hN/6hXdpRlw2S6NNdFaaWiZdi4l0WCQ9gk99Wwb766qvf//73t27dpgmstUYpCbEgIlD7dB891NC+sbtfUGWnCRXwlAtaUkxzv1AP/Zz5KJnXb6IpUuInC33qMgFnwvGpG8MyZsAevEEjQXYIkRPkp+MTGDaoMp+4VSkA/qEs/fgUarXfDyBnfOivWiKIz3kwgQwtmHkyGTh99tk90ZExK+dg2AApyEpqyqsJ0EC4hqRZXYAqkx5cZndoKE8T1EllcHliavUCMDEKJ+194AuG5J1hNPcozyjUt/p3AdAHL1KHgcYYJAvJaGfMAFprQ0pCw7RUBubwUwPMFwGy0YiumjoUncfG/XzTwHuPjL43lY2YHz9+JDonmRQAgtABM3TEMFoIfGtL+tK+fiWZFMos1KfT7zJoARVI+gRF6sPHX90mW9M5iJTwkTJwkn3qsT/NDJcyFDmJBUvcwhuW4/1758u/w0i2hGVlaxWmNe0PAXpoBZDYaduB1d/ZGUtp4h+bruNdApGA6Uq7UP/yl78IkPDe3bl5n5o+0l+tanCkpzMuOKcz7vS5GOIYig5Y6RcTkS51o8899dDcsIRJRUrCbpUUy/ho56UQ0u+X9RJ23759y9QcIpK2HlDu46O+MD19MLm3vhGTpKws++cT4vT1+vIyTfj2m2+uX1+yEry45CSbA4oMuz52f+GmkspUsUoXbqqzB4Oz+kIlv5QBQ8OCWJocpg/ADZVnCMea7EmvbbAlnBVveIeBVty8ecvOMxREzc54UG8tClzU8sDuBoxm+B0gx/BdX77+ww/e4//+7r27O7s7tuXQbd7OrXoB6qb1Sg0y49FaFRVDUwP9cg485xB6hqFShmBm8iYINzEp6vCvX2f/27LqFnPCRaBsN3yvra79U/H6svc26ez01taGw10A+Kc//fH77x8rtG5u37K/jY01Km2EAIXaoLlIw5UmXOThbp7J0NcTMjrlGWL03H4RtJuWaqw7HFLVRhCZmA3ONAp8bzWSqxg8UAabLnPJZkhiTepqyxRILRjQYhnvJFhTe/ToW0qrhAcDKkWBl9ed0zgGaoNOYWradqcxfUi2IogMZYiFyP0hcpUIMixjhhSPwhi0En+E15YjQzlCQDtRk2Eoag7JABrMpoe4B44KeA7M+o//+A/DZRrskj7I0F5+zNAfJmELh8SnFXKjdmpFEr/WX9YgtS7ZKkYS96LeKdOwtcJxbuND5BlYq7IwGjuPiQ7asanhG8oOM5cQyfVUd89FqqebbCaiHPszlNkyQcQJeHnR5lNm1UylA7Hn5oCNr95M+NOf/uTkO4qaQxr8BidNpr1KYDE8mrC/53WzSe9L//rL09mZ2EWd1KbdR/8Egf66CO1UdQ7Gyb+9gylf45503tPMnMzm1u765raDu21ETs5SEhls5z/99cTCiz48RJ4BIVCExEyV905IknTYdJX8YMgEqNu3bnHuFoyQmg4Qelxb3/zgtV5bUDkEb+Kz/RTDPguiX/E4Re2iDKrvuRC+nR1bRerr4mPLKev68hccjmlW/nNraeE6s6ceYNQp4VvkPz7ZYG54lAH+yNGac0jSGIOWELNFnUKqVrArimqQSgEAKcdFw/fkHGNPe8n9w4f3bYe2wIzBgiJA8gZ+B8XRTsgCvLA0YtC20iZSnTx74ToIokc01IJf9HRMwL07N8IdlKSwsDguYsjYeBoeZUCH1pvBSQzbMdhapyqwH4U6QZtGhOwUswQAYNi8QNCNB7xVLGPP53fff/v111/QWKz1mz6Bf5BvnJWddxg2mGTSZ4k+uJRIaudNXKwmMma4KCOMfP/hQ6z6lddOk8X0gYZerPHenxoWZcAMhMCasFfsx+T+zHx8V/jlS6/t2uETSZ1EWL5hz6BrcRGJN4jXuwGxLahffeWYo/tzC9Mf3n+wTZ8m0BAQprmtoO2dSX1vIWGDjhTELOkwhfve47EGAYBKMae0sTG/GPtqqWVC4lcaCPWGRRkQC1NQIadbgh7lcCEzk9SkjOrCLQyERsnIFHFq4C1er6p5T4307+7tbH9ofewVyGQLhEYUKh9j/1BdAhK0gCSCTE9SVUl95C38jOioMDrUT8nu7s4H36Scv5UUUyKBoT4wzufC8ChDzMqD1eERCMJFeEcds4SRppiIFjK5i0yZOR+rvt/VKatPDcwXWUzgJQS6EXGXRTdHNgKsuLQQrIFA2BXKgMwFnITZrJ3HawVb4zkk0OPhjnjaW7dvAEDUJBVtaXmqrjDqS+VhUQYEktCCzPs1RbOzvfPuve9GbiqHKv5VDAuB6wv2pzViEIdhZCNFhIrqzpFeRsmUQXK5uhonsng3R7k9eMlgYHP38pjq2QT4tB4GXwZIZ9K8fRtYNA8nhuo0+xUpcacognQZs3Ee9CXvNkypYVEG0p4CVHY+z/oaz6t3r3/++d+GrWSskC4srmEfArmsSRmKKk5YQSjsCfNJK2yIsqjsJTVS7stRecvRdYBx6hdYEjzlyWZOX8blsKb8UOKtf/3rXzCqQpS65S+4V8hSzH/QxhVjZ2Oy9UrKoFwSvtFVl+43TMBhUQacEBExVGbwSb9P2f7221OKgTp1M+kwxfXl7GsbjfRrh6xbDx8+/MPv//Dwi4cGe+5KWT/5Wi6P8yxvHW522PLAJnCgykwI6YC0t8zuCjfLZ3NbZArf3Lwm6HxYlAHyOd0ss76x/vpVfGgeq4qpaFCWnEI64xzSAyrBPX3+4MHvfvwdfTDPa7Q3iLnvWnBPHYghz6HpVKSupbPzGo1VP+uUOVpInWxbm/Meq+nesCgDrog6DFIFkS9evnr77h2GCZwYrCaNlt5y/6lOzZz64rLhsljW6MWg+VRzNSCT2qs8IDg/7BedUwp7bbH755HOd4hil5KDYudau0I0A6TuG+vDE8OiDGXMEAtV3mt7/vS5oYJ4qWhIEKcPiHbYhBeXd6zx+TbUPeMEXzwor9b5znQA02Ebw1+NE4AOe+w3B/1gHoRnCEvHOfBRCYDLQWlCADAknEtOOPbQ91LXN9eLP4iJcNRpEkLk8FHN+fk5X5X1zVlzu2trvj9r20W6BcCc/GsSwL70FQKHsBV5k8jNKwMHABIKKaUONBwIHKPm8ChDnPf29OlvPmxsQ5LZJKzCHuPnYxDXeokZulxaupanvunLKpuS3J9Xa9dNNk4KKYC4FJFTMVIfCgxx41BAWK8xwt+Aoz2ab5IIJ/saQJgE95Q5QgYg/DA2cITKs+dv19b3dnYtMnDd9vpOWCCNALL3MOnMmdiTnDaduutwlzt37oBNLItV4JQB5EnyjWIJ6beeKQo1ZLh2bbm8W+uQm5hHdlSMGc5ircNvUAlLPn7Lm8odWiV61mFNxNOVfY0zLE7RwNjDb35J4UAIOwAGF+cYFE/8ZYyiVtc2Xr9xCgbq2/LplYbwCL4hEJ6hLsoctn8V8Z1qMok3dqdmMB2LawsLl0YT4Bl2P6bprN7EKiEuwA4Xyng6XscrdWI+Q6YllmE0ThoOhaem7hgGDBMnlTDIDEoZBhAmMbRJ/QyE8MMMkrlUr9XjR669oEixTKfSusZC/RJ9YwZ9YEnYSdJRUo29Nt10nG1OJXCBN4BgoikD/RLAt5YglMRrymx9F5pwEWT0e5HH+v3MADwDcpeDkVLGeOe9d+/feb/ZZiTswRg3FCJQ8KJZj6nTxQVvs8Wn1+Ub7r3fzD2zPdbfeWcQNaWUr7YVady3Maz47TDtFe5Bh9q885kgDuLGYJQhZw/IvYzNz29X3opcZ32sprwGkMYJM1zihNQYZQQHC84/itO+Wv0CJjWzMRia6QhWosHsC6qJY9Lbkk8Fgw2TXsxsR0tV8eXMDEAZEBLp0wPQBGci+bwSg5SBI+HDG5qQSb5JwuvOcGFhofU6GxiUZGoSjLr7ipnriQM7grhi7reyPpXmJ/GBoaTEi90NA+qGv6b2B6AMGGAeAz6EzDmqAiSTqvPzzpyLpNAtHPJbXdaE/Mlm9R5vN5dzXeWBIZEMvycrj2wJyd73WuHNW8sLi3PbO/YFm6rYteWkjJkDLfhiE8SxoOzZaNQkDYqwg1EGJ1WjsuGyl4YFSDGfMDe7tm7pNxZfcuRAFjmQBuiiI522Ojo4oAkAyMtQhUuoDDk3MGWqQOKcGSP4RtQ6SUnQ38FnkZDFrbX1je3y+UOEUi1/P1KsRbie/sk2q8Z7aquHhwegDObsOQC0tt7sk92AF5c4U6oMGMIAIw096QGpjh7F2dydSiFtjMnxiRO/7t29SwESAKIgT287anFkKoVMC0q9q/3jjz/+4x//wAXCXQzCgTf4Hj165NtCy8vXbYr561//uvLP/2sOFh1QA5WwT8YbuUhUCfFFUc8oYJJB1K9GNG7ABhQrIZWBumjjXT83AGVAQZxgkCzrJgcQt2vAe34AGKScGhhHckGmdzUpPxVfYbvcUYElBQSPCdabN69/+eVDPtgBns45+Prrb7zb/cUXD8pXMsy6kk6nH6xNTTMWMfGKXJwJrvVDE1osLHtnIzxOJ4wpJdMzg7tvYDDKwCSzx4bO0JZQdnJipmEBxF22h3VMVcRmbKClwqISDnRPyxF5giiTvKD55OS1a9e9yMrM59Gdtujar+70f9xhIFDj1q2bjoSam19UwpMrFENKWugLutrRJmD8Uk4gDdAPD0AZCsJOro7RQlji8L8+gznd1KcCWkwEBuvIQcmkweOjzSNhSV/YPLSNkD87IAgz+Uv/4DhA+lAuYyXOO1XkE1MsxHMUnz/8an5h6aeffvrb3/7GfyIXlsGuL/oADJrAJFXKYHBfWm6P4hqk40CUwUtkkaCJsmQyl3l3u9nU0juJjFuM4b3SSQ5MaiVIrKAwoC9s7h3CWlso7sEu7oj78YAJKAdhziAF02DGVdTuMH0jh7l5n5iYu3PnnS0qqERqKUP+9gwhvYqIy8ufLFFxy1o2icLtDEAZBjNjyCkzycbQqInohmc9k7XrBrCTR2b5/vznPz969IgEFM+w4Bs8l10ZDE8NgvM1fJ4gToOlD+iBJrs7MXUxO2uQ4Jt6Maz68MHJd6Eh3Dh5VRN9+hXM2AuiU55B0iw1kwaiCbAeiGc4MFrY2tp0Ip35Cp9Cs4OyefmLHTnTMxTSgRdWOdin9Y2NYP/B/tTQvObRtYp38ABSOxqDNAtHnJ+MDuTPaoL/bESambO8YChl8WHfKgSpd4jbteVF3yJyrjiS6YE+eKqDrj5RJT3S9s4GzUR/Sxpl+yBlM313CT0Df8cLB/kKev4RI23t7oTImZozaTkzPS9QshepPuyj/9Zf0Dj/ppx7b+YoTikztzv37TdffXb39t7udsB0yZOdudNl0mySPoQ3iC8Oha0nhByj3RhKCDxmWX+jOV4Ln5uZWr6+ZNLBW5r27nlPvE3SKhP1FXZJPMsaC9tbnMPO1CQVpQlCp/xMWWnZT6QQpC5b7rp6rZ6hQqDgk9I+Obn2wZIC1zy7tckjo3X5MPje1iSS15NOfZ0hllb3Judn5wjE1ubGtaWFG8uYHap7uRPDZIQMyxB3Uk3IvNJUrjkLHInLqRBHr4WiBnuxs2Xe2RbGudnpqXU7ixd5Ei0cC26DcF0JLB+lmZnpOTHS2qoPW8U7qKFjes39US3OHRWk2thzDJ/a+mk3DM+t7S0+UaZMXaMqlYi3CNpVmvsXMyRKIU4QJd+4cVNM3K9ouDk0uu8pFL5F78PSeyzfsgsIglkCGCSaMQ81M9udyJ8LHvIbqmnfYN1LjpwCfeCszn2orptNKwM8TM8l/mWoVLaCYc0ANv9gQUhESr8BYn498SooQ7fSVJQhtm+gUtoLRqTbRk6trx0hGUkgFUaS1ICy9avxU3s8p3AAymD5sxI4JM18yuU5gPb9VjK4DBzD8mnfABGzB8WJviPYrwYRJOljssHcE39uwNcXfiE6P8AbUAbBgqUnc6wRJhR29Av+ztsZgDIkcClzsIZ5IN8nS9M55tHpxMGmgUvZgIQZbB59KOWdN3MlaiZN0CcmwcsicV+olM4lxgkl2QpgkTvX9QZC1gEoQ8zflLnq1AdWoS+UvQD5bDkziY4HABC54YrZXr9j53CMmHiEJhgXo4UyAXWswoUvxQVlc4ehc4iBLVLVjrULt3nhBwegDILCShlkpAtD39uD8TkFBwn7bjkYeAatWVfCm96avYRPow9lCP8dmzBixa1fXCvNWgQ0oRLKQBOsxtK9gRBxAL3yiqLPtrGZZphzPN0w/thA7jnllZW3mQdAH9ncMDr1dYdKovmkjJVrtLIvo+xu7MMYmhikGSIDRs/G6C9K4p+tiOugaF2oX30IVi03rQxIyfrmNmAYuoyQke+tIGouEx4JU9fX1vziStq/5vofqZ7QB7/Ia2Fa7JuoZmd7xAPZC+1bAYLJJTugGKkcWJMQw8miEj328+nHB6AMy8s3crMqykI0pLCEpJ8Gtq81OH0k5qCdzdEm/aACtr4i1u/GkkdsFuNNHURJWdLHfshAiEEZmuviXUlKynAi7KR86ETNqWllgI4ZGyknMdP9wXMQmyDCIFnyQ3mmqOyKCX7UTPDRax5NQlTjgxW2rKY779vsZ4p49pDE14F9gZhSIrEYo6TZaoBwA1AGNkbQSR9M1RUtCEtTu9afTssYF66trplgxYmBQXE6bENUijishtGtz1ZkSJuC20cQK03ABWqQm+rbg8mGLNQAlCHHSQ4zzXnMcA6D0oXif22MMS7E1wwA+sjgS9NUCujKyooNfTHG67fh0H4mLUsZkuWaQ/FJ8fqb8rrpOQBlIHxmk7xSQxlyHpMbtg5ZN6on2g9vBACEpp9oLQY4UWdcEBRgJljrMqe0FyvGfX05M9Ug5o5KEjJlyCBS8g2n3NRdGFQ7L5oXwRgMwS33AnndNkkQQ6XakT3SQfFG0blxdOF3yR+pcmkvijxHOF7FPORbwpr4J/awxp+NEdy2P8f32FD2YdWO1YPpGadXxPto+fCx3+5JFg0kJH6j98n4eJJ2LDhsrm8yVTbvceGmWrpvvLsnau/gJDj03hYU/sGJbvbGIQGrwzOiycnK9ZUE0yeste05U86CTzqHYEuzYNSH4Fktw88eAJ7YUjKTX+QvzYEpfx7AX+ysdlb81NQ8yZyYsMt9/tffXq+srO3tT09OzzlxcmcvNlkzJIf+yvL0Wb2eWQ4AM4r+WEmVwk/7hzwUX+RbMeum3+dmF0B6Zht9ujGA1VZqYAANacf1PH/+HNoS5UgL3Se8OmqG3NOI1lxJWsuOnhvtSlCOMVIJzcMOl5MBihGInRETk3v7sRbPMBHTOHxydn7h5atXz549RSG7uL0EZ6sl412sSY2k0L71uLIY19DHqgfgGcqQaIoTtNRogsIlZfC2Ve2KfxrjUhe895UBwqV3C2gAZW6QV0D5kPriCUleyL3XFby24J2FOIO19a6Vr9r5ohJmRenMTM7w5LOnUbTXsqKfrRhBX+SEi+i10c6eH4AyGD2bsfbtNr/FNpd9L4UxncFcY626rV2NoHfTdMha4Tz/LKUa+DVEZvipSmksrJNCm+d+/vlnRtowT2Usk+mmt67rpn7qulIGfGmANQMIk6DqNQ6WBpG8iR9WqmwT8kJ4w84Bffes9oHAaw0lAakBonctHX19gCYYJhM1ZC9ybyozTnR0aX5PiTcR2eP19Y3371fX11afv3xlUtUOGo8IcTkHnjw3FPUVro+NYYUL3QHG8NJlyUf0VmtqWhkQnSYgrh0Q5WWR1j6t5kVQ6CxAsLdgdzKOQDaSFjJUsUGtRB9840HusDwUIOcu2eA3b94Jh6wkbMVrudvmODY2tuiEU2VmvPBZAiT1aULdcQvp1wsFKHyJk53kKUg7cKuLfs0pA5QgwcO+ePFcgARbJbANvsgZy9aF45ntlp7391Dbu1slAEidBMyZz1yKGwVBkwcxkZqMECC9fPni73//Oye5u23obD7HbFKMktmsufkF2sJI+wiqZy0PV3PiddBDFxLA9C6ji8KXyAKsvlSjMkCAm6PTaErondxI3n766SefPU/Vh5VMGdEN4ITNFHgGz2FB1ncEAD7mBtbgwyVXhti77nDTODSp4ItTSlLIps2ozqX8hSxKJLBEU2HICr8Et3HOVX1U0mnKBhekxwJF+al5c3O9A+jEIKgcZ38ccAjO0huShV5UpqLmkfxa+acMplGS+ikWl/g3pK1tcXPpF+4SD+lWmK/iE1AghT4lksLkZdSs016AId0COMCgd90FzHW6BR3VqAwQgBI9YFccTbWxvv7s2TMyF7ZmCBLaAs9HUtAaVOnEULxWNg8B3gEClPGlCJx8TB4Qu7D358JXUabKnFu915tA4otS8VrhdK9NfuL5GsOkQuJwtTLobij24sXLInM1dvoJdA/dZgHLa0WhEiA0pkd5cyZp/w5VvJxZfJHYd5IthV2YdXDY+erQECkAhiM6A1VyRB6Qo+sZwsRKEIMJzTZ9ZA4beg1RtINuBEXGgmIDKvrvf//7v/7rvwRy6Zo7eHpcpUYK4IghO2mx1b96L7LG/krT9YVJoQaUQS/EqxzkvCojFe2oG69Pt18tObOIiG7C9/nzZ5QWhJ9+ePRrFEuFO8EmiSWGe4zthiMBD1NMXln6wKB0FHWDViPjSVV6BrR+9/bd6uqHxKrOoVd35AKhVT+BaYzNYuD4cS6vu4ZGrTZkqQEJwwtEkFjiGM4NDSJAogl37tz1CyjgNRBT1KgMheKtiWHhh1lq+MBqSAJTsyjlYMPYDeZbGcTCGTZWmg5NdkVILSUiQyMnfQAERuEUSgojMGlJK/YOD9QxFFoX5Kz7sJt8ws2bNyxykJn02HVPJ9WoDLEV92B2f2/6w/v17Thwu1pMgLbhUfWnnr8YMNWUqp7szK/+rGdOzczZirx3YH51cXPrYGd35v3qDkbs7/oisi1rCzZoHuxPz88vlg9XV7Rqwz+5F+2NYLLOaLUdXvZpO0nNofA2pL54/tqX6csEI0zTSfit11uEd2L393d296jifnwfYNJnIg5u37n55VcPF5fmt7Y3Fc7OT69vrFoarZXYFYNr6CVij2iWAcrNPzX0cfEm0y56npnMDcnG97mR1uYDTsxuTc46J5ca8NEXx6T7J+EF6zz0Nt01v+2y+5Z6faJ8XS58AlcAJE4AXyxveys4AVNCYfiualmw1y7Pfr5OZSi9wiTdnIwCCJ8NTNN30D1BAhX5EDRThmfPn7FWeBPv/bQ3pTXAiSaRT2UgZihQMI2X/elD3X7gGI5koXwzpTVuISEob7nj3r273vpC81LSEhuXdQtPjcpQQA/RTxuckneMHAO8BB7bAyq6CgzyIb+1ufnb09/yLQtGy8oDfgim3R0gqHV0DX2JJhA+amBngLnv5i1VOSIoIEFnjLA9z9fRjZuJfooNyksq1EGEY20OxfrXMZgauwwmtNd3UJxk8BRvXr/xCp5JDPwwv0QTUlsag6qBjmAEXx1l+Gc3StkmwxM2ugrEMRsZl6XP1vYnZPctai9CGsmworgDQr+pGHVTpkaDB4EKnyJYNfZ1MTKBEKE9SziS7raNkP7ffvvN+6gK001n8HqxLobzKYinMpBFy462ydAHPGoe2qS8ftHfUIFbsMpWvERr53ZqgpLkVK0Q1imgEZAHkugupRGCVa34dN54oXjoQD6CK2l+6AMzyTlYAU1WWYggNJ23PPw1kynwNUzKzdtUwkJv05C3zGUEqLp2QAS3gOY5C09mwOkW4mNWA7paozLYLQ8TaBAmh6tCDJ4NoNQVR0EIsEwF2r3Zubm2c3iBDVhiQq9dJf5NpGKDZynuqruGK4MW/DoFc3adwNNzl3jBIfz00080QYiIOw2Dh36MpH7pZAZIQe0Y07d8lLyUUDVgRutUhrbRzcMkE8/0D00T/bT+Ur7zTpUP2pdthVYJf/nlVyNpt4p7ixFemzFKsCjqDsfGttPQa5cV+D8qcxbjBXSMmM2e/fLLL+bNWKsym9R+rKl/URckgLTEJkwyVJDP74IHfYsmJAoN6GqNysB2SjkAgk9mGkCpFz6yVKxUzrEIlp48eUJEeDYMA3n6a97bN2xSnpQ3PB3ZFXbon84NtFggyUhF7La95u+tQ3gp96u8q8b7UTkCB9SmCbQxRb+QejBUrRX/luFMSYIqoveDgvW2YdScMsMtcA7OhhA1iSjKS7mt6DakygtyHETA0vLj9YJ10daJOGhTKyiGZlzKUHUbdV+/jg9sBg4D4U5xr6yk0QL9rLSRdx6IpNSpDEVkUJmt5ZGJ1GAo3pUYOaej/U03TBIskRj6QBn4biUaIz38hEwettRV881XxoQ2zPEGDxTgAiN4vX27wk65VEGmedj0KA4CoRkksbTLdGCDAqZOZShGiALku+TpjgdC8a46JRwsE2ixhyCZdhRV8xKFVa05MXdtMOEcKmPWVReNVUZ8guU3gS/7c+NTjgIkePEPSlIZVEi1aQy26CgUddKOIzDQUkRFT2AMShlqnFoOHpQdwtBLflSZRinebWegLaMdHMIjYwbTrDOzU48fPyY5pIc5sxBXYvFZ0VK3zTdcn2CFyraFzMSRVRSJr+YkcIQIAkmmYcCyOwubDqchHhJIiu+N/EDAqdEzQIkyoHJxztC0lNiKuQdC9w47DbBLKvYpxIhbYEodgcumQoeKEC1VmLSqcoeND6QamCWggt9Bhn/7298ESFDAEfGe8BVUkFKnYfBKfzGAQWqJMhRIFQ9mGFajMiD3wSRaby0uzd1/cI8Z+rD6dn5+1ilVu3vbNu46E4rFLR8Xjl2tzFPDzDi1OxaK5Pjb2zPIibkO+rCxvvW3//P/+d7VxMHM3q5XYbj1ORue9/eGAuZAxH7yj7viEzOiZfPVpI+xzM0ugvyf//j5f//176KShYVbBxMLu3u2n8xNTS8cTM44WNtftb+9nYkW+yWYtFEKmMq4JYxjmeFV6AV0r8hb9aMSlBPBT2VN3YU1hknBIMbGzsSpKSMkm3KhvbG54XgiIyV5loBVUMei78TszNY2Kawb34u0H1hMTDju6ddffnWe1t27dw0nnDlXeCYojzBjKBMxNmc/Oz0166xIoZFVBaImORmjxOugbojiRD85Hr2XbZGoh7AKgUAMzLLkkMavQt6ieWGoVxkqETGL/PDhQ8jjBx3IMMPdoIUUKtMQVyqQus0YPBhJY+ri4pKvDnlnGPDNM6xbsMWoSIvsf//7/+/sVEa5kDwsdLdN9VIf05PX4jHt6D0B4HupK+OSmtAOUXvp6uLP1qgMRVbCJ6b5t7DCCcq/fv2SQ2cYpCTN7k6c8Mh/XhyP+p8UL9EHkzDXry9PTn2FffgLkWaFqjs8CRzvu7LyjhpbUjCxVD4rOStI766h3moDI4dbJJ48pBj45VWXFhdv3PSu851811k1J72qk6rSW7ddP12jMoAlUWIVYM450AeFxp/eOSZYFIM+KIlPJYlSB/Vx9A6IBpHUW7Mx//ef/wT1o8ffeGfatNJA2NYByFEFeYnXk38/4RnkrRuiOVGLl54bNMLMIlOShAJPWknvUdCNhw/uLSzOWXFzl5yAOTWhQeg+0rJeZaj6IeyCRRSx1miBZXV1bWXFJrG3SFOiJvyZ3o2vhA1pwr8CZ5wi8WblzW9Pn968dcMWSxgxvUMKdDFGKPzmzWurh4TPbN7W1jZ7TOIahjmtHupJzErZoHr/1q2bC3NTB2EK9xlHRKYJNKRIywAGkPUqA8xhCHlIytAHRCFAt2/fWVq6hj0sFj6JkWJI3R7TNcynDrsj9IQJqyC18ubNL788MStw/fq1Eiw1Gn93CDBdsAPPDu0d/5RXM8qOEhFIrPs2nBCN7Zfahp84kHmFJDCgIRglhZy40TB42V29ygAxKfHnoFFEYmRtbTBm8p4rTZB8Ysw7+MMpUBVXcpGEYuPo1vb2yxevHj50Mvt15cm8wuuYHpHkqwcbywAjpY0sgYHo8wMOGLeqBRwhOn9A4Po3WdoFZkkcvwAjCY5so6Ui50fffOkIjHyVwi1AljgKnE37LsjUOKGbjPFLAXJCjVZI5AmTTKXxDIZNSrJm49aqC16qWubsw6Nz6GzZ9vbuh/d8WogXLkpBzTJdOBDDhoaWdIDB8RZwiH68tffqpZebN91VTm+xglFKaLvDv7faKIMs1EAzBMAvoacP//rXv0TL4HQLYTN2oMa99XbBpxvqFTMqAItGhAJIog4uwo43Kw4fa1RVhy4TMBfjmlPjmzgKxizB7OL3QhybBzwgKxtpwaB3ROYBiNeHD+9JWqpH0dKYCVC1YQh1nUnXaRBlEMzxPJbbJMqgHOmKT2gavKRGQ8pwmPTBtPYSYzqHmemZYjMGQ4LDsJ2bB3hALpEtAAvwNtY3CuQtfUhBVO3cdmq5mcD5JXM6AKRBDtP77v17MsfWgtktFcgcNGoB4txGA8ICHiqBBBhLi0tfffWVeVUGkYoWLQ2PgaRqnttYLTfrHTOcCnL66GQMz3Dt2tLblZn1je352QEM7E6F8PzC5BOO2utGH+y3otLFpMVcE+xSJc5vpI67ZuQ0W5QhlHZtbd2uKoLla7ahACVeBV6IWeMhqd6BlOAFSDmh5FSY28szswFbATvm4outGcwAegAWopIVGZhb0J2bj69NNs+hLiWyNcsRoBaZE3wb+WSki5dwweO2OHbZds/V07iAITOApKhGz9QAVH5LD4rtyaUMTWtDWsAKS0bQrpaHX3wButjDVjxt+ljQipSO1a8erDUzAGUocpMGIGZdbFVaXFr0HW5TILWi2nvjOCTW0A6Zy9Ysom7v7nARUvKS2LUlr/cOu2ghtfEjYPv7ttnyDEpY24A7wicnxJC8wWy2BQN8cJwXRSLk2tx0bJmwjfzHMEYF9kVQlzW7QL5PVQelDC0jilWzsxzDvNVcGCFX4zarK0IKgQhV6EMxYFZyObT4ChZlsJMA+Mqnjx7FVXAqel5nGJxQVcggrJmZtXXbbFsbIvlhLi1krqrUYCYMRGuNGQjxovnTp8/++c9/GtVsbKwX2II6CWFxvQ0C1+5qAMrg8GdYJ/7FMMQ3Ka5dX9zb3pzzpclZtmvPn+m12ANu889pf234m/uXUDGrhIzY+Q34pyYX5xanJqZ5i52tnbXVjfXVDZnJ/SkgK/QHibnpQEt+35HfPesDq5mGE+kyIUGUHIQHcFqkZSyZldfvnz975btcLG+pBmQZ4UcY4PqoBj/m4eSfD+oiyoyh+ySG7snvbG+8ffP6l1+f8AbgSXkAW47/64PwnJYHMIAGDZylCixrLvZovHktEg+6MAxxO5Yn0fZjtap+tHD4opE8e7KPOAAAG6FJREFUiFrS1/5HJG67DykHccC7i8E7/EIJo8pKHBGlA6EAB7QiMv2AW1/HMAaRYCNNjNNWCP4HJnd1zfZ5s6wqF0oee6iuy0D3REqHBHIJeOCZKgZxfd1azRHCsjVNQnsY0gF4BsFi+nQ4IwRoxEnLyzfMr1l68ZcRSFXnMLgDzCeHknNg4/eNAoOp4S/8I/yIuNzMoEE1OJVbP1G4sxNzhfE1iBkv0xyX44thpCOm1G8SEBgZhZuwjwlfy2xlAUu/gwo5juFVVg/C/CuvKFmoFJtYlQe1yii/QupYCw1cDkAZEquQoRJEQt7oeVmodP26PP4VGhXL2gABuumC5EnABqRRYG4iCEUouGhJuUhdQCyTNfNWec7eK/FDr0nv2aZMJpcS0tFDOuAXDJlS+Hrtsk/PHyYIaMHGmy4vX6MDEEkSZR2Xfeqzu2YGoAyJPDCx0C+5YeBM1dvJSMIOg480hy8Hnk+epeQBeGHBu5QtEFMuQYjHMErD7BKy6rsbX1vux6utmtJgpuw0xUiPlJC3yFtISuAGJFSnMirAdAN4wEYlCXFMsLIpboH2cIVTm6i7cADKgBxBmDZpYIguCCRSsq9djFTUI5haVaubCp20n8D4BRiAwSmui7cCiq9IHsNCU+owzGmh8Tj1gXB60buTjs6vo3EVdJc9ugQMitEEnZI03SnMr/IISc5vrbG7CfZh7RVk3rx1C8cPW0DV1IFaY4Ad7mgAvVb8A0einSRYWloUKSEN1iohbSrIHAZ34PkENZUBO12SclAlC0NCi+VTXgkofcjyvgCvZb37zdY0nh3JKMneczVQeSpnX/rtYyPgB5g3vT5/8CDVFfBJQL9Qq7DrY6edNDUAZUj+VczDP4DiLTXwhoDfZHaWd4JDk3USbD0CL75H5jrc+xFrnRLpBlNdQpeQXYXig95Bjc5K0pQuSHz2kiD5dVOZqIme9qPD3kGOFkh5JsyVuC8+wWst8lAAs7solPC77E+vXbYygKlVqLKp4AxBKvYMIVDEFhpvOOCihEaq+XUL4bpEqpbqwACt4MdoQYBkxobvInDVMWKHWZj5rO+XhkMZs020ngQOjicLlWQjx341UkGSmqB9hNKFR+heWb6c98KAQU0ZQpzadl2FMV120Joyov1p+AsKsVuW3gIVKWzOu3XrFsQVKkk1cBdYChPlukA8u90BKMMZwBD9CCfIGf+QO8wyDhkUaY7BSeZSM8EjTyJdFmN/ihk7xlGPEFyPTJVXmBKjw3ipf6y76jJvHf7Ve6oBMZJXM0NKdUo1yzSt6cuqkcYy0MDEJBHQCjzRuUxqAlNi0OwLPTHiKsA3BtsnOxoWZeAYyZitDIYN/AMLxz8kjw8LzSfxqa8CzoGHQCc8mIrFxUifPizG/qzpQVJbPP+BB2SqW1mhkphzgM866ktaI1gpW8qllL/M+6WdqvmRP6fNmm4xFTAslIkIMsEDswRO/OUWzBwqh0J5BaMmQLpudliUAeAIR27Sh1IGJ0X7RdOBcPQkIcFW8dVdE4IkjRiC+mTlwyUpvh43cDiIj6hrJmQ0Ufskdh6XPO6RzHALeZk6kOV+NZVtlicog4JTvJaa9SVdC3+tL4JNogBRUiBHMYNmlu7wXOoAlPVs5IdFGUI8yvSz2Nersdwoz+BgUJCnfzgbhSbuJEdTcAkimRPL6VhJFp4KhFv5oLsyEdRstzSKoCj0m3WqaofbUZgpRaqoU7gFhSFoh4abVYkMP1F6a1oNKshzZRFeQKW3QHLLcJlDwFbMhYihlwo465TR6sGBZ4ZFGcgVwcBlFCRqaJf6wDkMnEYAwDnCh4vAk8RIlCFMfTk88xwIUxT8ZrLWkNKfjyjUprzMyUaqWzJZwa/Hq+QReYWSPAhlijLkKz6hM82H5aml1AChQIVWDNyDBw/4BDES5uJpgTNI2pe1l5Oku1jJsChDoU7MrqIUIhpdUQZbbJ4/f0YUsDzRK7yXPUV0LoZ/508lj1MucRSPiWiZMDmzjZRat1OsrYClpEJ212lLsYc18CrVTmmEOAXi+V9LIRWESyFDFjjimaKNSgmfK0DSC+1LCXDUqSsBvmJElZcJ/cREWIuI8NGImRoActupTeUR2xnpN//gOOq6oOu+3WFRBpCXw6SwX+Zgb3/X24CfP7y/vrHqELuQCQ61HO1gpyidUaNzZB1h0Xllw87kcMih/9upGPGY79reca6HJZFFpo/4te+f92+RUQPnj3WIy0d/EB0euteuRc6jNJxB0QjglEs/UwftWdq4q1rcoaIl/IiXZegqujmcCtEOIaFW1ylIf1riJmMyLVxTmAh2AQAS1LY2Q+ivL1//7N5nd+7eubF8Q+HO7k7ocB4dZCQY3g4Th2LePPEbFmVIPu/tm16wAX9qd28He70e/eWXX/g+ue8J2M0alI7ju/fL3S6IeDozT2Nwu0zn/j7KKInDe3fBCYDrN64tLM46DCCEoTN9OKxX0UtI7yfSET0DS5h8j8T/OrXnqfV8lEchlSP65m9NQnCtRlyOheVFWtV6+OdUYL1nEvSInvPNE1DZc+Isjh2vTthcc//+/Xuf3YvvzO86UjIqT3tZBQIFi3CqTtctj/cAWj8fHRZlaONUyB42NDLsnCMcZcpLlWt0gU8ok4q7s0ffJms/3p9/K8ElZJmvpG1zK06iv3P7jhiGryeUrXClPz2f3UohzMefwxWDWMB0M85rQyKGmmeQZIqVOVy7b3n+JzxCicf0K4Nfkhcr7t9/8Pnnn1tWUyfjJb0WAI8K/zBpAgiHTRmOs0oobKj65ZdfWlJ12pT9+uhuF9POdh8M3vHO8vqo+KQauIPx1MAGPEeo20fgEiQEsB9bUU8HpKtSwITpLcmDPEMaDtOt6Uy6aq3zypWxQCjJG+2f3b+XKwkAwD5JOdJ13uagag61MqCgV1U4fXMRflkdLqJuSgV3iyM/3BF2snBK9nb3l+/GyxeiJoLX8hzFYh+u33y+vGEXkVJ2beRq65RvlNg7cgKb/kBX3E5gLsNdo4ZOb9++9c23XycYORfSn84aaWWolYEIsi5cLVIgtBjUJf9gaLgwb5q/Li9bWbuKBSDBcpcictMjgGLvaI0wIYavQ5AAVsbKLWCoqxl9L73VpAkwLuOTWBJFmeTUXYPle3eYLbeUFHJF+JQVkqpDQKozQRh2ZUBZsPMJfk1UC5kI4vPnPndSlyacJFXyNblOJ0XDGC1YUjPXDU4+0nyJSTYBiX7ByXxY671589bbt77YWyOhSLnuJGSxw+LuvbtmkICRhRURjl1W5cOWGWplQET+NxnMC+OxQaFLGzd/+/VZTSTWbIZJMtSgrQmtuRrbT2/fua3QORRF+MJdDEXipmILbSxpoxXPkO/NtBYragAx6UMfGCxeiPrpdGF+YXvHaUix/Ic+7gqZ+PN0DjVA0c8mh1oZIBqiWZJMRu2Ghl9+8eXsTMyjSwLT1BO1UL/y3XiQcpy/2Ugnv7YQeDb7zYxmy1sDvpk552xQc4UMYR6Ahc0NzSZ9CnSI04Q01QjiS6VlCX/rH//4Z4aablnERA3elZAmMT/Vat7nW05xL+wUb5BnBufKmjYdr14IHuMr1JNkuumrM4jqqTXsynAq1k7pMn5gjdgkJyjSB3EU8UX6NEJph5QQC2lrRwh7akunFGpFqRZIgBawlip4p9IQ+sa9G5/d/4w2CtAlYDgAxg79tsKe0lpjRQFQ0X8LW1MOnZqOL6w+eHD/w4dVCzXOjXF58+YNqwAmIeCYaPYCHkddqBQzV4Um04Se6eqY0r10XsuzI6kMZNTsKk1goamEIbVJJ4Yq9YFMYJKUckyUfee4c+JpJBaxYp4oHpLRlIxm2b/P7t3TrwnWck9k0ocDL6KpnlPiHnDvx8SXpcvFxYWZmc/ADn7fZUQflkI/gWA/ktbQFlkESLiQZA8di+969aeLfoDZRRtdSEkXrdZcFcczOoogdWHBr5eBGD9Rk72uXAHG+FWNWPh1kFwX7CnST2I0Aw+SJM8K3ry5bI/N4tISqXIaSwhdbNTrg4ntC7USzoJ17I+w5IAG5ld9cZj2klffgfYpaxUiTEKXouG9dI3InIxRnMFJe4FvJHWgIsJIKgPoCSKhxA9iaqWTZRK8vn79mpfAaLcYrSLQuD8dH+uoMP5URstZxeMlQIpts9r/7rvvTB2SfeMHdbznoxM7IRyi6/dTrdZ+nzKkCeDDUEBK9UAh+sBeiGSePHnimD3l9LgvAOmFpmlZa1qNX2dF9snz9AXCrhoZSWUgpnPzC+VluHgbjlnCEhksx3ijCMvVvISBRMRIId1dbGTSOAr6xd1sgeUTfH/77be8kJupCcVvOFB1cG9YHuVzNY4HOY3dK8f7AVXYooQy//DD9/a2PHnyy08//RSTBD2/WY4+1I8mlLgxJv1cshQ2Jh0FbWSuRlIZUFckgPQZBJN4to6YeheeYhBZ2yRFTW/f+WzNO668hP+xw7KjRHaoT/yYcol5Q5tBfvzxR/KkF7ZQv5oqomBPUFN7kz4FOltA9IEHNhi4LDsLgTplegG9wO/rpDB6/er1m7crKktqSh4IFQ+kP9XNofua0otm/YobWSSr4GU01U0rhxoceHYklQHfuAXxT6584Yp3qpBSIdaGMtxcvr58bfnG9Xfvrq9vrL94+cYXLwu7j47skvkR5cTNiodiCGcIK7x54/qdu3e//fabh58/2NhcjyPxJg9YvogzDvZ9WzwkaThSeANDBXCZBLOLdDIWyyMz5VCPPUfYls8fzi4uzH/99RebXizY3rZ8iXTOhqVCBJplkSm0KIsnBbdoLmbVSljVIlf0wRy5zy7YjFQmouMyDlkeGoJcgC0jqQzwJJHef6AFgTOulDjIshw27ext2zNM6n1O9/6Dz+jM4sICRyFc5iWK2kQkUewh3kUea8l3i3zhcyZ2tjd9ceyPf/zx8ePHWtjYWHW3RBaMaOwSze3HRSxazw32H28KBEaxLS+xQRl47R7shmbMz894j9BxSg6sefToq+0d39XcZdR9G+H5ixfv3r6bnNidmZpBhtDyeB0viEoNmBn6LwCK6yLoQaaolftQDuKbxVMT8ws2CsR3UGfnhmXh5QLsGFVlaKN6xDCnaBZJjfsYxtRJX3z5Bf/w/t37Mgm7WpYl7LBn4+M/if2kTsFhorAr41WK619E+tJXtvC43d0I/ZuUoR5tmEvs5AJBHj1+xFUUd3HdoZ5GWeYejLLcRYCQ+jD6ES0exAAgqCMsrM4ORDE1RKclYowO0E2JxFKMbhp1ZeiI8iaXLB4bPtpabNJpZWUF72XwEhd9VOEjL/mciWCzEpGDEYgM6ZHvqKehryQWkoREq/FtFX5yj38wCPZrehpZysTxtn2oYSPKZB0KZFI58UsdSOmXV6hCaMKIp0vC4/O5wKhlBZwzy07E7fmzTvfixQuhc4mdYj92SIkzTiZiHillglaQAGx2S/78Xobzbspo2PWyYBJyPBHoLCwuWquBexp4GyusRZikRpZcxHQ3Z6hbZPFkW+4TUw26FQ2WmbdUHloxnHToBKoroQwpyriOu8TdBBGVyH1sRhKCBIEQ1mKkzPxs7JOtGD/SPCamEnT8hjYUHOUtJ8aU6Ows0c/9LGkLrCoiC61QjjK5lMkcSCn0nq0oozVPpc9Mz5B9dSJ2w1nnSigD849PNpyG4Z8SBe3htBLvDNncySjSgSIW70mG4SNbh7ueojyqScxkcn04uXgWVLCQiG9KcKUMvuVgYMAoUAkVvAPEUJB4KKspZJKKl1g3wuYokEs11kQLqKGO36plGeksGEao/Eoog+gXF223293djtFyDCUt09rFuba0tPDZZ3cFTuRA1PTq1ctfnvxqFIGFQghKgvGEhhyMIr+LlMaL0Sm+kFIiv7Aw56waH/qRt/PaSR9GUMzB+nosU3Ik1tf93bhxzcr1jRvL7tAHWoEmKOkpbZaacSRHNouq0ihSqVLXS6UMmJSMSQOWeewpCuBma4QXkyQWpadnzJFbozN6tGDEFsqYP3r4+RcvX7x8+vQp+XDABO+hEq0YRc9AOiuCpBq0GB87r+KjoGX6NfJW5ObmghqMgtk2a2c8AZmH9fw80Y/VfUEUOph04j+rgZYSMadANL0oKiN+q5dR++dSKQN+p3EKG3VkcoPFwqNgEm6x9Lhrwc53qNpRL6GJsAHvH3/7+N7de2InhlAUwf5pCqfxe9SY+xHeYwY7F1WKMlhKjwhHnjIgC0zJt7CQVniK1YA+3BFKRihlUCGlVsiUOuF83Irao5wulTJU4p6agDeZqRiUfC1OIHbUKA9/UZJn1fdrOU+4TA0EBu6mHLCZVSOXIFMtxRzDBbLMgV8kSn1gCySCLmVlpEAcecNr9FEf0SiDvDpBwOKfj7U8EpeXShkOUzyFWwlRT25hoZ0aeJmcq26lPctflTc3NklDViMHqSo0RyOH2x/p/MedJ0fRgCN8oZ+pKEJ8rJFiEHROwy9CISAdSLeQDSRxkoZHmxylq8umDCm7OIAxyVqGKnUgf9OSpbVzSzUpOebS4xal/VaFJEBllx4cJcZeCFayHhRIOpR4khWQklwCJ0OFdviUwVXQG50lJIqLI9HphYAY3EOXk8EYk7zBSDbeACC9AVZhG2q79Jt1kvhuEQJpZurjW0FZ7VjNrD/av62phONIpMIjS1IpRLts4kBGVakEj5FDZ+XMRFC5mJL8LdU/2pHjrQ/99eVUBlwJsZ6ZMdEh8QmFax957K46FcuxyaVf5i4Ox40N4S0DmbdoBd4PPTd7BRBZggiH5DvpphAFJMqQd9GWblAMQZQSD0roybfI9ArHgJ4fWWXAsPTIGeQQXqlIc1AyZk5t8I5FpWtL17bLK8vu41OpFVVwDherS/m2bsSmvUwZHqijtdHlcQuZDv5BgcNUSpq0Cq3STcdwAh3sbozdqYV6oibb4+NshEPf6dFVjNEjJXtKtvwoN7Odd/xT0f9jjcHlhkgZcs/8MVLgBPmNQmQrKSuQ75TsZJWFAnJvKsSq8dr6mopeaLCtwDfYTJu35LiwoOKORoNhCtv8sivpWO9hDHt+I+xYm8NweSqpW4CFUWhNHGVJkGX/YHc/HKMD0qft3mJKnLvsqLLry3avhH/gIbY2fScgqBo7vttGR2vFyiSbgprGaWXnrDkqmZnZIZLAIQKlxYyj/6Aeyc6yyni7ZPXRV4kQiBpIeCMpXFhcUIEN82zK/9Emx1f9oQC3qSE0Nw+L2vPb8znZkCNsd3Gn6kk1eSWmI2TwFAf97h5VvKr+QDLDrgxi0KRLpRKHyYTE1IAHoBIqJPWruDYfoRKHHxnn+0WBHB6EGpSz70m/RB8oQ2ZSN7I7RiozyZRTudkvwC7czrArQ0U1mSrBFsXxgCYYH1dWB3sQvbji1pypR7jsihMXJtP4wZMUSKeNwm4lzSvFwAiaIMWIojDF3hamCi8k9XFEeZTU+Z2NkzCfXzJKygCTtP1+OVm7Zaz7UAmXKe55N3UmKe63ujyfEOO73VKAWCM4mc4H0+6kH/abcSy+UAl1XFIMyWUySx1WzFvb3fZbX/1hVwaEq0Qc9VHcpUzumBAdoak6aWbk0/AkvZJb+Xh9FLyyLScv8hcR0FnCCzESRkhk3d2MWo0rTMLaGJtH+ChXwa2tPJtwOIg4pMqArBV9yTSyIhzrIsmjowy6szTuShVjPJWKoUR5YdDH+dPhoPklgYLQJ5HROcmOIzIp/ZmvKlRcY79ydYKLkLnqYVLK6DGJSOlXKKNCGJYy8DUkIPdZgqBJUyTGiYoBLuX9puhXTWWh33GqlQJJcPTPlPyteEErlLv0G8asLP6ki/DqEMulHK89BUiVKx1TnmDLZF7lWhEZgGdI3CqRTaoJHZUXFYhPLEuoRvSZmYq4CKFyqoF8akjVTkWmJGtWrgrHmb5TIOncYl9pPUU2fYKCvJRJHqVWeApzMRp/r3m7ajNewubhUyuSpxWoWshnT3K5qtPHzACUAWJJJpkkXBCovJecNEIsaqBQyuFXRdYqU5HgZEl1a5wZHgpgU7Ibx3GZjcNibn95d8dWcK+OmG4CbQbDKqdgeCSfyowHa8VoAMqQ+ECPMZBYCHGkbwY61tetFG60YDDcTZWolQTjxhugAD7qpZJsYo3RCk2OK3dZvUFBK6iEpDxVKDXBb91wDkAZKoqQeOYhV82uLS0lsShAIo8QSbJUj7oJMW6/VgrgaUq/X3zH5WS3ATS5d3KPncXG0+klVMikcqbUpXyqPjgHoAyJDCRjZFBO97diYOPX/vaeM9skFSBPEySXDZiE+ug7bhkF8Fq465ftc5nGLn+tkuK1RCWwWwUuwvQrxVA/uZ8VXNZNzAEoQxoJyAsZJZ4BCXwPanq2HO4Z6hAKIOVYuQEq1E3lK94+VmJipooURF+JvUnYneEAoacJ5IGJdOCfM0qoBC3yuDoqV8/WlBmMMqQloAnOjoekFwi8f94aMhefANskQU1oj5ttmAI4XvE0o53w+1zB9IxP0FUflwhDuL9PH2y8t/tYBZOwbGKqk99awa5RGWLrtZOy22EPoZeHj3Jbfx17Cueyb6Wc3+gA7aBDaztXrTiPG2+eAmnXU6YzWAJDsLtwvGXzOZCySzy2fU/uX1tc8iHdzesbsSix+sHJsOZaPFK5kUrBTnUaZ73nfQ7uNSoDQWfeAYoEJpIBITSCj02mfAJXWMgBtdbRhedAOb51pSiQwu3XXgNykiZyfm4+d/tFeUnkykkF7hIq+d5JVKMyiHTAR32BC1YuzxSqsbKpgxgklHKIyfhVwW/v+IxbuBwUSKmACwMqnz5BhvRLpIVQuZTU6YsmaKdGZWjLeYg7NRAU8Qn2mdIE0Gfg6BYlSWXoF0qXQxquMhaHzaI8gSEkCOI3p18daWXbH+GhKu6mLPVOsRqVISx+Od4duGYJ+ATREeiBnnIPz0TSZZb0js+4hUtDgYyOoENOMjGpKf0kyopE7k5IEeoL1jUqQyBzEG4uJgeuWVVbquCGWyKZapCaXd3tC2LjRkadApV9lEllICfUIF9spBi5icNc0wiMGYwU+DBxkWQYlLJOQ1ITsAqSqf38XVU46iwcw98vCpCKqqk0moSEPign/UaeJEp5bmqqavaS6ZtnSNAJd8o3mGw8NVtMEwRI0KhE360UfSXyHsxn87IXZMbPXlYKVLaSdHEFBIaLIFdmKYmN36pCLxTojzIAMQ0/yNKX0V0b73waSYm7Ke4JceYBfSwu6gs+vdBi/OyQU4DQkytiI5NiJvY2ks7B9Ang20c3nbhxVkFM7Jx1r/NywKW+EnTgUlkOwcI6+Qa6X0lr+dt5s+OaYwocpkAlP8QsRKpMNMmT4b6IcWwaPdzfBfLgyBU0z4LPiJm++vjFfHsX7gXaHD8ypsBJClQxRZURLBFg6ViU4dkLrED/P+yj2h5nslRnAAAAAElFTkSuQmCC";
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";
3171
3487
 
3172
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==";
3173
3489
 
@@ -3469,7 +3785,7 @@ const deserializeValue = (value) => {
3469
3785
  };
3470
3786
 
3471
3787
  const FileInputWidget = ({ config }) => {
3472
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3788
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3473
3789
  const { translate, translateConfig } = useWidgetTranslation();
3474
3790
  const accept = widgetConfig['widget-data-options']?.accept;
3475
3791
  const multiple = widgetConfig['widget-data-options']?.multiple || false;
@@ -3709,7 +4025,7 @@ const FileInputWidget = ({ config }) => {
3709
4025
  setPreviewFile(null);
3710
4026
  } })] }));
3711
4027
  }
3712
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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
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
3713
4029
  ? 'opacity-50 cursor-not-allowed'
3714
4030
  : ''}`, style: {
3715
4031
  width: '100%',
@@ -3770,6 +4086,13 @@ const namespaceWidgetConfig = (widgetConfig, namespace) => {
3770
4086
  if (namespaced['widget-data-path']) {
3771
4087
  namespaced['widget-data-path'] = namespaceDataPath(namespaced['widget-data-path'], namespace);
3772
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
+ }
3773
4096
  // Recursively namespace nested widgets (for layout widgets)
3774
4097
  if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
3775
4098
  namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
@@ -3974,6 +4297,9 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3974
4297
  const isVisible = shouldShowWidget(widget['widget-data-options'], currentSchemaData);
3975
4298
  if (!isVisible)
3976
4299
  continue;
4300
+ const isEnabled = shouldEnableWidget(widget['widget-data-options'], currentSchemaData);
4301
+ if (!isEnabled)
4302
+ continue;
3977
4303
  const widgetId = widget['widget-id'];
3978
4304
  if (isTableLikeWidget(widget)) {
3979
4305
  const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
@@ -3983,7 +4309,8 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3983
4309
  continue;
3984
4310
  }
3985
4311
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3986
- const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
4312
+ const isRequired = shouldRequireWidget(widget['widget-data-options'], currentSchemaData, widget['widget-required'] ?? false);
4313
+ const errors = validateWidget(value, widget['widget-data-validation'], isRequired, skipRequired);
3987
4314
  if (errors.length > 0) {
3988
4315
  isValid = false;
3989
4316
  dispatch(setTouched({ widgetId, touched: true }));
@@ -4016,6 +4343,108 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
4016
4343
  return isValid;
4017
4344
  };
4018
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
+
4019
4448
  /** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
4020
4449
  const READONLY_VALUE_ROW_ROOT_CLASSES = [
4021
4450
  'TextDisplayWidget',
@@ -4227,6 +4656,8 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4227
4656
  }, [forceExitEdit]); // eslint-disable-line react-hooks/exhaustive-deps
4228
4657
  const [isDocumentsExpanded, setIsDocumentsExpanded] = React.useState(true);
4229
4658
  const sectionRef = React.useRef(null);
4659
+ const baselineSnapshotRef = React.useRef(null);
4660
+ const editEntrySnapshotRef = React.useRef(null);
4230
4661
  const [sectionHeight, setSectionHeight] = React.useState(null);
4231
4662
  const [editSectionPosition, setEditSectionPosition] = React.useState(null);
4232
4663
  // Capture section position when entering edit mode and update on scroll
@@ -4291,6 +4722,18 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4291
4722
  panels: makePanelsEditable(sectionToRender.panels, widgetsEditable),
4292
4723
  };
4293
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]);
4294
4737
  // Handle edit button click
4295
4738
  const handleEdit = () => {
4296
4739
  // Capture height BEFORE entering edit mode to preserve space
@@ -4298,6 +4741,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4298
4741
  const height = sectionRef.current.offsetHeight;
4299
4742
  setSectionHeight(height);
4300
4743
  }
4744
+ captureEditEntrySnapshot();
4301
4745
  setIsEditMode(true);
4302
4746
  onEditModeChange?.(originalSectionId, true);
4303
4747
  };
@@ -4472,8 +4916,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4472
4916
  }
4473
4917
  return { records, files };
4474
4918
  }, [originalSection, hasSupportingDocuments]);
4475
- // Capture baseline when entering edit mode (used for isDirty comparison)
4476
- const baselineSnapshotRef = React.useRef(null);
4477
4919
  // IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
4478
4920
  const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = React.useState(0);
4479
4921
  // IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
@@ -4491,6 +4933,9 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4491
4933
  // Set baseline when entering edit mode; clear when leaving (baseline captured only on entry)
4492
4934
  React.useEffect(() => {
4493
4935
  if (effectiveEditModeForDirty) {
4936
+ if (!editEntrySnapshotRef.current) {
4937
+ captureEditEntrySnapshot();
4938
+ }
4494
4939
  const oldSchemaData = schemaData || contextSchemaData || {};
4495
4940
  if (namespace) {
4496
4941
  const namespacedSchema = getValueByPath(oldSchemaData, namespace);
@@ -4499,11 +4944,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4499
4944
  : buildSectionSnapshot(oldSchemaData);
4500
4945
  }
4501
4946
  else {
4502
- baselineSnapshotRef.current = buildSectionSnapshot(oldSchemaData);
4947
+ baselineSnapshotRef.current = buildSectionSnapshot(store.getState().widget.values, namespace);
4503
4948
  }
4504
4949
  }
4505
4950
  else {
4506
4951
  baselineSnapshotRef.current = null;
4952
+ editEntrySnapshotRef.current = null;
4507
4953
  onSectionDirtyChange?.(sectionId, false);
4508
4954
  }
4509
4955
  // eslint-disable-next-line react-hooks/exhaustive-deps -- baseline must be captured only when effectiveEditModeForDirty toggles
@@ -4529,56 +4975,103 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4529
4975
  // and handleCancel.
4530
4976
  const revertToOriginalValues = React.useCallback(() => {
4531
4977
  const sectionWidgets = collectWidgets(originalSection.panels);
4532
- const oldSchemaData = schemaData || contextSchemaData;
4533
4978
  const currentStoreValues = store.getState().widget.values;
4979
+ const snapshot = editEntrySnapshotRef.current;
4534
4980
  let newStoreValues = currentStoreValues;
4535
- sectionWidgets.forEach(widget => {
4536
- const originalWidgetId = widget['widget-id'];
4537
- const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4538
- const widgetId = namespacedWidgetId;
4539
- const originalDataPath = widget['widget-data-path'];
4540
- const storeDataPath = namespace && originalDataPath
4541
- ? (typeof originalDataPath === 'string'
4542
- ? `${namespace}.${originalDataPath}`
4543
- : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
4544
- : originalDataPath;
4545
- if (widgetId && originalDataPath) {
4546
- let oldValue;
4547
- if (typeof originalDataPath === 'object') {
4548
- oldValue = {};
4549
- Object.entries(originalDataPath).forEach(([key, path]) => {
4550
- if (typeof path === 'string') {
4551
- oldValue[key] = getValueByPath(oldSchemaData, path);
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
+ }
4552
5030
  }
4553
- });
4554
- }
4555
- else if (typeof originalDataPath === 'string') {
4556
- oldValue = getValueByPath(oldSchemaData, originalDataPath);
5031
+ else {
5032
+ newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
5033
+ }
5034
+ }
4557
5035
  }
4558
- if (oldValue !== undefined) {
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);
4559
5046
  newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4560
- // Also revert the widgetId-based entry — useBaseWidget.handleChange
4561
- // sets values[widgetId] during editing, and useBaseWidget.currentValue
4562
- // reads values[widgetId] first before falling through to the dataPath.
4563
- newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
4564
- }
5047
+ });
4565
5048
  }
4566
- });
4567
- if (hasSupportingDocuments) {
4568
- const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4569
- originalSupportingDocuments.forEach((doc, index) => {
4570
- const widgetId = `supporting-doc-${sectionId}-${index}`;
4571
- const originalDataPath = doc['document-data-path'];
4572
- const storeDataPath = namespace && originalDataPath
4573
- ? `${namespace}.${originalDataPath}`
4574
- : originalDataPath;
4575
- const oldValue = getValueByPath(oldSchemaData, originalDataPath);
4576
- newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4577
- });
4578
- }
4579
- if (newStoreValues !== currentStoreValues) {
4580
- dispatch(setValues(newStoreValues));
4581
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));
4582
5075
  }, [originalSection, schemaData, contextSchemaData, store, namespace, hasSupportingDocuments, sectionId, dispatch]);
4583
5076
  // Handle save button click
4584
5077
  const handleSave = async () => {
@@ -4592,7 +5085,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4592
5085
  // This ensures we use the original widget IDs and data paths
4593
5086
  const sectionWidgets = collectWidgets(originalSection.panels);
4594
5087
  const currentState = store.getState().widget;
4595
- const currentSchemaData = currentState.values || {};
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
+ }
4596
5094
  const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4597
5095
  if (!isSectionValid) {
4598
5096
  return;
@@ -4658,7 +5156,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4658
5156
  if (isDraft !== false && store && onSectionSave) {
4659
5157
  const sectionWidgets = collectWidgets(originalSection.panels);
4660
5158
  const currentState = store.getState().widget;
4661
- const currentSchemaData = currentState.values || {};
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
+ }
4662
5165
  const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4663
5166
  if (!isSectionValid)
4664
5167
  return;
@@ -5074,7 +5577,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5074
5577
  color: changeRequestType === 'new' ? 'var(--owt-color-bg, #FFFFFF)' : 'var(--owt-color-error, #B91C1C)',
5075
5578
  whiteSpace: 'nowrap',
5076
5579
  boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
5077
- }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { 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: {
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: {
5078
5581
  marginTop: '20px',
5079
5582
  paddingBottom: '30px',
5080
5583
  display: 'flex',
@@ -5122,7 +5625,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5122
5625
  fontSize: '14px',
5123
5626
  color: 'var(--owt-color-text, #011627)',
5124
5627
  fontWeight: 'normal',
5125
- }, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !hideEditButton && (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 && !hideEditButton && (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: {
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: {
5126
5629
  fontFamily: 'Roboto, sans-serif',
5127
5630
  fontSize: '16px',
5128
5631
  color: 'var(--owt-color-text-muted, #727474)',
@@ -5928,7 +6431,7 @@ const JSONEditorPanel = ({ section, onChange, onReset, context = 'section', }) =
5928
6431
  'widget-data-format.dateConstraint': ['any', 'past-only', 'future-only'],
5929
6432
  'widget-data-format.dateTimeConstraint': ['any', 'past-only', 'future-only'],
5930
6433
  // Widget options
5931
- 'widget-data-options.action': ['show', 'hide', 'enable', 'disable'],
6434
+ 'widget-data-options.action': ['show', 'hide', 'enable', 'disable', 'require'],
5932
6435
  'widget-data-options.condition.operator': CONDITION_OPERATORS,
5933
6436
  };
5934
6437
  }, []);
@@ -7313,7 +7816,7 @@ const removeMask = (value, mask) => {
7313
7816
  };
7314
7817
 
7315
7818
  const TextInputWidget = ({ config }) => {
7316
- 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 });
7317
7820
  const { translate, translateConfig } = useWidgetTranslation();
7318
7821
  // Track raw value separately for masking (to preserve unmasked value internally)
7319
7822
  const formatConfig = widgetConfig['widget-data-format'];
@@ -7450,7 +7953,7 @@ const TextInputWidget = ({ config }) => {
7450
7953
  const label = translateConfig(widgetConfig['widget-label']);
7451
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 }) })] }));
7452
7955
  }
7453
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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
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
7454
7957
  ? 'decimal'
7455
7958
  : formatConfig?.characterType === 'numeric' || formatConfig?.characterType === 'numeric-decimal'
7456
7959
  ? 'numeric'
@@ -7473,7 +7976,7 @@ const NumberInputWidget = ({ config }) => {
7473
7976
  }
7474
7977
  return { ...config, 'widget-data-default': normalizedDefault };
7475
7978
  }, [config]);
7476
- 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 });
7477
7980
  const { translate, translateConfig } = useWidgetTranslation();
7478
7981
  const formatConfig = widgetConfig['widget-data-format'];
7479
7982
  const validationConfig = widgetConfig['widget-data-validation'];
@@ -7604,7 +8107,7 @@ const NumberInputWidget = ({ config }) => {
7604
8107
  const display = numValue !== null ? formatNumber(numValue, formatConfig) : '';
7605
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 }) })] }));
7606
8109
  }
7607
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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 === ''))
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 === ''))
7608
8111
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7609
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
7610
8113
  ? 'text-red-500'
@@ -7612,7 +8115,7 @@ const NumberInputWidget = ({ config }) => {
7612
8115
  };
7613
8116
 
7614
8117
  const BooleanWidget = ({ config }) => {
7615
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8118
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7616
8119
  const { translate, translateConfig } = useWidgetTranslation();
7617
8120
  const formatConfig = widgetConfig['widget-data-format'];
7618
8121
  const representation = formatConfig?.booleanRepresentation || 'true-false';
@@ -7681,7 +8184,7 @@ const BooleanWidget = ({ config }) => {
7681
8184
  }
7682
8185
  // Render based on control type
7683
8186
  if (controlType === 'checkbox') {
7684
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { 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", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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] }))] })] }) }));
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] }))] })] }) }));
7685
8188
  }
7686
8189
  if (controlType === 'radio') {
7687
8190
  const containerClass = orientation === 'horizontal'
@@ -7689,10 +8192,10 @@ const BooleanWidget = ({ config }) => {
7689
8192
  : 'flex flex-col items-start gap-2';
7690
8193
  const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
7691
8194
  const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
7692
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { 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", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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] }))] })] }) }));
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] }))] })] }) }));
7693
8196
  }
7694
8197
  // Toggle/switch control type
7695
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { 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", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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
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
7696
8199
  ? 'bg-blue-600 text-white border-blue-600'
7697
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
7698
8201
  ? 'bg-blue-600 text-white border-blue-600'
@@ -7702,7 +8205,7 @@ const BooleanWidget = ({ config }) => {
7702
8205
  };
7703
8206
 
7704
8207
  const DateInputWidget = ({ config }) => {
7705
- 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 });
7706
8209
  const formValues = reactRedux.useSelector((state) => state.widget.values);
7707
8210
  const { translateConfig } = useWidgetTranslation();
7708
8211
  const formatConfig = widgetConfig['widget-data-format'];
@@ -7903,7 +8406,7 @@ const DateInputWidget = ({ config }) => {
7903
8406
  }
7904
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 }) })] }));
7905
8408
  }
7906
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), 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
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
7907
8410
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7908
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] })] })] }) }));
7909
8412
  };
@@ -8193,7 +8696,7 @@ const validateDateTimeConstraints = (date, minDateTime, maxDateTime, constraint)
8193
8696
  };
8194
8697
 
8195
8698
  const DateTimeInputWidget = ({ config }) => {
8196
- 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 });
8197
8700
  const { translate, translateConfig } = useWidgetTranslation();
8198
8701
  const formatConfig = widgetConfig['widget-data-format'];
8199
8702
  const optionsConfig = widgetConfig['widget-data-options'];
@@ -8346,29 +8849,33 @@ const DateTimeInputWidget = ({ config }) => {
8346
8849
  }
8347
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 }) })] }));
8348
8851
  }
8349
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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 === ''))
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 === ''))
8350
8853
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8351
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] }))] })] }) }));
8352
8855
  };
8353
8856
 
8354
8857
  const SelectWidget = ({ config }) => {
8355
- 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 });
8356
8859
  const { translate, translateConfig } = useWidgetTranslation();
8357
8860
  // For readonly mode, render as display text showing only the selected label
8358
8861
  if (widgetConfig['widget-readonly']) {
8359
8862
  const label = translateConfig(widgetConfig['widget-label']);
8360
8863
  // Find the selected option's label
8361
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
8362
- const displayValue = selectedOption ? selectedOption.label : (value || '-');
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)) : '-'));
8363
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 }) })] }));
8364
8871
  }
8365
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(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 === ''))
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 === ''))
8366
8873
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8367
- : '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] }))] })] }) }));
8368
8875
  };
8369
8876
 
8370
8877
  const RadioWidget = ({ config }) => {
8371
- 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 });
8372
8879
  const { translate, translateConfig } = useWidgetTranslation();
8373
8880
  const formatConfig = widgetConfig['widget-data-format'];
8374
8881
  const layout = formatConfig?.layout || widgetConfig['widget-orientation'] || 'vertical';
@@ -8431,14 +8938,16 @@ const RadioWidget = ({ config }) => {
8431
8938
  if (widgetConfig['widget-readonly']) {
8432
8939
  const label = translateConfig(widgetConfig['widget-label']);
8433
8940
  const selectedOption = processedOptions.find(opt => opt.value === currentValue);
8434
- const displayValue = selectedOption ? selectedOption.label : (allowUnset && currentValue === null ? '-' : '');
8941
+ const displayValue = selectedOption
8942
+ ? translateConfig(selectedOption.label)
8943
+ : (allowUnset && currentValue === null ? '-' : '');
8435
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 }) })] }));
8436
8945
  }
8437
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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] }))] })] }) }));
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] }))] })] }) }));
8438
8947
  };
8439
8948
 
8440
8949
  const CheckboxWidget = ({ config }) => {
8441
- 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 });
8442
8951
  const { translate, translateConfig } = useWidgetTranslation();
8443
8952
  const hasDataSource = !!widgetConfig['widget-data-source'];
8444
8953
  const formatConfig = widgetConfig['widget-data-format'];
@@ -8453,7 +8962,7 @@ const CheckboxWidget = ({ config }) => {
8453
8962
  const displayValue = isChecked ? 'Yes' : 'No';
8454
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 }) })] }));
8455
8964
  }
8456
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { 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", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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] }))] })] }) }));
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] }))] })] }) }));
8457
8966
  }
8458
8967
  // Multiple checkboxes (with data source) - for array values
8459
8968
  // Process and sort options if needed
@@ -8523,7 +9032,7 @@ const CheckboxWidget = ({ config }) => {
8523
9032
  : '-';
8524
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 }) })] }));
8525
9034
  }
8526
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { 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", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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] }))] })] }) }));
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] }))] })] }) }));
8527
9036
  };
8528
9037
 
8529
9038
  const SimpleTableWidget = ({ config }) => {
@@ -8574,7 +9083,7 @@ const SimpleTableWidget = ({ config }) => {
8574
9083
  };
8575
9084
 
8576
9085
  const ArrayWidget = ({ config }) => {
8577
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
9086
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8578
9087
  const { translate, translateConfig } = useWidgetTranslation();
8579
9088
  const items = Array.isArray(value) ? value : [];
8580
9089
  const itemConfig = widgetConfig['widget-item'];
@@ -8598,7 +9107,7 @@ const ArrayWidget = ({ config }) => {
8598
9107
  newItems[index] = newValue;
8599
9108
  onChange(newItems);
8600
9109
  };
8601
- 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.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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) => {
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) => {
8602
9111
  ({
8603
9112
  ...itemConfig,
8604
9113
  'widget-id': `${widgetConfig['widget-id']}-item-${index}`,
@@ -8609,7 +9118,7 @@ const ArrayWidget = ({ config }) => {
8609
9118
  };
8610
9119
 
8611
9120
  const IterableAccordionWidget = ({ config }) => {
8612
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
9121
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8613
9122
  const { translate, translateConfig } = useWidgetTranslation();
8614
9123
  const items = Array.isArray(value) ? value : [];
8615
9124
  const itemConfig = widgetConfig['widget-item'];
@@ -8658,7 +9167,7 @@ const IterableAccordionWidget = ({ config }) => {
8658
9167
  newItems[index] = newValue;
8659
9168
  onChange(newItems);
8660
9169
  };
8661
- 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.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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) => {
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) => {
8662
9171
  const isCollapsed = collapsedItems[index] ?? defaultCollapsed;
8663
9172
  const parentPath = widgetConfig['widget-data-path'];
8664
9173
  const childPath = itemConfig['widget-data-path'];
@@ -8697,7 +9206,7 @@ const IterableAccordionWidget = ({ config }) => {
8697
9206
  };
8698
9207
 
8699
9208
  const PhoneInputWidget = ({ config }) => {
8700
- 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 });
8701
9210
  const { translate, translateConfig } = useWidgetTranslation();
8702
9211
  // Use formatted value if available, otherwise raw value
8703
9212
  const displayValue = formattedValue !== undefined && formattedValue !== value
@@ -8708,13 +9217,13 @@ const PhoneInputWidget = ({ config }) => {
8708
9217
  const label = translateConfig(widgetConfig['widget-label']);
8709
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 || '-' }) })] }));
8710
9219
  }
8711
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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 === ''))
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 === ''))
8712
9221
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8713
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] }))] })] }) }));
8714
9223
  };
8715
9224
 
8716
9225
  const CurrencyInputWidget = ({ config }) => {
8717
- 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 });
8718
9227
  const { translate, translateConfig } = useWidgetTranslation();
8719
9228
  // For input, use raw numeric value; formatted value is for display only
8720
9229
  const numericValue = typeof value === 'number' ? value : (value ? parseFloat(String(value)) : '');
@@ -8736,7 +9245,7 @@ const CurrencyInputWidget = ({ config }) => {
8736
9245
  const display = formattedValue || (value !== null && value !== undefined ? String(value) : '-');
8737
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 }) })] }));
8738
9247
  }
8739
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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 === ''))
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 === ''))
8740
9249
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8741
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] }))] })] }) }));
8742
9251
  };
@@ -8784,20 +9293,69 @@ const DisplayWidget = ({ config }) => {
8784
9293
  if (!label || label.trim() === '') {
8785
9294
  return (jsxRuntimeExports.jsx("div", { className: "DisplayFieldWidget mb-3 min-w-0 w-full overflow-hidden text-ellipsis whitespace-nowrap text-base text-gray-700", title: String(displayValue ?? ''), children: displayValue }));
8786
9295
  }
8787
- // With label, render as key-value pair (structure matches other readonly widgets for SectionRenderer ellipsis)
8788
- return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DisplayFieldWidget flex flex-col sm:flex-row sm:items-start", children: [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 min-w-0", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
9296
+ // With label, render as key-value pair (structure matches other readonly widgets for SectionRenderer ellipsis)
9297
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DisplayFieldWidget flex flex-col sm:flex-row sm:items-start", children: [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 min-w-0", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
9298
+ };
9299
+
9300
+ const getDateColumnConstraintError = (column, cellValue, rowValues, translateConfig) => {
9301
+ const displayValue = cellValue && typeof cellValue === 'string' ? cellValue.split('T')[0] : '';
9302
+ if (!displayValue) {
9303
+ return null;
9304
+ }
9305
+ const optionsConfig = column['widget-data-options'];
9306
+ const formatConfig = column['widget-data-format'];
9307
+ const dateConstraint = formatConfig?.dateConstraint || 'any';
9308
+ const minDate = optionsConfig?.minDate;
9309
+ const maxDate = optionsConfig?.maxDate;
9310
+ const minDateField = optionsConfig?.minDateField;
9311
+ const maxDateField = optionsConfig?.maxDateField;
9312
+ const minDateMessage = optionsConfig?.minDateMessage
9313
+ ? translateConfig(optionsConfig.minDateMessage)
9314
+ : undefined;
9315
+ const maxDateMessage = optionsConfig?.maxDateMessage
9316
+ ? translateConfig(optionsConfig.maxDateMessage)
9317
+ : undefined;
9318
+ const resolveSiblingDate = (fieldRef) => {
9319
+ if (!fieldRef) {
9320
+ return undefined;
9321
+ }
9322
+ const raw = getValueByPath(rowValues, fieldRef) ?? rowValues[fieldRef];
9323
+ return resolveDateBoundFromFieldValue(raw);
9324
+ };
9325
+ const effectiveMinDate = mergeMinDateBounds(getMinDate(dateConstraint, minDate), resolveSiblingDate(minDateField));
9326
+ const effectiveMaxDate = mergeMaxDateBounds(getMaxDate(dateConstraint, maxDate), resolveSiblingDate(maxDateField));
9327
+ return validateDateConstraints(displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, { minDateMessage, maxDateMessage });
9328
+ };
9329
+ const isTableRowDataValid = (rowData, columns, tableReadonly, translateConfig) => {
9330
+ for (const col of columns) {
9331
+ if (tableReadonly || col['widget-readonly'] === true) {
9332
+ continue;
9333
+ }
9334
+ const columnKey = col['column-key'];
9335
+ const cellValue = rowData[columnKey];
9336
+ const widgetErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
9337
+ if (widgetErrors.length > 0) {
9338
+ return false;
9339
+ }
9340
+ if ((col.widget || 'text') === 'date') {
9341
+ const dateError = getDateColumnConstraintError(col, cellValue, rowData, translateConfig);
9342
+ if (dateError) {
9343
+ return false;
9344
+ }
9345
+ }
9346
+ }
9347
+ return true;
8789
9348
  };
8790
-
8791
9349
  const TableCellSelect = ({ config, value, onValueChange }) => {
8792
9350
  const { translate } = useWidgetTranslation();
8793
9351
  // Use useBaseWidget to get data source options (it handles loading)
8794
9352
  const { dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8795
9353
  const isReadonly = config['widget-readonly'] || false;
8796
- return (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: {
8797
- borderRadius: '10px',
8798
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8799
- backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8800
- }, children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') || 'Select' }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }));
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: {
9355
+ borderRadius: '10px',
9356
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9357
+ backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9358
+ }, children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') || 'Select' }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] }));
8801
9359
  };
8802
9360
  const SelectDisplayValue$1 = ({ config, value }) => {
8803
9361
  const { dataSourceOptions, loading } = useBaseWidget({ config });
@@ -8807,7 +9365,7 @@ const SelectDisplayValue$1 = ({ config, value }) => {
8807
9365
  if (value === null || value === undefined || value === '') {
8808
9366
  return jsxRuntimeExports.jsx("span", { children: "-" });
8809
9367
  }
8810
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
9368
+ const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
8811
9369
  return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
8812
9370
  };
8813
9371
  const TableCellText = ({ config, value, onValueChange }) => {
@@ -8816,11 +9374,11 @@ const TableCellText = ({ config, value, onValueChange }) => {
8816
9374
  config['widget-data-format'];
8817
9375
  const maxLength = config['widget-data-validation']?.maxLength;
8818
9376
  const displayValue = value !== null && value !== undefined ? String(value) : '';
8819
- return (jsxRuntimeExports.jsx("input", { type: "text", value: displayValue, onChange: (e) => onValueChange(e.target.value), disabled: isReadonly, placeholder: placeholder, maxLength: maxLength, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
8820
- borderRadius: '10px',
8821
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8822
- backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8823
- } }));
9377
+ return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsx("input", { type: "text", value: displayValue, onChange: (e) => onValueChange(e.target.value), disabled: isReadonly, placeholder: placeholder, maxLength: maxLength, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
9378
+ borderRadius: '10px',
9379
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9380
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9381
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] }));
8824
9382
  };
8825
9383
  const TableCellNumber = ({ config, value, onValueChange }) => {
8826
9384
  const isReadonly = config['widget-readonly'] || false;
@@ -8843,11 +9401,11 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
8843
9401
  onValueChange(inputValue);
8844
9402
  }
8845
9403
  };
8846
- return (jsxRuntimeExports.jsx("input", { type: "number", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: validationConfig?.min, max: validationConfig?.max, step: formatConfig?.decimalPlaces ? Math.pow(0.1, formatConfig.decimalPlaces) : undefined, className: `w-full h-[28px] px-2 text-sm border focus:outline-none text-right ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
8847
- borderRadius: '10px',
8848
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8849
- backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8850
- } }));
9404
+ return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsx("input", { type: "number", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: validationConfig?.min, max: validationConfig?.max, step: formatConfig?.decimalPlaces ? Math.pow(0.1, formatConfig.decimalPlaces) : undefined, className: `w-full h-[28px] px-2 text-sm border focus:outline-none text-right ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
9405
+ borderRadius: '10px',
9406
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9407
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9408
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] }));
8851
9409
  };
8852
9410
  const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8853
9411
  const { translateConfig } = useWidgetTranslation();
@@ -8906,13 +9464,15 @@ const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8906
9464
  setConstraintError(error);
8907
9465
  };
8908
9466
  const hasError = Boolean(constraintError);
8909
- return (jsxRuntimeExports.jsxs("div", { className: "w-full", children: [jsxRuntimeExports.jsx("input", { type: "date", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: effectiveMinDate, max: effectiveMaxDate, title: constraintError || translateConfig(config['widget-data-tooltip']), className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} ${hasError ? 'border-red-500' : ''} table-cell-input`, style: {
9467
+ return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsx("input", { type: "date", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: effectiveMinDate, max: effectiveMaxDate, title: constraintError || translateConfig(config['widget-data-tooltip']), className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
8910
9468
  borderRadius: '10px',
8911
9469
  borderColor: hasError
8912
9470
  ? 'var(--owt-color-error, #B91C1C)'
8913
9471
  : 'var(--owt-widget-input-border, #C4C4C4)',
8914
9472
  backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8915
- } }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-xs mt-0.5 leading-tight", children: constraintError }))] }));
9473
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error text-xs mt-0.5 leading-tight", style: {
9474
+ color: hasError ? 'var(--owt-color-error, #B91C1C)' : 'transparent',
9475
+ }, "aria-live": "polite", children: constraintError ?? '\u00a0' })] }));
8916
9476
  };
8917
9477
  const TableWidget = ({ config }) => {
8918
9478
  const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
@@ -8938,6 +9498,18 @@ const TableWidget = ({ config }) => {
8938
9498
  const isSectionEditMode = !isReadonly && operations.edit;
8939
9499
  // Check if any row is being edited (either manually or via section edit mode)
8940
9500
  const isAnyRowEditing = editingState !== null || isAdding;
9501
+ const canSaveEditingRow = React.useMemo(() => {
9502
+ if (!editingState) {
9503
+ return false;
9504
+ }
9505
+ return isTableRowDataValid(editingState.currentValue, columns, isReadonly, translateConfig);
9506
+ }, [editingState, columns, isReadonly, translateConfig]);
9507
+ const canSaveNewRow = React.useMemo(() => {
9508
+ if (!isAdding || !newRowData) {
9509
+ return false;
9510
+ }
9511
+ return isTableRowDataValid(newRowData, columns, isReadonly, translateConfig);
9512
+ }, [isAdding, newRowData, columns, isReadonly, translateConfig]);
8941
9513
  // Show confirmation dialog
8942
9514
  const showConfirmation = React.useCallback((message, onConfirm, onCancel) => {
8943
9515
  setConfirmationState({
@@ -9027,6 +9599,8 @@ const TableWidget = ({ config }) => {
9027
9599
  const saveEdit = React.useCallback(async () => {
9028
9600
  if (!editingState)
9029
9601
  return;
9602
+ if (!canSaveEditingRow)
9603
+ return;
9030
9604
  const rowData = editingState.currentValue;
9031
9605
  const rowIndex = editingState.rowIndex;
9032
9606
  setLoadingRowIndex(rowIndex);
@@ -9098,7 +9672,7 @@ const TableWidget = ({ config }) => {
9098
9672
  finally {
9099
9673
  setLoadingRowIndex(null);
9100
9674
  }
9101
- }, [editingState, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
9675
+ }, [editingState, canSaveEditingRow, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
9102
9676
  // Add new row
9103
9677
  const startAdd = React.useCallback(() => {
9104
9678
  // If there's an unsaved edit, cancel it first (no confirmation needed)
@@ -9116,6 +9690,8 @@ const TableWidget = ({ config }) => {
9116
9690
  const saveAdd = React.useCallback(async () => {
9117
9691
  if (!isAdding || !newRowData)
9118
9692
  return;
9693
+ if (!canSaveNewRow)
9694
+ return;
9119
9695
  setLoadingRowIndex(-1); // Use -1 to indicate new row
9120
9696
  try {
9121
9697
  let savedRow = { ...newRowData };
@@ -9146,7 +9722,7 @@ const TableWidget = ({ config }) => {
9146
9722
  finally {
9147
9723
  setLoadingRowIndex(null);
9148
9724
  }
9149
- }, [isAdding, newRowData, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode]);
9725
+ }, [isAdding, newRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode]);
9150
9726
  // Delete row
9151
9727
  const deleteRow = React.useCallback(async (rowIndex) => {
9152
9728
  if (isAnyRowEditing) {
@@ -9424,6 +10000,22 @@ const TableWidget = ({ config }) => {
9424
10000
  box-shadow: 0 0 0 1px var(--owt-widget-input-focus-border, #F07B1A);
9425
10001
  border-color: var(--owt-widget-input-focus-border, #F07B1A);
9426
10002
  }
10003
+
10004
+ /* Keep inputs and action buttons top-aligned when a cell shows validation text */
10005
+ .${tableWidgetId} tr.table-row-editing td {
10006
+ vertical-align: top;
10007
+ }
10008
+
10009
+ .${tableWidgetId} .table-cell-field-error {
10010
+ min-height: 1.125rem;
10011
+ }
10012
+
10013
+ .${tableWidgetId} .table-cell-actions {
10014
+ display: flex;
10015
+ flex-direction: row;
10016
+ gap: 0.5rem;
10017
+ align-items: flex-start;
10018
+ }
9427
10019
  ` }), jsxRuntimeExports.jsxs("div", { className: `table-widget-container ${tableWidgetId}`, children: [confirmationState?.show && (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 max-w-md w-full mx-4", style: { backgroundColor: 'var(--owt-color-bg, #FFFFFF)' }, children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: 'var(--owt-color-text, #011627)' }, children: translate('table.confirm') || 'Confirm Action' }), jsxRuntimeExports.jsx("p", { className: "mb-6", style: { color: 'var(--owt-color-text, #011627)' }, children: confirmationState.message }), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3", children: [jsxRuntimeExports.jsx("button", { onClick: confirmationState.onCancel, className: "px-4 py-2 text-sm font-medium", style: {
9428
10020
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9429
10021
  border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
@@ -9442,7 +10034,7 @@ const TableWidget = ({ config }) => {
9442
10034
  }, children: translate('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: { borderRadius: 'var(--owt-widget-table-border-radius, 15px)', borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translateConfig(col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translate('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && !isAdding && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: columns.length + (((operations.edit || operations.remove) && !isReadonly) || isSectionEditMode ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
9443
10035
  const isEditing = isRowEditing(rowIndex);
9444
10036
  const isLoading = loadingRowIndex === rowIndex;
9445
- return (jsxRuntimeExports.jsxs("tr", { className: isLoading ? 'opacity-50' : '', style: {
10037
+ return (jsxRuntimeExports.jsxs("tr", { className: `${isLoading ? 'opacity-50' : ''}${isEditing ? ' table-row-editing' : ''}`, style: {
9446
10038
  borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
9447
10039
  backgroundColor: isEditing
9448
10040
  ? 'var(--owt-widget-table-editing-row-bg, #FBE6AA)'
@@ -9453,7 +10045,7 @@ const TableWidget = ({ config }) => {
9453
10045
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rowIndex, col, row) }, col['column-key']));
9454
10046
  }), ((operations.edit || operations.remove) && !isReadonly) || isEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: isEditing ? (
9455
10047
  // Show OK (Save)/Cancel buttons when row is being edited (works in both section edit mode and normal mode)
9456
- jsxRuntimeExports.jsxs("div", { className: "flex flex-row gap-2 items-center", style: { width: '100%' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveEdit, disabled: isLoading, className: "px-3 py-1 text-xs font-medium disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap flex-shrink-0", style: {
10048
+ jsxRuntimeExports.jsxs("div", { className: "table-cell-actions", style: { width: '100%' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveEdit, disabled: isLoading || !canSaveEditingRow, className: "px-3 py-1 text-xs font-medium disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap flex-shrink-0", style: {
9457
10049
  display: 'inline-block',
9458
10050
  minWidth: '60px',
9459
10051
  backgroundColor: 'var(--owt-color-success, #16A34A)',
@@ -9480,7 +10072,7 @@ const TableWidget = ({ config }) => {
9480
10072
  backgroundColor: 'transparent',
9481
10073
  border: 'none',
9482
10074
  }, children: translate('common.remove') || 'Delete' }))] })) })) : null] }, rowIndex));
9483
- }), isAdding && newRowData && (jsxRuntimeExports.jsxs("tr", { style: { backgroundColor: 'var(--owt-widget-table-editing-row-bg, #FBE6AA)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rows.length, col, { ...newRowData, edit_action: 'ADD' }) }, col['column-key']))), jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveAdd, disabled: loadingRowIndex === -1, className: "px-3 py-1 text-xs disabled:opacity-50", style: {
10075
+ }), isAdding && newRowData && (jsxRuntimeExports.jsxs("tr", { className: "table-row-editing", style: { backgroundColor: 'var(--owt-widget-table-editing-row-bg, #FBE6AA)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rows.length, col, { ...newRowData, edit_action: 'ADD' }) }, col['column-key']))), jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsxs("div", { className: "table-cell-actions", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveAdd, disabled: loadingRowIndex === -1 || !canSaveNewRow, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
9484
10076
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9485
10077
  backgroundColor: 'var(--owt-color-success, #16A34A)',
9486
10078
  color: 'var(--owt-color-bg, #FFFFFF)',
@@ -9496,6 +10088,23 @@ const TableWidget = ({ config }) => {
9496
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] }))] })] }));
9497
10089
  };
9498
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
+ };
9499
10108
  // Display select value label in view mode
9500
10109
  const SelectDisplayValue = ({ config, value }) => {
9501
10110
  const { dataSourceOptions, loading } = useBaseWidget({ config });
@@ -9503,53 +10112,57 @@ const SelectDisplayValue = ({ config, value }) => {
9503
10112
  return jsxRuntimeExports.jsx("span", { children: "-" });
9504
10113
  if (value === null || value === undefined || value === '')
9505
10114
  return jsxRuntimeExports.jsx("span", { children: "-" });
9506
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
10115
+ const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
9507
10116
  return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
9508
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
+ });
9509
10137
  /**
9510
10138
  * Dialog table widget:
9511
10139
  * - Table displays a subset of columns (n out of x)
9512
10140
  * - Add/Edit happens in a modal dialog that shows ALL columns as a form
9513
- *
9514
- * Usage in schema:
9515
- * {
9516
- * "widget": "dialog-table",
9517
- * "widget-type": "table",
9518
- * "widget-label": "Household Members",
9519
- * "widget-id": "householdMembers",
9520
- * "widget-data-path": "household.members",
9521
- * "widget-data-columns": [ ...all columns... ],
9522
- * "widget-data-visible-columns": ["firstName", "lastName", "dob"], // optional override; default = by column flag
9523
- * // Per-column control (recommended): set "column-visible-in-table": false to hide it in the table
9524
- * "widget-data-operations": { "add": true, "edit": true, "remove": true }
9525
- * }
9526
10141
  */
9527
10142
  const DialogTableWidget = ({ config }) => {
9528
10143
  const { value, error, touched, isEnabled, onChange, config: widgetConfig } = useBaseWidget({ config });
9529
10144
  const { translate, translateConfig } = useWidgetTranslation();
9530
10145
  const dispatch = reactRedux.useDispatch();
9531
- const storeValues = reactRedux.useSelector((state) => state.widget?.values ?? {});
9532
10146
  const rows = Array.isArray(value) ? value : [];
9533
10147
  const columns = widgetConfig['widget-data-columns'] || [];
9534
10148
  const operations = widgetConfig['widget-data-operations'] || {};
9535
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;
9536
10152
  const visibleColumnKeys = widgetConfig['widget-data-visible-columns'];
9537
10153
  const visibleColumns = React.useMemo(() => {
9538
- // 1) If explicit list provided, it wins
9539
10154
  if (Array.isArray(visibleColumnKeys) && visibleColumnKeys.length > 0) {
9540
10155
  const keySet = new Set(visibleColumnKeys);
9541
10156
  return columns.filter((c) => keySet.has(c['column-key']));
9542
10157
  }
9543
- // 2) Otherwise decide per column (default = visible)
9544
10158
  return columns.filter((c) => c['column-visible-in-table'] !== false);
9545
10159
  }, [columns, visibleColumnKeys]);
9546
10160
  const [dialogOpen, setDialogOpen] = React.useState(false);
9547
10161
  const [dialogMode, setDialogMode] = React.useState('add');
9548
10162
  const [activeRowIndex, setActiveRowIndex] = React.useState(null);
9549
- const [formData, setFormData] = React.useState({});
9550
- /** Unique per dialog open so Redux widget ids don't reuse stale values across rows/add sessions */
9551
10163
  const dialogSessionRef = React.useRef(0);
9552
10164
  const [dialogSessionId, setDialogSessionId] = React.useState(0);
10165
+ const membersWidgetId = widgetConfig['widget-id'];
9553
10166
  const addDialogTitle = translateConfig(widgetConfig['widget-data-dialog-title-add']) ||
9554
10167
  translate('table.addRecordDialog') ||
9555
10168
  'Add record';
@@ -9560,19 +10173,35 @@ const DialogTableWidget = ({ config }) => {
9560
10173
  const emptyRow = {};
9561
10174
  columns.forEach((col) => {
9562
10175
  const key = col['column-key'];
9563
- emptyRow[key] = col['widget-data-default'] ?? '';
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
+ }
9564
10182
  });
9565
10183
  return emptyRow;
9566
10184
  }, [columns]);
9567
- const dialogFieldWidgetId = React.useCallback((columnKey) => `${widgetConfig['widget-id']}-dlg-${dialogSessionId}-${columnKey}`, [widgetConfig, dialogSessionId]);
10185
+ const dialogFieldWidgetId = React.useCallback((sessionId, columnKey) => `${membersWidgetId}-dlg-${sessionId}-${columnKey}`, [membersWidgetId]);
9568
10186
  const resetDialogWidgets = React.useCallback((sessionId) => {
9569
10187
  if (sessionId <= 0)
9570
10188
  return;
9571
10189
  columns.forEach((col) => {
9572
- const wid = `${widgetConfig['widget-id']}-dlg-${sessionId}-${col['column-key']}`;
9573
- dispatch(resetWidget(wid));
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
+ }
9574
10200
  });
9575
- }, [columns, widgetConfig, dispatch]);
10201
+ if (Object.keys(seeds).length > 0) {
10202
+ dispatch(setValues(seeds));
10203
+ }
10204
+ }, [columns, dialogFieldWidgetId, dispatch]);
9576
10205
  const beginDialogSession = React.useCallback(() => {
9577
10206
  dialogSessionRef.current += 1;
9578
10207
  const nextSession = dialogSessionRef.current;
@@ -9581,15 +10210,21 @@ const DialogTableWidget = ({ config }) => {
9581
10210
  }, []);
9582
10211
  const openAddDialog = React.useCallback(() => {
9583
10212
  resetDialogWidgets(dialogSessionId);
9584
- beginDialogSession();
10213
+ const sessionId = beginDialogSession();
10214
+ seedDialogReduxValues(sessionId, buildEmptyRow());
9585
10215
  setDialogMode('add');
9586
10216
  setActiveRowIndex(null);
9587
- setFormData(buildEmptyRow());
9588
10217
  setDialogOpen(true);
9589
- }, [buildEmptyRow, beginDialogSession, resetDialogWidgets, dialogSessionId]);
10218
+ }, [
10219
+ buildEmptyRow,
10220
+ beginDialogSession,
10221
+ resetDialogWidgets,
10222
+ dialogSessionId,
10223
+ seedDialogReduxValues,
10224
+ ]);
9590
10225
  const openEditDialog = React.useCallback((rowIndex) => {
9591
10226
  resetDialogWidgets(dialogSessionId);
9592
- beginDialogSession();
10227
+ const sessionId = beginDialogSession();
9593
10228
  const row = rows[rowIndex] || {};
9594
10229
  const nextFormData = buildEmptyRow();
9595
10230
  columns.forEach((col) => {
@@ -9597,44 +10232,71 @@ const DialogTableWidget = ({ config }) => {
9597
10232
  if (row[key] !== undefined)
9598
10233
  nextFormData[key] = row[key];
9599
10234
  });
10235
+ seedDialogReduxValues(sessionId, nextFormData);
9600
10236
  setDialogMode('edit');
9601
10237
  setActiveRowIndex(rowIndex);
9602
- setFormData(nextFormData);
9603
10238
  setDialogOpen(true);
9604
- }, [rows, columns, buildEmptyRow, resetDialogWidgets, dialogSessionId, beginDialogSession]);
10239
+ }, [
10240
+ rows,
10241
+ columns,
10242
+ buildEmptyRow,
10243
+ resetDialogWidgets,
10244
+ dialogSessionId,
10245
+ beginDialogSession,
10246
+ seedDialogReduxValues,
10247
+ ]);
9605
10248
  const closeDialog = React.useCallback(() => {
9606
10249
  const sessionToClear = dialogSessionId;
9607
10250
  setDialogOpen(false);
9608
10251
  setActiveRowIndex(null);
9609
- setFormData({});
9610
10252
  resetDialogWidgets(sessionToClear);
9611
10253
  setDialogSessionId(0);
9612
10254
  }, [dialogSessionId, resetDialogWidgets]);
9613
- const updateField = React.useCallback((columnKey, newValue) => {
9614
- setFormData((prev) => ({ ...prev, [columnKey]: newValue }));
9615
- }, []);
9616
- const collectMergedRowPayload = React.useCallback(() => {
9617
- const merged = { ...formData };
10255
+ const dialogStoreValues = reactRedux.useSelector((state) => {
10256
+ if (dialogSessionId <= 0) {
10257
+ return {};
10258
+ }
10259
+ const values = state.widget?.values ?? {};
10260
+ const row = {};
9618
10261
  columns.forEach((col) => {
9619
10262
  const k = col['column-key'];
9620
- const wid = dialogFieldWidgetId(k);
9621
- const fromStore = storeValues[wid];
9622
- if (fromStore !== undefined)
9623
- merged[k] = fromStore;
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
+ }
9624
10283
  });
9625
- return merged;
9626
- }, [formData, columns, storeValues, dialogFieldWidgetId]);
10284
+ return result;
10285
+ }, [columns]);
9627
10286
  const saveDialog = React.useCallback(() => {
9628
10287
  const payload = collectMergedRowPayload();
9629
10288
  let hasErrors = false;
9630
10289
  columns.forEach((col) => {
9631
10290
  const key = col['column-key'];
9632
- const cellWidgetId = dialogFieldWidgetId(key);
10291
+ const cellWidgetId = dialogFieldWidgetId(dialogSessionId, key);
9633
10292
  const isColReadonly = isReadonly || col['widget-readonly'] === true;
9634
10293
  if (isColReadonly)
9635
10294
  return;
10295
+ if (!shouldShowWidget(col['widget-data-options'], payload))
10296
+ return;
9636
10297
  const cellValue = payload[key];
9637
- const validationErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
10298
+ const isRequired = shouldRequireWidget(col['widget-data-options'], payload, col['widget-required']);
10299
+ const validationErrors = validateWidget(cellValue, col['widget-data-validation'], isRequired);
9638
10300
  if (validationErrors && validationErrors.length > 0) {
9639
10301
  hasErrors = true;
9640
10302
  dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
@@ -9647,8 +10309,9 @@ const DialogTableWidget = ({ config }) => {
9647
10309
  if (hasErrors) {
9648
10310
  return;
9649
10311
  }
10312
+ const cleaned = finalizeDialogRowPayload(payload);
9650
10313
  if (dialogMode === 'add') {
9651
- const savedRow = { ...payload, edit_action: 'ADD' };
10314
+ const savedRow = { ...cleaned, edit_action: 'ADD' };
9652
10315
  onChange([...rows, savedRow]);
9653
10316
  closeDialog();
9654
10317
  return;
@@ -9658,15 +10321,43 @@ const DialogTableWidget = ({ config }) => {
9658
10321
  const currentRow = newRows[activeRowIndex] || {};
9659
10322
  const wasDeleted = currentRow.edit_action === 'DELETE';
9660
10323
  const editAction = wasDeleted ? 'UPDATE' : (currentRow.edit_action ?? 'UPDATE');
9661
- newRows[activeRowIndex] = { ...currentRow, ...payload, edit_action: editAction };
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;
9662
10332
  onChange(newRows);
9663
10333
  closeDialog();
9664
10334
  }
9665
- }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
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
+ ]);
9666
10349
  const deleteRow = React.useCallback((rowIndex) => {
9667
- const newRows = rows.filter((_, i) => i !== rowIndex);
9668
- onChange(newRows);
9669
- }, [rows, onChange]);
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]);
9670
10361
  const getDisplayValue = React.useCallback((rowIndex, column) => {
9671
10362
  const key = column['column-key'];
9672
10363
  const cellValue = rows[rowIndex]?.[key];
@@ -9674,11 +10365,12 @@ const DialogTableWidget = ({ config }) => {
9674
10365
  if (cellValue === null || cellValue === undefined || cellValue === '')
9675
10366
  return '-';
9676
10367
  if (widgetType === 'select')
9677
- return null; // handled by SelectDisplayValue
10368
+ return null;
9678
10369
  if (column['widget-data-format'])
9679
10370
  return formatValue(cellValue, column['widget-data-format'], column.widget);
9680
10371
  return String(cellValue);
9681
10372
  }, [rows]);
10373
+ const visibleDialogColumns = React.useMemo(() => columns.filter((col) => shouldShowWidget(col['widget-data-options'], dialogRowValues)), [columns, dialogRowValues]);
9682
10374
  const tableWidgetId = `dialog-table-widget-${widgetConfig['widget-id']}`;
9683
10375
  const columnSpan = widgetConfig['widget-column-span'] || 2;
9684
10376
  const minWidth = columnSpan * 200;
@@ -9706,37 +10398,40 @@ const DialogTableWidget = ({ config }) => {
9706
10398
  }, children: translate('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
9707
10399
  borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
9708
10400
  borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
9709
- }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translateConfig(col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translate('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => (jsxRuntimeExports.jsxs("tr", { style: {
9710
- borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
9711
- backgroundColor: row?.edit_action === 'DELETE'
9712
- ? 'var(--owt-widget-table-deleted-row-bg, #FEE2E2)'
9713
- : undefined,
9714
- }, children: [visibleColumns.map((col) => {
9715
- const key = col['column-key'];
9716
- const widgetType = col.widget || 'text';
9717
- const displayValue = getDisplayValue(rowIndex, col);
9718
- if (widgetType === 'select' && displayValue === null) {
9719
- const displayConfig = {
9720
- ...col,
9721
- 'widget-id': `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`,
9722
- 'widget-label': '',
9723
- 'widget-readonly': true,
9724
- 'widget-data-path': undefined,
9725
- };
9726
- return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: displayConfig, value: row?.[key] }) }) }, key));
9727
- }
9728
- return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", children: displayValue }) }, key));
9729
- }), ((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: {
9730
- borderRadius: 'var(--owt-btn-border-radius, 10px)',
9731
- color: 'var(--owt-color-primary-dark, #F07B1A)',
9732
- backgroundColor: 'transparent',
9733
- border: 'none',
9734
- }, 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: {
9735
- borderRadius: 'var(--owt-btn-border-radius, 10px)',
9736
- color: 'var(--owt-color-error, #B91C1C)',
9737
- backgroundColor: 'transparent',
9738
- border: 'none',
9739
- }, children: translate('common.remove') || 'Delete' }))] }) }))] }, rowIndex)))] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] }), dialogOpen && (jsxRuntimeExports.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, children: jsxRuntimeExports.jsxs("div", { className: "rounded-lg p-6 w-full mx-4", style: {
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: {
9740
10435
  maxWidth: '900px',
9741
10436
  backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
9742
10437
  borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
@@ -9747,22 +10442,10 @@ const DialogTableWidget = ({ config }) => {
9747
10442
  cursor: 'pointer',
9748
10443
  fontSize: '20px',
9749
10444
  lineHeight: 1,
9750
- }, "aria-label": "Close", children: "\u00D7" })] }), jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", style: { maxHeight: '70vh', overflow: 'auto' }, children: columns.map((col) => {
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) => {
9751
10446
  const key = col['column-key'];
9752
- const widgetType = col.widget || 'text';
9753
- const cellWidgetId = dialogFieldWidgetId(key);
9754
- const initialValue = formData[key] ?? col['widget-data-default'] ?? '';
9755
- const fieldConfig = {
9756
- ...col,
9757
- widget: widgetType,
9758
- 'widget-type': col['widget-type'] || 'input',
9759
- 'widget-id': cellWidgetId,
9760
- 'widget-label': col['widget-label'],
9761
- 'widget-readonly': isReadonly || col['widget-readonly'] === true,
9762
- 'widget-data-path': undefined,
9763
- 'widget-data-default': initialValue,
9764
- };
9765
- 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}`));
9766
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: {
9767
10450
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9768
10451
  border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
@@ -9952,13 +10635,13 @@ const ProfileWidget = ({ config }) => {
9952
10635
  if (placeholder) {
9953
10636
  placeholder.style.display = 'flex';
9954
10637
  }
9955
- } })) : null, jsxRuntimeExports.jsx("div", { className: "profile-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, 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 })] }))] })] })] }));
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 })] }))] })] })] }));
9956
10639
  };
9957
10640
 
9958
10641
  const TextAreaWidget = ({ config }) => {
9959
10642
  // Check readonly early from original config
9960
10643
  const isReadonly = config['widget-readonly'] || false;
9961
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
10644
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9962
10645
  const { translate, translateConfig } = useWidgetTranslation();
9963
10646
  const formatConfig = widgetConfig['widget-data-format'] || {};
9964
10647
  const validationConfig = widgetConfig['widget-data-validation'] || {};
@@ -10006,7 +10689,6 @@ const TextAreaWidget = ({ config }) => {
10006
10689
  ? translateConfig(widgetConfig['widget-label'])
10007
10690
  : '';
10008
10691
  // Check if required
10009
- const isRequired = widgetConfig['widget-required'] || false;
10010
10692
  // Error display
10011
10693
  const hasError = touched && error && error.length > 0;
10012
10694
  const errorMessage = hasError ? error[0] : '';
@@ -10024,7 +10706,7 @@ const TextAreaWidget = ({ config }) => {
10024
10706
  border: 'none',
10025
10707
  }, children: displayValue }) })] }));
10026
10708
  }
10027
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [label, isRequired && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), 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
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
10028
10710
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
10029
10711
  : 'border-gray-300'} ${!isEnabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: {
10030
10712
  borderRadius: '10px',
@@ -10170,17 +10852,16 @@ const HeaderSectionWidget = ({ config }) => {
10170
10852
  result = searchIn(schemaData);
10171
10853
  return result;
10172
10854
  }, [paths, values, schemaData]);
10173
- const imageVal = findValue('image');
10174
10855
  const imageUrlVal = findValue('imageUrl');
10175
10856
  const [previewUrl, setPreviewUrl] = React.useState(null);
10176
10857
  React.useEffect(() => {
10177
- if (imageVal instanceof File) {
10178
- const url = URL.createObjectURL(imageVal);
10858
+ if (imageUrlVal instanceof File) {
10859
+ const url = URL.createObjectURL(imageUrlVal);
10179
10860
  setPreviewUrl(url);
10180
10861
  return () => URL.revokeObjectURL(url);
10181
10862
  }
10182
10863
  setPreviewUrl(null);
10183
- }, [imageVal]);
10864
+ }, [imageUrlVal]);
10184
10865
  const displayImageUrl = previewUrl || (typeof imageUrlVal === 'string' && imageUrlVal ? imageUrlVal : null);
10185
10866
  const displayName = findValue('name') || '';
10186
10867
  const functionalId = findValue('functionalId') || '';
@@ -10299,14 +10980,16 @@ const HeaderSectionWidget = ({ config }) => {
10299
10980
  const fileInputRef = React.useRef(null);
10300
10981
  const handleImageUpload = React.useCallback((e) => {
10301
10982
  const file = e.target.files?.[0];
10302
- if (!file)
10983
+ if (!file || !paths.imageUrl)
10303
10984
  return;
10304
- updateFieldValue('image', file);
10985
+ dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, file)));
10305
10986
  e.target.value = '';
10306
- }, [updateFieldValue]);
10987
+ }, [paths.imageUrl, values, dispatch]);
10307
10988
  const handleImageDelete = React.useCallback(() => {
10308
- updateFieldValue('image', '');
10309
- }, [updateFieldValue]);
10989
+ if (!paths.imageUrl)
10990
+ return;
10991
+ dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, null)));
10992
+ }, [paths.imageUrl, values, dispatch]);
10310
10993
  // ── Scoped class for CSS isolation ────────────────────────────
10311
10994
  const cls = `header-section-widget-${widgetConfig['widget-id']}`;
10312
10995
  // ── RENDER ────────────────────────────────────────────────────
@@ -10615,7 +11298,7 @@ const HeaderSectionWidget = ({ config }) => {
10615
11298
  .parentElement?.querySelector('.hdr-avatar-placeholder');
10616
11299
  if (placeholder)
10617
11300
  placeholder.style.display = 'flex';
10618
- } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", 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: () => {
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: () => {
10619
11302
  if (isReasonMissing)
10620
11303
  setShowReasonRequired(true);
10621
11304
  }, onChange: (e) => {
@@ -11360,6 +12043,621 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
11360
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] })] })] }) })] })] }));
11361
12044
  };
11362
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
+
11363
12661
  /**
11364
12662
  * Register all default/generic widgets
11365
12663
  * This is called automatically when the package is imported
@@ -11407,6 +12705,10 @@ const registerDefaultWidgets = () => {
11407
12705
  widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
11408
12706
  // ID Authentication widget for OIDC-based foundational ID authentication (view-only + action)
11409
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 });
11410
12712
  };
11411
12713
  // Auto-register on import
11412
12714
  registerDefaultWidgets();
@@ -11469,6 +12771,27 @@ var enTranslations = {
11469
12771
  "common.sectionSaved": "Saved",
11470
12772
  "common.sectionModified": "Modified and not saved",
11471
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",
11472
12795
  "table.addRecord": "Add New Record",
11473
12796
  "table.confirm": "Confirm Action",
11474
12797
  "table.discard": "Discard & Continue",
@@ -11709,13 +13032,10 @@ const translateWidgetConfig = (widgetConfig, translate) => {
11709
13032
  ...dataSource,
11710
13033
  options: dataSource.options.map((option) => {
11711
13034
  if (option.label && typeof option.label === 'string') {
11712
- const optionLabel = option.label;
11713
- if (isTranslationKey(optionLabel)) {
11714
- return {
11715
- ...option,
11716
- label: translate(optionLabel, { defaultValue: optionLabel }),
11717
- };
11718
- }
13035
+ return {
13036
+ ...option,
13037
+ label: translate(option.label, { defaultValue: option.label }),
13038
+ };
11719
13039
  }
11720
13040
  return option;
11721
13041
  }),
@@ -11778,16 +13098,19 @@ exports.DateTimeInputWidget = DateTimeInputWidget;
11778
13098
  exports.DialogTableWidget = DialogTableWidget;
11779
13099
  exports.DisplayWidget = DisplayWidget;
11780
13100
  exports.FileInputWidget = FileInputWidget;
13101
+ exports.GEO_LEVEL_CLEARED = GEO_LEVEL_CLEARED;
11781
13102
  exports.HeaderSectionWidget = HeaderSectionWidget;
11782
13103
  exports.IdAuthenticationWidget = IdAuthenticationWidget;
11783
13104
  exports.IterableAccordionWidget = IterableAccordionWidget;
11784
13105
  exports.JSONEditorPanel = JSONEditorPanel;
13106
+ exports.MultiSelectWidget = MultiSelectWidget;
11785
13107
  exports.NumberInputWidget = NumberInputWidget;
11786
13108
  exports.PanelRenderer = PanelRenderer;
11787
13109
  exports.PhoneInputWidget = PhoneInputWidget;
11788
13110
  exports.ProfileWidget = ProfileWidget;
11789
13111
  exports.PropertyEditor = PropertyEditor;
11790
13112
  exports.RadioWidget = RadioWidget;
13113
+ exports.RegisterLookupWidget = RegisterLookupWidget;
11791
13114
  exports.ScoresDisplayWidget = ScoresDisplayWidget;
11792
13115
  exports.SectionBuilder = SectionBuilder;
11793
13116
  exports.SectionRenderer = SectionRenderer;
@@ -11805,10 +13128,14 @@ exports.WidgetRenderer = WidgetRenderer;
11805
13128
  exports.applyCaseControl = applyCaseControl;
11806
13129
  exports.applyDecimalPrecision = applyDecimalPrecision;
11807
13130
  exports.applyMask = applyMask;
13131
+ exports.applySharedGeoHierarchyToValues = applySharedGeoHierarchyToValues;
13132
+ exports.collectGeoWidgetRegistrationsFromWidgets = collectGeoWidgetRegistrationsFromWidgets;
13133
+ exports.createGeoLevelMnemonicResolver = createGeoLevelMnemonicResolver;
11808
13134
  exports.createWidgetStore = createWidgetStore;
11809
13135
  exports.createZodSchema = createZodSchema;
11810
13136
  exports.defaultTheme = defaultTheme;
11811
13137
  exports.evaluateCondition = evaluateCondition;
13138
+ exports.evaluateWidgetConditions = evaluateWidgetConditions;
11812
13139
  exports.filterByCharacterType = filterByCharacterType;
11813
13140
  exports.formatCurrency = formatCurrency;
11814
13141
  exports.formatDate = formatDate;
@@ -11817,21 +13144,38 @@ exports.formatPhone = formatPhone;
11817
13144
  exports.formatValue = formatValue;
11818
13145
  exports.geoHierarchyBuilder = geoHierarchyBuilder;
11819
13146
  exports.getApiDataSource = getApiDataSource;
13147
+ exports.getCachedApiDataSource = getCachedApiDataSource;
11820
13148
  exports.getFormattedNumberLength = getFormattedNumberLength;
13149
+ exports.getGeoDescendantWidgetIds = getGeoDescendantWidgetIds;
13150
+ exports.getGeoGroupId = getGeoGroupId;
13151
+ exports.getGeoWidgetRegistrationsInGroup = getGeoWidgetRegistrationsInGroup;
11821
13152
  exports.getSchemaDataSource = getSchemaDataSource;
11822
13153
  exports.getStaticDataSource = getStaticDataSource;
11823
13154
  exports.getValueByPath = getValueByPath;
11824
13155
  exports.getWidgetValue = getWidgetValue;
13156
+ exports.hasVisibilityRules = hasVisibilityRules;
11825
13157
  exports.initI18n = initI18n;
11826
13158
  exports.isAllowedKey = isAllowedKey;
13159
+ exports.isUpstreamGeoAncestor = isUpstreamGeoAncestor;
11827
13160
  exports.normalizeNumericDefault = normalizeNumericDefault;
13161
+ exports.normalizeOptionRules = normalizeOptionRules;
13162
+ exports.orderGeoWidgetRegistrations = orderGeoWidgetRegistrations;
11828
13163
  exports.parseDataPath = parseDataPath;
11829
13164
  exports.parseNumber = parseNumber;
13165
+ exports.rebuildGeoHierarchyFromRegistrations = rebuildGeoHierarchyFromRegistrations;
13166
+ exports.reconcileGeoHierarchiesInValues = reconcileGeoHierarchiesInValues;
11830
13167
  exports.registerDefaultWidgets = registerDefaultWidgets;
13168
+ exports.registerGeoWidget = registerGeoWidget;
13169
+ exports.registerGeoWidgetParent = registerGeoWidgetParent;
11831
13170
  exports.removeMask = removeMask;
11832
13171
  exports.resetAll = resetAll;
13172
+ exports.resetAndSeedGeoHierarchyFromValues = resetAndSeedGeoHierarchyFromValues;
11833
13173
  exports.resetWidget = resetWidget;
13174
+ exports.resolveGeoWidgetLevelLabel = resolveGeoWidgetLevelLabel;
13175
+ exports.resolveGeoWidgetLevelValue = resolveGeoWidgetLevelValue;
11834
13176
  exports.resolveTheme = resolveTheme;
13177
+ exports.resolveWidgetIdValue = resolveWidgetIdValue;
13178
+ exports.seedGeoHierarchyFromValues = seedGeoHierarchyFromValues;
11835
13179
  exports.setDataSource = setDataSource;
11836
13180
  exports.setError = setError;
11837
13181
  exports.setLoading = setLoading;
@@ -11841,11 +13185,14 @@ exports.setValueByPath = setValueByPath;
11841
13185
  exports.setValues = setValues;
11842
13186
  exports.setWidgetValue = setWidgetValue;
11843
13187
  exports.shouldEnableWidget = shouldEnableWidget;
13188
+ exports.shouldRequireWidget = shouldRequireWidget;
11844
13189
  exports.shouldShowWidget = shouldShowWidget;
11845
13190
  exports.transformDataSourceOptions = transformDataSourceOptions;
11846
13191
  exports.translatePanelConfig = translatePanelConfig;
11847
13192
  exports.translateUISchema = translateUISchema;
11848
13193
  exports.translateWidgetConfig = translateWidgetConfig;
13194
+ exports.unregisterGeoWidget = unregisterGeoWidget;
13195
+ exports.unregisterGeoWidgetParent = unregisterGeoWidgetParent;
11849
13196
  exports.useBaseWidget = useBaseWidget;
11850
13197
  exports.useGeoWidgetCascade = useGeoWidgetCascade;
11851
13198
  exports.useWidgetCascade = useWidgetCascade;