@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.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createSlice, configureStore } from '@reduxjs/toolkit';
2
- import React, { useContext, createContext, useMemo, useEffect, useRef, useCallback, useState, useId } from 'react';
2
+ import React, { useContext, createContext, useMemo, useEffect, useRef, useCallback, useState, useId, memo } from 'react';
3
3
  import { Provider, useDispatch, useSelector, useStore } from 'react-redux';
4
4
  import { z } from 'zod';
5
5
  import { createPortal } from 'react-dom';
@@ -173,15 +173,33 @@ const parseDataPath = (dataPath) => {
173
173
  /**
174
174
  * Get value from widget state using data path
175
175
  */
176
+ /**
177
+ * Resolve a widget-id reference in Redux values.
178
+ * Supports namespaced ids (e.g. "rv-section-0__region_code" when ref is "region_code").
179
+ */
180
+ const resolveWidgetIdValue = (values, ref) => {
181
+ if (!ref) {
182
+ return undefined;
183
+ }
184
+ if (ref.includes('.')) {
185
+ return getValueByPath(values, ref);
186
+ }
187
+ if (Object.prototype.hasOwnProperty.call(values, ref)) {
188
+ return values[ref];
189
+ }
190
+ const suffix = `__${ref}`;
191
+ for (const [key, val] of Object.entries(values)) {
192
+ if (key.endsWith(suffix)) {
193
+ return val;
194
+ }
195
+ }
196
+ return undefined;
197
+ };
176
198
  const getWidgetValue = (values, dataPath, widgetId) => {
177
199
  if (!dataPath) {
178
200
  // Fallback to widget-id if no data path
179
201
  return values[widgetId];
180
202
  }
181
- if (widgetId == "user-profile") {
182
- console.log('values', values);
183
- console.log('dataPath', dataPath);
184
- }
185
203
  if (typeof dataPath === 'string') {
186
204
  return getValueByPath(values, dataPath);
187
205
  }
@@ -384,6 +402,18 @@ const createZodSchema = (validation, required = false) => {
384
402
  return schema;
385
403
  };
386
404
 
405
+ const normalizeBooleanLike = (val) => {
406
+ if (val === true || val === 1)
407
+ return true;
408
+ if (val === false || val === 0 || val === null || val === undefined || val === '') {
409
+ return false;
410
+ }
411
+ if (typeof val === 'string') {
412
+ const normalized = val.trim().toLowerCase();
413
+ return normalized === 'true' || normalized === 'yes' || normalized === '1';
414
+ }
415
+ return Boolean(val);
416
+ };
387
417
  /**
388
418
  * Evaluate condition against field value
389
419
  */
@@ -392,6 +422,9 @@ const evaluateCondition = (condition, allValues) => {
392
422
  const { operator, value } = condition;
393
423
  switch (operator) {
394
424
  case 'equals':
425
+ if (typeof value === 'boolean' || typeof fieldValue === 'boolean') {
426
+ return normalizeBooleanLike(fieldValue) === normalizeBooleanLike(value);
427
+ }
395
428
  return fieldValue === value;
396
429
  case 'notEquals':
397
430
  return fieldValue !== value;
@@ -424,37 +457,62 @@ const evaluateCondition = (condition, allValues) => {
424
457
  }
425
458
  };
426
459
  /**
427
- * Check if widget should be visible based on conditions
460
+ * Normalize widget-data-options into a sequential list of action rules.
461
+ * Supports legacy single { action, condition } and new { actions: [...] }.
428
462
  */
429
- const shouldShowWidget = (options, allValues) => {
430
- if (!options?.condition) {
431
- return true;
463
+ const normalizeOptionRules = (options) => {
464
+ if (!options) {
465
+ return [];
432
466
  }
433
- const conditionResult = evaluateCondition(options.condition, allValues);
434
- if (options.action === 'show') {
435
- return conditionResult;
467
+ if (Array.isArray(options.actions) && options.actions.length > 0) {
468
+ return options.actions.filter((rule) => !!rule?.action);
436
469
  }
437
- if (options.action === 'hide') {
438
- return !conditionResult;
470
+ if (options.action && options.condition) {
471
+ return [{ action: options.action, condition: options.condition }];
439
472
  }
440
- return true;
473
+ return [];
474
+ };
475
+ const hasVisibilityRules = (options) => {
476
+ return normalizeOptionRules(options).some((rule) => rule.action === 'show' || rule.action === 'hide');
441
477
  };
442
478
  /**
443
- * Check if widget should be enabled based on conditions
479
+ * Evaluate widget-data-options rules sequentially.
480
+ * show/hide and enable/disable only affect visibility and enabled state.
481
+ * require is independent: required = widget-required OR require-condition-match.
444
482
  */
445
- const shouldEnableWidget = (options, allValues) => {
446
- if (!options?.condition) {
447
- return true;
448
- }
449
- const conditionResult = evaluateCondition(options.condition, allValues);
450
- if (options.action === 'enable') {
451
- return conditionResult;
452
- }
453
- if (options.action === 'disable') {
454
- return !conditionResult;
483
+ const evaluateWidgetConditions = (options, allValues, baseRequired = false) => {
484
+ let visible = true;
485
+ let enabled = true;
486
+ let required = baseRequired;
487
+ const rules = normalizeOptionRules(options);
488
+ for (const rule of rules) {
489
+ if (!rule.condition) {
490
+ continue;
491
+ }
492
+ const match = evaluateCondition(rule.condition, allValues);
493
+ switch (rule.action) {
494
+ case 'show':
495
+ visible = match;
496
+ break;
497
+ case 'hide':
498
+ visible = !match;
499
+ break;
500
+ case 'enable':
501
+ enabled = match;
502
+ break;
503
+ case 'disable':
504
+ enabled = !match;
505
+ break;
506
+ case 'require':
507
+ required = baseRequired || match;
508
+ break;
509
+ }
455
510
  }
456
- return true;
511
+ return { visible, enabled, required };
457
512
  };
513
+ const shouldShowWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).visible;
514
+ const shouldEnableWidget = (options, allValues) => evaluateWidgetConditions(options, allValues).enabled;
515
+ const shouldRequireWidget = (options, allValues, baseRequired = false) => evaluateWidgetConditions(options, allValues, baseRequired).required;
458
516
 
459
517
  /**
460
518
  * Format number with thousand and decimal separators
@@ -1024,6 +1082,67 @@ const formatValue = (value, format, widgetType) => {
1024
1082
  return value?.toString() || '';
1025
1083
  };
1026
1084
 
1085
+ const apiDataSourceCache = new Map();
1086
+ const apiDataSourceInflight = new Map();
1087
+ function buildApiRequestContext(dataSource, allValues, levelId) {
1088
+ let depValue = null;
1089
+ if (dataSource.dependsOn) {
1090
+ if (dataSource.dependsOn.includes('.')) {
1091
+ depValue = getValueByPath(allValues, dataSource.dependsOn);
1092
+ }
1093
+ else {
1094
+ depValue = resolveWidgetIdValue(allValues, dataSource.dependsOn);
1095
+ }
1096
+ if (depValue === null || depValue === undefined || depValue === '') {
1097
+ return null;
1098
+ }
1099
+ }
1100
+ const method = dataSource.method || 'GET';
1101
+ const staticParams = { ...dataSource.params };
1102
+ const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
1103
+ for (const [key, value] of Object.entries(dataSource)) {
1104
+ if (!standardFields.includes(key) && value !== undefined && value !== null) {
1105
+ staticParams[key] = value;
1106
+ }
1107
+ }
1108
+ if (levelId) {
1109
+ staticParams.level_id = levelId;
1110
+ }
1111
+ const requestParams = { ...staticParams };
1112
+ if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
1113
+ const parentValueId = typeof depValue === 'object' && depValue !== null
1114
+ ? (depValue.level_value_id || depValue.id || depValue.value || depValue)
1115
+ : depValue;
1116
+ if (staticParams.level_id) {
1117
+ requestParams.parent_level_value_id = parentValueId;
1118
+ }
1119
+ else {
1120
+ const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
1121
+ requestParams[paramKey] = parentValueId;
1122
+ }
1123
+ }
1124
+ else if (staticParams.level_id) {
1125
+ requestParams.parent_level_value_id = '';
1126
+ }
1127
+ const service = dataSource.service;
1128
+ const endpoint = dataSource.endpoint;
1129
+ if (!service || !endpoint) {
1130
+ return null;
1131
+ }
1132
+ return { service, endpoint, method, requestParams };
1133
+ }
1134
+ function buildApiDataSourceCacheKey(service, endpoint, method, requestParams) {
1135
+ return `${service}|${endpoint}|${method}|${JSON.stringify(requestParams)}`;
1136
+ }
1137
+ /** Return cached API options when already fetched (e.g. duplicate table cells). */
1138
+ function getCachedApiDataSource(dataSource, allValues, levelId) {
1139
+ const context = buildApiRequestContext(dataSource, allValues, levelId);
1140
+ if (!context) {
1141
+ return undefined;
1142
+ }
1143
+ const cacheKey = buildApiDataSourceCacheKey(context.service, context.endpoint, context.method, context.requestParams);
1144
+ return apiDataSourceCache.get(cacheKey);
1145
+ }
1027
1146
  /**
1028
1147
  * Get static data source options
1029
1148
  */
@@ -1041,111 +1160,33 @@ const getApiDataSource = async (dataSource, allValues, dataSourceRequestHandler,
1041
1160
  return [];
1042
1161
  }
1043
1162
  try {
1044
- // Get dependency value if exists
1045
- // dependsOn can be either a data path (e.g., "person.address") or a widget-id
1046
- let depValue = null;
1047
- if (dataSource.dependsOn) {
1048
- // First try as data path
1049
- depValue = getValueByPath(allValues, dataSource.dependsOn);
1050
- // If not found and doesn't contain dots, try as widget-id
1051
- if ((depValue === null || depValue === undefined) && !dataSource.dependsOn.includes('.')) {
1052
- depValue = allValues[dataSource.dependsOn];
1053
- // Smart resolution: If not found at top level, try to find the dependency in the same nested object
1054
- // by looking for other keys in allValues that might contain the dependency.
1055
- // We look for objects that contain both the current widget's path (if we can guess it) and the dependency.
1056
- // But since we don't know the current widget's path here, we search for any object that has this dependency key.
1057
- if (depValue === null || depValue === undefined || depValue === '') {
1058
- for (const val of Object.values(allValues)) {
1059
- if (val && typeof val === 'object' && !Array.isArray(val) && dataSource.dependsOn in val) {
1060
- depValue = val[dataSource.dependsOn];
1061
- if (depValue !== null && depValue !== undefined && depValue !== '')
1062
- break;
1063
- }
1064
- }
1065
- }
1066
- }
1067
- if (depValue === null || depValue === undefined || depValue === '') {
1068
- // If dependency is empty, return empty array
1069
- return [];
1070
- }
1071
- }
1072
- // Build request parameters
1073
- const method = dataSource.method || 'GET';
1074
- // Extract static params from dataSource
1075
- // Include explicit params object and any additional fields (like level_id)
1076
- const staticParams = { ...dataSource.params };
1077
- // Extract additional fields that aren't part of the standard ApiDataSource interface
1078
- // These are fields like level_id that might be directly on the dataSource
1079
- // BUT: level_id should come from widget-geo-config.level, not from dataSource
1080
- const standardFields = ['type', 'service', 'endpoint', 'url', 'method', 'dependsOn', 'valueKey', 'labelKey', 'headers', 'body', 'params', 'level_id'];
1081
- for (const [key, value] of Object.entries(dataSource)) {
1082
- if (!standardFields.includes(key) && value !== undefined && value !== null) {
1083
- staticParams[key] = value;
1084
- }
1085
- }
1086
- // If levelId is provided (from widget-geo-config.level), use it instead of any level_id in dataSource
1087
- if (levelId) {
1088
- staticParams.level_id = levelId;
1089
- }
1090
- // Build request params object
1091
- const requestParams = { ...staticParams };
1092
- // Add dependency value to params
1093
- if (dataSource.dependsOn && depValue !== null && depValue !== undefined) {
1094
- // Extract the actual value ID if depValue is an object
1095
- const parentValueId = typeof depValue === 'object' && depValue !== null
1096
- ? (depValue.level_value_id || depValue.id || depValue.value || depValue)
1097
- : depValue;
1098
- // For geo APIs, use parent_level_value_id
1099
- if (staticParams.level_id) {
1100
- requestParams.parent_level_value_id = parentValueId;
1101
- }
1102
- else {
1103
- // For other APIs, use the dependency field name as param key
1104
- const paramKey = dataSource.dependsOn.split('.').pop() || 'filter';
1105
- requestParams[paramKey] = parentValueId;
1106
- }
1107
- }
1108
- else if (staticParams.level_id) {
1109
- // First level has no parent, send empty string as many OpenG2P APIs expect it
1110
- requestParams.parent_level_value_id = "";
1111
- }
1112
- // Get service mnemonic and endpoint (required)
1113
- const service = dataSource.service;
1114
- const endpoint = dataSource.endpoint;
1115
- if (!service) {
1116
- console.error('[getApiDataSource] API data source missing service mnemonic. Use "service" field instead of "url"');
1163
+ const context = buildApiRequestContext(dataSource, allValues, levelId);
1164
+ if (!context) {
1117
1165
  return [];
1118
1166
  }
1119
- if (!endpoint) {
1120
- console.error('[getApiDataSource] API data source missing endpoint. Use "endpoint" field to specify the operation (e.g., "get_g2p_geo_level_values")');
1121
- return [];
1122
- }
1123
- // Call handler — let any throw propagate to the outer catch so it is logged once
1124
- // by useBaseWidget rather than double-logged here (which can cascade when
1125
- // intercept-console-error.js converts console.error calls into thrown errors).
1126
- const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, {
1127
- headers: dataSource.headers,
1128
- });
1129
- // Handle OpenG2P response format (response_body.response_payload)
1130
- if (response && typeof response === 'object') {
1131
- if (response.response_body?.response_payload && Array.isArray(response.response_body.response_payload)) {
1132
- return response.response_body.response_payload;
1133
- }
1134
- }
1135
- // Handle array response
1136
- if (Array.isArray(response)) {
1137
- return response;
1167
+ const { service, endpoint, method, requestParams } = context;
1168
+ const cacheKey = buildApiDataSourceCacheKey(service, endpoint, method, requestParams);
1169
+ const cached = apiDataSourceCache.get(cacheKey);
1170
+ if (cached) {
1171
+ return cached;
1172
+ }
1173
+ const inflight = apiDataSourceInflight.get(cacheKey);
1174
+ if (inflight) {
1175
+ return inflight;
1176
+ }
1177
+ const fetchPromise = (async () => {
1178
+ const response = await dataSourceRequestHandler(service, endpoint, method, requestParams, { headers: dataSource.headers });
1179
+ const parsed = Array.isArray(response) ? response : [];
1180
+ apiDataSourceCache.set(cacheKey, parsed);
1181
+ return parsed;
1182
+ })();
1183
+ apiDataSourceInflight.set(cacheKey, fetchPromise);
1184
+ try {
1185
+ return await fetchPromise;
1138
1186
  }
1139
- // Handle object response (extract array from common keys)
1140
- if (response && typeof response === 'object') {
1141
- if (response.data && Array.isArray(response.data)) {
1142
- return response.data;
1143
- }
1144
- if (response.results && Array.isArray(response.results)) {
1145
- return response.results;
1146
- }
1187
+ finally {
1188
+ apiDataSourceInflight.delete(cacheKey);
1147
1189
  }
1148
- return [];
1149
1190
  }
1150
1191
  catch (error) {
1151
1192
  // Rethrow so useBaseWidget's catch can log it with full widget context
@@ -1931,85 +1972,588 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
1931
1972
  return content;
1932
1973
  };
1933
1974
 
1934
- // Define stable empty arrays to avoid selector reference issues
1935
- const EMPTY_ERRORS = [];
1936
- const EMPTY_DATA_SOURCE$1 = [];
1937
- const useBaseWidget = (options) => {
1938
- const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
1939
- const dispatch = useDispatch();
1940
- const context = useWidgetContext();
1941
- const eventBus = useWidgetEventBus();
1942
- const widgetId = config['widget-id'];
1943
- // Fall back to WidgetContext for dataSourceRequestHandler
1944
- const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
1945
- // Get state from Redux
1946
- const values = useSelector((state) => state.widget.values);
1947
- const errors = useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
1948
- const touched = useSelector((state) => state.widget.touched[widgetId] || false);
1949
- const loading = useSelector((state) => state.widget.loading[widgetId] || false);
1950
- const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE$1);
1951
- // Skip value handling for layout widgets (they don't store data values)
1952
- // Infer layout from widget-type
1953
- const isLayoutWidget = config['widget-type'] === 'layout';
1954
- // Track if user has explicitly set a value to prevent default from overwriting
1955
- const userHasSetValueRef = useRef(false);
1956
- // Use ref for values to avoid stale closures in handleChange
1957
- const valuesRef = useRef(values);
1958
- const loadingRef = useRef(loading);
1959
- const dataSourceOptionsRef = useRef(dataSourceOptions);
1960
- useEffect(() => {
1961
- valuesRef.current = values;
1962
- loadingRef.current = loading;
1963
- dataSourceOptionsRef.current = dataSourceOptions;
1964
- }, [values, loading, dataSourceOptions]);
1965
- // Track last dispatched value to prevent duplicate dispatches
1966
- const lastDispatchedValueRef = useRef(null);
1967
- // Helper to extract displayable value from object (especially geo hierarchy objects)
1968
- const extractValueFromObject = useCallback((obj) => {
1969
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
1970
- return obj;
1975
+ /**
1976
+ * Custom hook for widget translations
1977
+ * Provides translation function with widget-specific namespace and fallback support
1978
+ */
1979
+ const useWidgetTranslation = () => {
1980
+ const { translate: translateFunction } = useWidgetContext();
1981
+ /**
1982
+ * Translate a key with flexible namespace support
1983
+ * Supports translation keys in various formats and direct strings
1984
+ *
1985
+ * Translation key formats supported:
1986
+ * - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
1987
+ * - "Name" - Direct string (will be looked up in flat translation structure)
1988
+ * - "sections.personalDetails" - Nested key (for backward compatibility)
1989
+ *
1990
+ * With flat translation structure, direct strings like "Name" are automatically
1991
+ * translated by looking them up in the translation resources.
1992
+ *
1993
+ * @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
1994
+ * @param options - Translation options (interpolation values, default value, etc.)
1995
+ * @returns Translated string or original string if translation not found
1996
+ */
1997
+ const translate = (keyOrString, options) => {
1998
+ if (!keyOrString) {
1999
+ return options?.defaultValue || '';
1971
2000
  }
1972
- // Check for geo hierarchy structure first
1973
- const geoConfig = config['widget-geo-config'];
1974
- if (geoConfig) {
1975
- // If we have a geo hierarchy object, extract the value for this specific level
1976
- const hierarchy = obj.hierarchy || obj.geo_code_hierarchy_json?.hierarchy;
1977
- if (Array.isArray(hierarchy)) {
1978
- const levelData = hierarchy.find((l) => l.level === geoConfig.level);
1979
- if (levelData) {
1980
- return levelData.level_value_id;
1981
- }
1982
- }
2001
+ // Use the provided translation function or fallback to the key
2002
+ if (translateFunction) {
2003
+ return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
1983
2004
  }
1984
- if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj || 'hierarchy' in obj) {
1985
- if ('geo_lowest_level_value_id' in obj) {
1986
- return obj.geo_lowest_level_value_id;
1987
- }
1988
- if ('lowest_level_value_id' in obj) {
1989
- return obj.lowest_level_value_id;
1990
- }
1991
- // Fallback for nested geo_code_hierarchy_json
1992
- if (obj.geo_code_hierarchy_json?.lowest_level_value_id) {
1993
- return obj.geo_code_hierarchy_json.lowest_level_value_id;
2005
+ // Fallback to key if no translation function available
2006
+ return options?.defaultValue || keyOrString;
2007
+ };
2008
+ /**
2009
+ * Translate widget config property
2010
+ * Attempts to translate the value, but if translation is not found,
2011
+ * returns the original value as-is (graceful fallback)
2012
+ *
2013
+ * This function will:
2014
+ * - Try to translate any string value
2015
+ * - If translation exists, use the translated value
2016
+ * - If translation doesn't exist (returns same value or throws), use original value
2017
+ * - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
2018
+ */
2019
+ const translateConfig = (value, fallback) => {
2020
+ if (!value) {
2021
+ return fallback || '';
2022
+ }
2023
+ // Try to translate the value
2024
+ if (translateFunction) {
2025
+ try {
2026
+ // Pass defaultValue to ensure we get the original value if translation fails
2027
+ const translated = translateFunction(value, { defaultValue: value });
2028
+ // If translation returns empty, null, undefined, or the exact same value,
2029
+ // it means no translation was found - return the original value
2030
+ if (!translated || translated === value) {
2031
+ return value;
2032
+ }
2033
+ // Translation found, return it
2034
+ return translated;
1994
2035
  }
1995
- if (obj.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
1996
- return obj.geo_code_hierarchy_json.geo_lowest_level_value_id;
2036
+ catch (error) {
2037
+ // If translation throws an error (e.g., missing key warning), return original value
2038
+ return value;
1997
2039
  }
1998
- // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
1999
- return undefined;
2000
2040
  }
2001
- // Try common value fields
2002
- if ('value' in obj) {
2003
- return obj.value;
2041
+ // No translation function available, return value as-is
2042
+ return value;
2043
+ };
2044
+ // No need of this getLanguage and changeLanguage functions
2045
+ /**
2046
+ * Get current language
2047
+ */
2048
+ // const getLanguage = (): string => {
2049
+ // return i18n.language || 'en';
2050
+ // };
2051
+ /**
2052
+ * Change language
2053
+ */
2054
+ // const changeLanguage = (lng: string): Promise<void> => {
2055
+ // return i18n.changeLanguage(lng).then(() => undefined);
2056
+ // };
2057
+ return {
2058
+ t: translate,
2059
+ translate,
2060
+ translateConfig,
2061
+ // getLanguage,
2062
+ // changeLanguage,
2063
+ // i18n: null,
2064
+ };
2065
+ };
2066
+
2067
+ /**
2068
+ * Geo Hierarchy Builder
2069
+ * Manages geo hierarchy state and builds hierarchy JSON structure
2070
+ */
2071
+ function extractLevelValueFromStored(value, geoConfig) {
2072
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
2073
+ return value;
2074
+ }
2075
+ const hierarchy = value.hierarchy || value.geo_code_hierarchy_json?.hierarchy;
2076
+ if (Array.isArray(hierarchy)) {
2077
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2078
+ if (levelData) {
2079
+ return levelData.level_value_id;
2004
2080
  }
2005
- if ('id' in obj) {
2006
- return obj.id;
2081
+ // Level absent from hierarchy (e.g. cleared by upstream cascade) — do not use lowest-level fallback
2082
+ return undefined;
2083
+ }
2084
+ if ('geo_lowest_level_value_id' in value) {
2085
+ return value.geo_lowest_level_value_id;
2086
+ }
2087
+ if ('lowest_level_value_id' in value) {
2088
+ return value.lowest_level_value_id;
2089
+ }
2090
+ if (value.geo_code_hierarchy_json?.lowest_level_value_id) {
2091
+ return value.geo_code_hierarchy_json.lowest_level_value_id;
2092
+ }
2093
+ if (value.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2094
+ return value.geo_code_hierarchy_json.geo_lowest_level_value_id;
2095
+ }
2096
+ return undefined;
2097
+ }
2098
+ /**
2099
+ * Resolve the display value for a geo level widget.
2100
+ * When widgetId is explicitly set in Redux (including cleared undefined/null), do not
2101
+ * fall back to shared hierarchy dataPath — that stale path was keeping grandchildren visible.
2102
+ */
2103
+ function resolveGeoWidgetLevelValue(values, widgetId, dataPath, geoConfig) {
2104
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
2105
+ let value = values[widgetId];
2106
+ if (value === undefined || value === null || value === '') {
2107
+ return value;
2007
2108
  }
2008
- if ('label' in obj) {
2009
- return obj.label;
2109
+ if (typeof value === 'object' && !Array.isArray(value)) {
2110
+ return extractLevelValueFromStored(value, geoConfig);
2010
2111
  }
2011
- if ('name' in obj) {
2012
- return obj.name;
2112
+ return value;
2113
+ }
2114
+ if (!dataPath) {
2115
+ return undefined;
2116
+ }
2117
+ let value = getWidgetValue(values, dataPath, widgetId);
2118
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
2119
+ value = extractLevelValueFromStored(value, geoConfig);
2120
+ }
2121
+ return value;
2122
+ }
2123
+ /**
2124
+ * Write the in-memory geo hierarchy builder state into Redux at the shared dataPath.
2125
+ */
2126
+ function applySharedGeoHierarchyToValues(baseValues, groupId, dataPath, widgetId) {
2127
+ const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2128
+ if (!dataPath || typeof dataPath !== 'string') {
2129
+ return baseValues;
2130
+ }
2131
+ const inner = hierarchyJson?.geo_code_hierarchy_json;
2132
+ const lowestId = hierarchyJson?.geo_lowest_level_value_id;
2133
+ if (dataPath.endsWith('.geo_code_hierarchy_json')) {
2134
+ const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2135
+ let finalUpdatedValues = setWidgetValue(baseValues, dataPath, widgetId, inner);
2136
+ finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, lowestId);
2137
+ return finalUpdatedValues;
2138
+ }
2139
+ return setWidgetValue(baseValues, dataPath, widgetId, inner);
2140
+ }
2141
+ class GeoHierarchyBuilder {
2142
+ constructor() {
2143
+ this.hierarchies = new Map();
2144
+ }
2145
+ /**
2146
+ * Get or create hierarchy state for a group
2147
+ */
2148
+ getHierarchy(groupId = 'default') {
2149
+ if (!this.hierarchies.has(groupId)) {
2150
+ this.hierarchies.set(groupId, {
2151
+ levels: new Map(),
2152
+ order: [],
2153
+ });
2154
+ }
2155
+ return this.hierarchies.get(groupId);
2156
+ }
2157
+ /**
2158
+ * Add a level to the hierarchy
2159
+ */
2160
+ addLevel(level, level_value_id, level_value_mnemonic, groupId = 'default') {
2161
+ const hierarchy = this.getHierarchy(groupId);
2162
+ // If level already exists, remove it and everything after it
2163
+ const existingIndex = hierarchy.order.indexOf(level);
2164
+ if (existingIndex >= 0) {
2165
+ // Remove this level and all subsequent levels
2166
+ const levelsToRemove = hierarchy.order.slice(existingIndex);
2167
+ levelsToRemove.forEach((l) => {
2168
+ hierarchy.levels.delete(l);
2169
+ hierarchy.order = hierarchy.order.filter((o) => o !== l);
2170
+ });
2171
+ }
2172
+ // Add new level
2173
+ hierarchy.levels.set(level, {
2174
+ level,
2175
+ level_value_id,
2176
+ level_value_mnemonic,
2177
+ });
2178
+ hierarchy.order.push(level);
2179
+ }
2180
+ /**
2181
+ * Remove a level and all levels below it
2182
+ */
2183
+ removeLevelAndBelow(level, groupId = 'default') {
2184
+ const hierarchy = this.getHierarchy(groupId);
2185
+ const index = hierarchy.order.indexOf(level);
2186
+ if (index >= 0) {
2187
+ // Remove this level and all subsequent levels
2188
+ const levelsToRemove = hierarchy.order.slice(index);
2189
+ levelsToRemove.forEach((l) => {
2190
+ hierarchy.levels.delete(l);
2191
+ hierarchy.order = hierarchy.order.filter((o) => o !== l);
2192
+ });
2193
+ }
2194
+ }
2195
+ /**
2196
+ * Build hierarchy JSON structure
2197
+ */
2198
+ buildHierarchyJson(groupId = 'default') {
2199
+ const hierarchy = this.getHierarchy(groupId);
2200
+ if (hierarchy.order.length === 0) {
2201
+ return null;
2202
+ }
2203
+ const hierarchyArray = hierarchy.order.map((level) => {
2204
+ const data = hierarchy.levels.get(level);
2205
+ return {
2206
+ level: data.level,
2207
+ level_value_id: data.level_value_id,
2208
+ level_value_mnemonic: data.level_value_mnemonic,
2209
+ };
2210
+ });
2211
+ const lowestLevel = hierarchy.order[hierarchy.order.length - 1];
2212
+ const lowestLevelData = hierarchy.levels.get(lowestLevel);
2213
+ return {
2214
+ geo_lowest_level_value_id: lowestLevelData.level_value_id,
2215
+ geo_code_hierarchy_json: {
2216
+ hierarchy: hierarchyArray,
2217
+ lowest_level_value_id: lowestLevelData.level_value_id,
2218
+ },
2219
+ };
2220
+ }
2221
+ /**
2222
+ * Clear hierarchy for a group
2223
+ */
2224
+ clear(groupId = 'default') {
2225
+ this.hierarchies.delete(groupId);
2226
+ }
2227
+ /**
2228
+ * Clear all hierarchies
2229
+ */
2230
+ clearAll() {
2231
+ this.hierarchies.clear();
2232
+ }
2233
+ /**
2234
+ * Get current levels for a group
2235
+ */
2236
+ getLevels(groupId = 'default') {
2237
+ const hierarchy = this.getHierarchy(groupId);
2238
+ return hierarchy.order.map((level) => hierarchy.levels.get(level));
2239
+ }
2240
+ }
2241
+ // Singleton instance
2242
+ const geoHierarchyBuilder = new GeoHierarchyBuilder();
2243
+ /** Sentinel written to Redux when a geo level is cleared by cascade (distinct from "never set"). */
2244
+ const GEO_LEVEL_CLEARED = null;
2245
+ const geoWidgetParentRegistry = new Map();
2246
+ const geoWidgetConfigRegistry = new Map();
2247
+ function registerGeoWidgetParent(widgetId, parentWidgetId) {
2248
+ geoWidgetParentRegistry.set(widgetId, parentWidgetId || null);
2249
+ }
2250
+ function unregisterGeoWidgetParent(widgetId) {
2251
+ geoWidgetParentRegistry.delete(widgetId);
2252
+ }
2253
+ function registerGeoWidget(widgetId, geoConfig, dataPath) {
2254
+ if (typeof dataPath !== 'string') {
2255
+ return;
2256
+ }
2257
+ const parentWidgetId = geoConfig.parentWidgetId?.trim() ? geoConfig.parentWidgetId : null;
2258
+ registerGeoWidgetParent(widgetId, parentWidgetId);
2259
+ geoWidgetConfigRegistry.set(widgetId, {
2260
+ widgetId,
2261
+ parentWidgetId,
2262
+ level: geoConfig.level,
2263
+ geoConfig,
2264
+ dataPath,
2265
+ groupId: getGeoGroupId(dataPath),
2266
+ });
2267
+ }
2268
+ function unregisterGeoWidget(widgetId) {
2269
+ unregisterGeoWidgetParent(widgetId);
2270
+ geoWidgetConfigRegistry.delete(widgetId);
2271
+ }
2272
+ function orderGeoWidgetRegistrations(registrations) {
2273
+ if (registrations.length <= 1) {
2274
+ return registrations;
2275
+ }
2276
+ const roots = registrations.filter((entry) => !entry.parentWidgetId);
2277
+ if (roots.length === 0) {
2278
+ return registrations;
2279
+ }
2280
+ const ordered = [];
2281
+ let current = roots[0];
2282
+ const visited = new Set();
2283
+ while (current && !visited.has(current.widgetId)) {
2284
+ visited.add(current.widgetId);
2285
+ ordered.push(current);
2286
+ current = registrations.find((entry) => entry.parentWidgetId === current.widgetId);
2287
+ }
2288
+ return ordered.length > 0 ? ordered : registrations;
2289
+ }
2290
+ function resolveLevelValueId(rawValue) {
2291
+ if (rawValue === null || rawValue === undefined || rawValue === '') {
2292
+ return null;
2293
+ }
2294
+ if (typeof rawValue === 'string' || typeof rawValue === 'number') {
2295
+ return String(rawValue);
2296
+ }
2297
+ if (typeof rawValue === 'object') {
2298
+ const id = rawValue.level_value_id || rawValue.id || rawValue.value;
2299
+ return id != null && id !== '' ? String(id) : null;
2300
+ }
2301
+ return null;
2302
+ }
2303
+ function resolveStoredMnemonic(values, registration, valueId) {
2304
+ const stored = getWidgetValue(values, registration.dataPath, registration.widgetId);
2305
+ const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
2306
+ if (!Array.isArray(hierarchy)) {
2307
+ return undefined;
2308
+ }
2309
+ const levelData = hierarchy.find((entry) => entry.level === registration.level);
2310
+ if (levelData && String(levelData.level_value_id) === String(valueId)) {
2311
+ return levelData.level_value_mnemonic ? String(levelData.level_value_mnemonic) : undefined;
2312
+ }
2313
+ return undefined;
2314
+ }
2315
+ /** Resolve display mnemonic from cached dropdown options, then stored hierarchy. */
2316
+ function createGeoLevelMnemonicResolver(values, dataSources) {
2317
+ return (registration, valueId) => {
2318
+ const options = dataSources[registration.widgetId];
2319
+ const option = options?.find((entry) => String(entry.value) === String(valueId));
2320
+ if (option?.label) {
2321
+ return option.label;
2322
+ }
2323
+ return resolveStoredMnemonic(values, registration, valueId);
2324
+ };
2325
+ }
2326
+ /** Rebuild group hierarchy from widget values in parent→child order; stop at first missing level. */
2327
+ function rebuildGeoHierarchyFromRegistrations(groupId, values, registrations, resolveMnemonic) {
2328
+ const ordered = orderGeoWidgetRegistrations(registrations.filter((entry) => entry.groupId === groupId));
2329
+ geoHierarchyBuilder.clear(groupId);
2330
+ for (const registration of ordered) {
2331
+ const rawValue = resolveGeoWidgetLevelValue(values, registration.widgetId, registration.dataPath, registration.geoConfig);
2332
+ const valueId = resolveLevelValueId(rawValue);
2333
+ if (!valueId) {
2334
+ break;
2335
+ }
2336
+ const mnemonic = resolveMnemonic?.(registration, valueId) ??
2337
+ resolveStoredMnemonic(values, registration, valueId) ??
2338
+ valueId;
2339
+ geoHierarchyBuilder.addLevel(registration.level, valueId, mnemonic, groupId);
2340
+ }
2341
+ return geoHierarchyBuilder.buildHierarchyJson(groupId) !== null;
2342
+ }
2343
+ function collectGeoWidgetRegistrationsFromWidgets(widgets, namespace) {
2344
+ return widgets
2345
+ .filter((widget) => widget['widget-geo-config'] && typeof widget['widget-data-path'] === 'string')
2346
+ .map((widget) => {
2347
+ const originalWidgetId = widget['widget-id'];
2348
+ const originalDataPath = widget['widget-data-path'];
2349
+ const geoConfig = widget['widget-geo-config'];
2350
+ const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
2351
+ const parentWidgetId = geoConfig.parentWidgetId?.trim()
2352
+ ? (namespace ? `${namespace}__${geoConfig.parentWidgetId}` : geoConfig.parentWidgetId)
2353
+ : null;
2354
+ const dataPath = namespace ? `${namespace}.${originalDataPath}` : originalDataPath;
2355
+ return {
2356
+ widgetId,
2357
+ parentWidgetId,
2358
+ level: geoConfig.level,
2359
+ geoConfig,
2360
+ dataPath,
2361
+ groupId: getGeoGroupId(dataPath),
2362
+ };
2363
+ });
2364
+ }
2365
+ /** Reconcile all geo groups in section values before save. */
2366
+ function reconcileGeoHierarchiesInValues(values, registrations, dataSources = {}) {
2367
+ const groupIds = [...new Set(registrations.map((entry) => entry.groupId))];
2368
+ let updatedValues = values;
2369
+ const resolveMnemonic = createGeoLevelMnemonicResolver(updatedValues, dataSources);
2370
+ for (const groupId of groupIds) {
2371
+ const groupRegistrations = registrations.filter((entry) => entry.groupId === groupId);
2372
+ const dataPath = groupRegistrations[0]?.dataPath;
2373
+ const widgetId = groupRegistrations[0]?.widgetId;
2374
+ if (!dataPath || !widgetId) {
2375
+ continue;
2376
+ }
2377
+ rebuildGeoHierarchyFromRegistrations(groupId, updatedValues, groupRegistrations, resolveMnemonic);
2378
+ updatedValues = applySharedGeoHierarchyToValues(updatedValues, groupId, dataPath, widgetId);
2379
+ }
2380
+ return updatedValues;
2381
+ }
2382
+ function getGeoWidgetRegistrationsInGroup(groupId) {
2383
+ return orderGeoWidgetRegistrations([...geoWidgetConfigRegistry.values()].filter((entry) => entry.groupId === groupId));
2384
+ }
2385
+ /** True when changedWidgetId is any upstream geo parent of widgetId (not only immediate parent). */
2386
+ function isUpstreamGeoAncestor(changedWidgetId, widgetId, immediateParentWidgetId) {
2387
+ if (changedWidgetId === widgetId) {
2388
+ return false;
2389
+ }
2390
+ let cursor = immediateParentWidgetId;
2391
+ while (cursor) {
2392
+ if (cursor === changedWidgetId) {
2393
+ return true;
2394
+ }
2395
+ cursor = geoWidgetParentRegistry.get(cursor) ?? null;
2396
+ }
2397
+ return false;
2398
+ }
2399
+ /** Group id for geo widgets sharing the same register prefix (e.g. `{registerId}`). */
2400
+ function getGeoGroupId(dataPath) {
2401
+ if (typeof dataPath === 'string' && dataPath.includes('.')) {
2402
+ return dataPath.split('.').slice(0, -1).join('.');
2403
+ }
2404
+ return 'default';
2405
+ }
2406
+ /**
2407
+ * Resolve the human-readable label for a geo level from persisted hierarchy JSON.
2408
+ * Used in readonly mode when API options are not loaded.
2409
+ */
2410
+ function resolveGeoWidgetLevelLabel(values, widgetId, dataPath, geoConfig) {
2411
+ if (!dataPath || typeof dataPath !== 'string') {
2412
+ return undefined;
2413
+ }
2414
+ const stored = getWidgetValue(values, dataPath, widgetId);
2415
+ const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
2416
+ if (!Array.isArray(hierarchy)) {
2417
+ return undefined;
2418
+ }
2419
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2420
+ if (levelData?.level_value_mnemonic) {
2421
+ return String(levelData.level_value_mnemonic);
2422
+ }
2423
+ return undefined;
2424
+ }
2425
+ /** All registered geo widgets that are descendants of ancestorWidgetId. */
2426
+ function getGeoDescendantWidgetIds(ancestorWidgetId) {
2427
+ const descendants = [];
2428
+ for (const [childId, parentId] of geoWidgetParentRegistry.entries()) {
2429
+ if (isUpstreamGeoAncestor(ancestorWidgetId, childId, parentId)) {
2430
+ descendants.push(childId);
2431
+ }
2432
+ }
2433
+ return descendants;
2434
+ }
2435
+ function readStoredHierarchyLevels(values, dataPath, widgetId) {
2436
+ const stored = getWidgetValue(values, dataPath, widgetId);
2437
+ const hierarchy = stored?.hierarchy || stored?.geo_code_hierarchy_json?.hierarchy;
2438
+ if (!Array.isArray(hierarchy)) {
2439
+ return [];
2440
+ }
2441
+ return hierarchy.filter((entry) => entry?.level && entry.level_value_id);
2442
+ }
2443
+ function builderMatchesStored(groupId, storedLevels) {
2444
+ const builderLevels = geoHierarchyBuilder.getLevels(groupId);
2445
+ if (builderLevels.length !== storedLevels.length) {
2446
+ return false;
2447
+ }
2448
+ return storedLevels.every((stored, index) => {
2449
+ const built = builderLevels[index];
2450
+ return (built.level === stored.level &&
2451
+ String(built.level_value_id) === String(stored.level_value_id));
2452
+ });
2453
+ }
2454
+ /** Seed in-memory builder from persisted hierarchy JSON (edit-mode rehydration). */
2455
+ function seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId) {
2456
+ if (!dataPath || typeof dataPath !== 'string') {
2457
+ return;
2458
+ }
2459
+ const storedLevels = readStoredHierarchyLevels(values, dataPath, widgetId);
2460
+ if (storedLevels.length === 0) {
2461
+ return;
2462
+ }
2463
+ if (builderMatchesStored(groupId, storedLevels)) {
2464
+ return;
2465
+ }
2466
+ geoHierarchyBuilder.clear(groupId);
2467
+ storedLevels.forEach((entry) => {
2468
+ geoHierarchyBuilder.addLevel(entry.level, String(entry.level_value_id), entry.level_value_mnemonic || String(entry.level_value_id), groupId);
2469
+ });
2470
+ }
2471
+ /** Force-clear builder for a group, then seed from Redux/schema values (e.g. after Cancel). */
2472
+ function resetAndSeedGeoHierarchyFromValues(values, dataPath, widgetId, groupId) {
2473
+ geoHierarchyBuilder.clear(groupId);
2474
+ seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId);
2475
+ }
2476
+
2477
+ // Define stable empty arrays to avoid selector reference issues
2478
+ const EMPTY_ERRORS = [];
2479
+ const EMPTY_DATA_SOURCE = [];
2480
+ const useBaseWidget = (options) => {
2481
+ const { config, dataSourceRequestHandler: propHandler, schemaData, onValueChange } = options;
2482
+ const dispatch = useDispatch();
2483
+ const context = useWidgetContext();
2484
+ const eventBus = useWidgetEventBus();
2485
+ const { translateConfig } = useWidgetTranslation();
2486
+ const widgetId = config['widget-id'];
2487
+ // Fall back to WidgetContext for dataSourceRequestHandler
2488
+ const dataSourceRequestHandler = propHandler || context.dataSourceRequestHandler;
2489
+ // Get state from Redux
2490
+ const values = useSelector((state) => state.widget.values);
2491
+ const errors = useSelector((state) => state.widget.errors[widgetId] ?? EMPTY_ERRORS);
2492
+ const touched = useSelector((state) => state.widget.touched[widgetId] || false);
2493
+ const loading = useSelector((state) => state.widget.loading[widgetId] || false);
2494
+ const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
2495
+ // Skip value handling for layout widgets (they don't store data values)
2496
+ // Infer layout from widget-type
2497
+ const isLayoutWidget = config['widget-type'] === 'layout';
2498
+ // Track if user has explicitly set a value to prevent default from overwriting
2499
+ const userHasSetValueRef = useRef(false);
2500
+ // Use ref for values to avoid stale closures in handleChange
2501
+ const valuesRef = useRef(values);
2502
+ const loadingRef = useRef(loading);
2503
+ const dataSourceOptionsRef = useRef(dataSourceOptions);
2504
+ useEffect(() => {
2505
+ valuesRef.current = values;
2506
+ loadingRef.current = loading;
2507
+ dataSourceOptionsRef.current = dataSourceOptions;
2508
+ }, [values, loading, dataSourceOptions]);
2509
+ // Track last dispatched value to prevent duplicate dispatches
2510
+ const lastDispatchedValueRef = useRef(null);
2511
+ // Helper to extract displayable value from object (especially geo hierarchy objects)
2512
+ const extractValueFromObject = useCallback((obj) => {
2513
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
2514
+ return obj;
2515
+ }
2516
+ // Check for geo hierarchy structure first
2517
+ const geoConfig = config['widget-geo-config'];
2518
+ if (geoConfig) {
2519
+ // If we have a geo hierarchy object, extract the value for this specific level
2520
+ const hierarchy = obj.hierarchy || obj.geo_code_hierarchy_json?.hierarchy;
2521
+ if (Array.isArray(hierarchy)) {
2522
+ const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2523
+ if (levelData) {
2524
+ return levelData.level_value_id;
2525
+ }
2526
+ }
2527
+ }
2528
+ if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj || 'hierarchy' in obj) {
2529
+ if ('geo_lowest_level_value_id' in obj) {
2530
+ return obj.geo_lowest_level_value_id;
2531
+ }
2532
+ if ('lowest_level_value_id' in obj) {
2533
+ return obj.lowest_level_value_id;
2534
+ }
2535
+ // Fallback for nested geo_code_hierarchy_json
2536
+ if (obj.geo_code_hierarchy_json?.lowest_level_value_id) {
2537
+ return obj.geo_code_hierarchy_json.lowest_level_value_id;
2538
+ }
2539
+ if (obj.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2540
+ return obj.geo_code_hierarchy_json.geo_lowest_level_value_id;
2541
+ }
2542
+ // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
2543
+ return undefined;
2544
+ }
2545
+ // Try common value fields
2546
+ if ('value' in obj) {
2547
+ return obj.value;
2548
+ }
2549
+ if ('id' in obj) {
2550
+ return obj.id;
2551
+ }
2552
+ if ('label' in obj) {
2553
+ return obj.label;
2554
+ }
2555
+ if ('name' in obj) {
2556
+ return obj.name;
2013
2557
  }
2014
2558
  // If no extractable value found, return undefined to avoid rendering object as React child
2015
2559
  // This prevents "Objects are not valid as a React child" errors
@@ -2020,6 +2564,17 @@ const useBaseWidget = (options) => {
2020
2564
  if (isLayoutWidget) {
2021
2565
  return undefined; // Layout widgets don't have values
2022
2566
  }
2567
+ const geoConfig = config['widget-geo-config'];
2568
+ if (geoConfig) {
2569
+ const value = resolveGeoWidgetLevelValue(values, widgetId, config['widget-data-path'], geoConfig);
2570
+ if (userHasSetValueRef.current) {
2571
+ return value;
2572
+ }
2573
+ if (value === null) {
2574
+ return null;
2575
+ }
2576
+ return value !== undefined ? value : config['widget-data-default'];
2577
+ }
2023
2578
  // Try to get value from widgetId first (this should have the actual selected value)
2024
2579
  // For geo widgets with dataPath, widgetId stores the actual ID, while dataPath stores the hierarchy object
2025
2580
  let value = values[widgetId];
@@ -2098,6 +2653,15 @@ const useBaseWidget = (options) => {
2098
2653
  }
2099
2654
  // eslint-disable-next-line react-hooks/exhaustive-deps
2100
2655
  }, [isLayoutWidget]); // Only run once on mount
2656
+ const resolveIsRequired = useCallback((currentValues) => {
2657
+ if (isLayoutWidget) {
2658
+ return false;
2659
+ }
2660
+ if (config['widget-readonly']) {
2661
+ return false;
2662
+ }
2663
+ return evaluateWidgetConditions(config['widget-data-options'], currentValues, config['widget-required'] ?? false).required;
2664
+ }, [config, isLayoutWidget]);
2101
2665
  // Handle value change
2102
2666
  // CRITICAL: Don't include 'values' in dependency array - it causes the callback to be recreated
2103
2667
  // every time values change, which can lead to stale closures and double dispatches
@@ -2114,13 +2678,16 @@ const useBaseWidget = (options) => {
2114
2678
  // This prevents data disappearance when switching to Edit mode and components
2115
2679
  // incorrectly clear values before options load or if handler is temporarily missing.
2116
2680
  if (newValue === '' || newValue === null || newValue === undefined) {
2117
- if (loadingRef.current) {
2118
- console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
2119
- return;
2120
- }
2121
- if (config['widget-data-source']?.type === 'api' && dataSourceOptionsRef.current.length === 0) {
2122
- console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because API options are empty`);
2123
- return;
2681
+ const allowEmptyClear = config.widget === 'register-lookup';
2682
+ if (!allowEmptyClear) {
2683
+ if (loadingRef.current) {
2684
+ console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
2685
+ return;
2686
+ }
2687
+ if (config['widget-data-source']?.type === 'api' && dataSourceOptionsRef.current.length === 0) {
2688
+ console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because API options are empty`);
2689
+ return;
2690
+ }
2124
2691
  }
2125
2692
  }
2126
2693
  // Mark that user has set a value (unless this is the default initialization)
@@ -2138,29 +2705,26 @@ const useBaseWidget = (options) => {
2138
2705
  lastDispatchedValueRef.current = newValue;
2139
2706
  dispatch(setValue({ widgetId, value: newValue }));
2140
2707
  }
2708
+ else if (config['widget-geo-config']) {
2709
+ // Geo widgets: hierarchy dataPath is managed by useGeoWidgetCascade
2710
+ getGeoDescendantWidgetIds(widgetId).forEach((descendantId) => {
2711
+ dispatch(setValue({ widgetId: descendantId, value: GEO_LEVEL_CLEARED }));
2712
+ dispatch(setDataSource({ widgetId: descendantId, data: [] }));
2713
+ });
2714
+ dispatch(setValue({ widgetId, value: newValue }));
2715
+ }
2141
2716
  else {
2142
- // Has dataPath: update both widgetId and dataPath
2143
- // CRITICAL: For geo widgets, we do NOT want to overwrite the shared hierarchy dataPath
2144
- // with a primitive value (the selected ID). The hierarchy object is managed by useGeoWidgetCascade.
2145
- if (config['widget-geo-config']) {
2146
- dispatch(setValue({ widgetId, value: newValue }));
2147
- return;
2148
- }
2149
- // For non-geo widgets, update both widgetId and dataPath
2150
- // CRITICAL: Create updated values object with newValue already set
2151
- // This prevents setWidgetValue from reading stale values
2717
+ // Non-geo widgets: update both widgetId and dataPath
2152
2718
  const currentValuesWithUpdate = {
2153
2719
  ...valuesRef.current,
2154
- [widgetId]: newValue, // Ensure widgetId has the new value
2720
+ [widgetId]: newValue,
2155
2721
  };
2156
2722
  const updatedValues = setWidgetValue(currentValuesWithUpdate, config['widget-data-path'], widgetId, newValue);
2157
- // setWidgetValue returns the complete updated structure with all existing data preserved
2158
- // Use setValues to update the entire state with deep merge
2159
2723
  dispatch(setValues(updatedValues));
2160
2724
  }
2161
2725
  // Validate if needed
2162
2726
  if (validate) {
2163
- const validationErrors = validateWidget(newValue, config['widget-data-validation'], config['widget-required']);
2727
+ const validationErrors = validateWidget(newValue, config['widget-data-validation'], resolveIsRequired(currentValues));
2164
2728
  dispatch(setError({ widgetId, errors: validationErrors }));
2165
2729
  }
2166
2730
  // Call custom onChange if provided
@@ -2179,13 +2743,12 @@ const useBaseWidget = (options) => {
2179
2743
  timestamp: Date.now(),
2180
2744
  });
2181
2745
  }
2182
- }, [config, widgetId, dispatch, onValueChange, eventBus] // Removed 'values' to prevent stale closures
2746
+ }, [config, widgetId, dispatch, onValueChange, eventBus, resolveIsRequired] // Removed 'values' to prevent stale closures
2183
2747
  );
2184
2748
  // Handle blur
2185
2749
  const handleBlur = useCallback(() => {
2186
2750
  dispatch(setTouched({ widgetId, touched: true }));
2187
- // Validate on blur
2188
- const validationErrors = validateWidget(currentValue, config['widget-data-validation'], config['widget-required']);
2751
+ const validationErrors = validateWidget(currentValue, config['widget-data-validation'], resolveIsRequired(valuesRef.current));
2189
2752
  dispatch(setError({ widgetId, errors: validationErrors }));
2190
2753
  // Publish widget:blur event
2191
2754
  if (eventBus) {
@@ -2196,7 +2759,7 @@ const useBaseWidget = (options) => {
2196
2759
  timestamp: Date.now(),
2197
2760
  });
2198
2761
  }
2199
- }, [currentValue, config, widgetId, dispatch, eventBus]);
2762
+ }, [currentValue, config, widgetId, dispatch, eventBus, resolveIsRequired]);
2200
2763
  // Get field value helper
2201
2764
  const getFieldValue = useCallback((path) => {
2202
2765
  return getWidgetValue(values, path, '');
@@ -2204,7 +2767,7 @@ const useBaseWidget = (options) => {
2204
2767
  // Conditional visibility and enablement
2205
2768
  const isVisible = useMemo(() => {
2206
2769
  // Layout widgets are always visible unless explicitly hidden
2207
- if (isLayoutWidget && !config['widget-data-options']?.condition) {
2770
+ if (isLayoutWidget && !hasVisibilityRules(config['widget-data-options'])) {
2208
2771
  return true;
2209
2772
  }
2210
2773
  return shouldShowWidget(config['widget-data-options'], values);
@@ -2219,6 +2782,7 @@ const useBaseWidget = (options) => {
2219
2782
  }
2220
2783
  return shouldEnableWidget(config['widget-data-options'], values);
2221
2784
  }, [config['widget-readonly'], config['widget-data-options'], values, isLayoutWidget]);
2785
+ const isRequired = useMemo(() => resolveIsRequired(values), [resolveIsRequired, values]);
2222
2786
  // Format value for display
2223
2787
  const formattedValue = useMemo(() => {
2224
2788
  if (!config['widget-data-format']) {
@@ -2229,6 +2793,14 @@ const useBaseWidget = (options) => {
2229
2793
  // Track readonly state explicitly to detect changes
2230
2794
  // Use JSON.stringify to create a stable reference for the dependency array
2231
2795
  const isReadonly = config['widget-readonly'] ?? false;
2796
+ // Leaving edit mode (Cancel): allow mirror/rehydration on next Edit
2797
+ useEffect(() => {
2798
+ if (config['widget-readonly']) {
2799
+ userHasSetValueRef.current = false;
2800
+ lastMirroredValueRef.current = null;
2801
+ lastDispatchedValueRef.current = null;
2802
+ }
2803
+ }, [config['widget-readonly']]);
2232
2804
  const dataSource = config['widget-data-source'];
2233
2805
  const geoConfig = config['widget-geo-config'];
2234
2806
  // Use ref to store handler to avoid stale closures
@@ -2241,6 +2813,8 @@ const useBaseWidget = (options) => {
2241
2813
  const apiService = dataSource?.type === 'api' ? dataSource.service : '';
2242
2814
  const apiEndpoint = dataSource?.type === 'api' ? dataSource.endpoint : '';
2243
2815
  const configKey = `${widgetId}-${isReadonly}-${dataSource?.type || 'none'}-${apiService}-${apiEndpoint}`;
2816
+ // Stable key so inline schemaData objects (e.g. dialog-table fields) don't retrigger loads every render
2817
+ const schemaDataKey = useMemo(() => (schemaData ? JSON.stringify(schemaData) : ''), [schemaData]);
2244
2818
  // Extract dependency value using a granular selector to prevent unnecessary re-renders
2245
2819
  // and infinite loops when other unrelated values in the state change.
2246
2820
  const dependencyValue = useSelector((state) => {
@@ -2257,10 +2831,9 @@ const useBaseWidget = (options) => {
2257
2831
  if (!dataSource) {
2258
2832
  return;
2259
2833
  }
2260
- // For API data sources, check if widget is readonly
2261
- // According to PRD: "Level 1 geo widgets load on widget mount or when entering edit mode"
2262
- // So we should only load API data sources when widget is NOT readonly
2263
- if (dataSource.type === 'api' && isReadonly) {
2834
+ const loadApiInReadonly = !!geoConfig ||
2835
+ ['select', 'radio', 'checkbox', 'multi-select'].includes(config.widget);
2836
+ if (dataSource.type === 'api' && isReadonly && !loadApiInReadonly) {
2264
2837
  return;
2265
2838
  }
2266
2839
  // For widgets with dependencies, check if dependency value exists
@@ -2302,6 +2875,30 @@ const useBaseWidget = (options) => {
2302
2875
  // React will call this effect again when the handler is ready
2303
2876
  return;
2304
2877
  }
2878
+ const resolveOptionKeys = () => {
2879
+ if (dataSource.type === 'static') {
2880
+ return { valueKey: undefined, labelKey: undefined };
2881
+ }
2882
+ if (geoConfig) {
2883
+ return {
2884
+ valueKey: dataSource.valueKey || 'level_value_id',
2885
+ labelKey: dataSource.labelKey || 'level_value_mnemonic',
2886
+ };
2887
+ }
2888
+ return { valueKey: dataSource.valueKey, labelKey: dataSource.labelKey };
2889
+ };
2890
+ if (dataSource.type === 'api') {
2891
+ const levelId = geoConfig?.level;
2892
+ const cached = getCachedApiDataSource(dataSource, valuesRef.current, levelId);
2893
+ if (cached) {
2894
+ const { valueKey, labelKey } = resolveOptionKeys();
2895
+ dispatch(setDataSource({
2896
+ widgetId,
2897
+ data: transformDataSourceOptions(cached, valueKey, labelKey),
2898
+ }));
2899
+ return;
2900
+ }
2901
+ }
2305
2902
  dispatch(setLoading({ widgetId, loading: true }));
2306
2903
  let data = [];
2307
2904
  if (dataSource.type === 'static') {
@@ -2314,31 +2911,13 @@ const useBaseWidget = (options) => {
2314
2911
  dispatch(setDataSource({ widgetId, data: [] }));
2315
2912
  return;
2316
2913
  }
2317
- // Extract level_id from widget-geo-config.level if available
2318
2914
  const levelId = geoConfig?.level;
2319
2915
  data = await getApiDataSource(dataSource, valuesRef.current, currentHandler, levelId);
2320
2916
  }
2321
2917
  else if (dataSource.type === 'schema') {
2322
2918
  data = getSchemaDataSource(dataSource, schemaData || {});
2323
2919
  }
2324
- // Transform to { value, label } format
2325
- // For geo widgets, default to level_value_id and level_value_mnemonic
2326
- let valueKey;
2327
- let labelKey;
2328
- if (dataSource.type === 'static') {
2329
- valueKey = undefined;
2330
- labelKey = undefined;
2331
- }
2332
- else if (geoConfig) {
2333
- // Geo widgets: default to level_value_id and level_value_mnemonic
2334
- valueKey = dataSource.valueKey || 'level_value_id';
2335
- labelKey = dataSource.labelKey || 'level_value_mnemonic';
2336
- }
2337
- else {
2338
- // Non-geo widgets: use specified keys or undefined
2339
- valueKey = dataSource.valueKey;
2340
- labelKey = dataSource.labelKey;
2341
- }
2920
+ const { valueKey, labelKey } = resolveOptionKeys();
2342
2921
  const transformed = transformDataSourceOptions(data, valueKey, labelKey);
2343
2922
  dispatch(setDataSource({ widgetId, data: transformed }));
2344
2923
  }
@@ -2353,16 +2932,25 @@ const useBaseWidget = (options) => {
2353
2932
  loadDataSource();
2354
2933
  // Use configKey and dependencyValue to ensure effect runs only when relevant state changes
2355
2934
  // eslint-disable-next-line react-hooks/exhaustive-deps
2356
- }, [configKey, dependencyValue, dataSourceRequestHandler, schemaData, widgetId, dispatch]);
2935
+ }, [configKey, dependencyValue, dataSourceRequestHandler, schemaDataKey, widgetId, dispatch]);
2936
+ const geoDisplayLabel = useMemo(() => {
2937
+ if (!geoConfig) {
2938
+ return undefined;
2939
+ }
2940
+ const rawLabel = resolveGeoWidgetLevelLabel(values, widgetId, config['widget-data-path'], geoConfig);
2941
+ return rawLabel ? translateConfig(rawLabel) : undefined;
2942
+ }, [values, widgetId, config, geoConfig, translateConfig]);
2357
2943
  return {
2358
2944
  widgetId,
2359
2945
  value: currentValue,
2946
+ geoDisplayLabel,
2360
2947
  formattedValue,
2361
2948
  error: errors,
2362
2949
  touched,
2363
2950
  loading,
2364
2951
  isVisible,
2365
2952
  isEnabled,
2953
+ isRequired,
2366
2954
  onChange: handleChange,
2367
2955
  onBlur: handleBlur,
2368
2956
  setError: (errors) => dispatch(setError({ widgetId, errors })),
@@ -2433,115 +3021,6 @@ const useWidgetCascade = (options) => {
2433
3021
  }, [cascadeConfig, eventBus, dataSource, widgetId, dispatch]);
2434
3022
  };
2435
3023
 
2436
- /**
2437
- * Geo Hierarchy Builder
2438
- * Manages geo hierarchy state and builds hierarchy JSON structure
2439
- */
2440
- class GeoHierarchyBuilder {
2441
- constructor() {
2442
- this.hierarchies = new Map();
2443
- }
2444
- /**
2445
- * Get or create hierarchy state for a group
2446
- */
2447
- getHierarchy(groupId = 'default') {
2448
- if (!this.hierarchies.has(groupId)) {
2449
- this.hierarchies.set(groupId, {
2450
- levels: new Map(),
2451
- order: [],
2452
- });
2453
- }
2454
- return this.hierarchies.get(groupId);
2455
- }
2456
- /**
2457
- * Add a level to the hierarchy
2458
- */
2459
- addLevel(level, level_value_id, level_value_mnemonic, groupId = 'default') {
2460
- const hierarchy = this.getHierarchy(groupId);
2461
- // If level already exists, remove it and everything after it
2462
- const existingIndex = hierarchy.order.indexOf(level);
2463
- if (existingIndex >= 0) {
2464
- // Remove this level and all subsequent levels
2465
- const levelsToRemove = hierarchy.order.slice(existingIndex);
2466
- levelsToRemove.forEach((l) => {
2467
- hierarchy.levels.delete(l);
2468
- hierarchy.order = hierarchy.order.filter((o) => o !== l);
2469
- });
2470
- }
2471
- // Add new level
2472
- hierarchy.levels.set(level, {
2473
- level,
2474
- level_value_id,
2475
- level_value_mnemonic,
2476
- });
2477
- hierarchy.order.push(level);
2478
- }
2479
- /**
2480
- * Remove a level and all levels below it
2481
- */
2482
- removeLevelAndBelow(level, groupId = 'default') {
2483
- const hierarchy = this.getHierarchy(groupId);
2484
- const index = hierarchy.order.indexOf(level);
2485
- if (index >= 0) {
2486
- // Remove this level and all subsequent levels
2487
- const levelsToRemove = hierarchy.order.slice(index);
2488
- levelsToRemove.forEach((l) => {
2489
- hierarchy.levels.delete(l);
2490
- hierarchy.order = hierarchy.order.filter((o) => o !== l);
2491
- });
2492
- }
2493
- }
2494
- /**
2495
- * Build hierarchy JSON structure
2496
- */
2497
- buildHierarchyJson(groupId = 'default') {
2498
- const hierarchy = this.getHierarchy(groupId);
2499
- if (hierarchy.order.length === 0) {
2500
- return null;
2501
- }
2502
- const hierarchyArray = hierarchy.order.map((level) => {
2503
- const data = hierarchy.levels.get(level);
2504
- return {
2505
- level: data.level,
2506
- level_value_id: data.level_value_id,
2507
- level_value_mnemonic: data.level_value_mnemonic,
2508
- };
2509
- });
2510
- const lowestLevel = hierarchy.order[hierarchy.order.length - 1];
2511
- const lowestLevelData = hierarchy.levels.get(lowestLevel);
2512
- return {
2513
- geo_lowest_level_value_id: lowestLevelData.level_value_id,
2514
- geo_code_hierarchy_json: {
2515
- hierarchy: hierarchyArray,
2516
- lowest_level_value_id: lowestLevelData.level_value_id,
2517
- },
2518
- };
2519
- }
2520
- /**
2521
- * Clear hierarchy for a group
2522
- */
2523
- clear(groupId = 'default') {
2524
- this.hierarchies.delete(groupId);
2525
- }
2526
- /**
2527
- * Clear all hierarchies
2528
- */
2529
- clearAll() {
2530
- this.hierarchies.clear();
2531
- }
2532
- /**
2533
- * Get current levels for a group
2534
- */
2535
- getLevels(groupId = 'default') {
2536
- const hierarchy = this.getHierarchy(groupId);
2537
- return hierarchy.order.map((level) => hierarchy.levels.get(level));
2538
- }
2539
- }
2540
- // Singleton instance
2541
- const geoHierarchyBuilder = new GeoHierarchyBuilder();
2542
-
2543
- // Define stable empty array to avoid selector reference issues
2544
- const EMPTY_DATA_SOURCE = [];
2545
3024
  /**
2546
3025
  * Hook for geo widget cascade functionality
2547
3026
  * Handles geo hierarchy building and cascade behavior
@@ -2559,236 +3038,151 @@ const useGeoWidgetCascade = (options) => {
2559
3038
  : 'default';
2560
3039
  const valuesRef = useRef(values);
2561
3040
  const handlerRef = useRef(dataSourceRequestHandler);
3041
+ const lastCascadePublishRef = useRef(undefined);
3042
+ const lastDirectParentValueRef = useRef(undefined);
2562
3043
  // Keep refs updated
2563
3044
  useEffect(() => {
2564
3045
  valuesRef.current = values;
2565
3046
  handlerRef.current = dataSourceRequestHandler;
2566
3047
  }, [values, dataSourceRequestHandler]);
2567
3048
  // Get current value and data source options
2568
- const currentValue = useSelector((state) => {
2569
- // Try to get value from widgetId first (most recent selection)
2570
- let value = state.widget.values[widgetId];
2571
- // If not found in widgetId, try dataPath
2572
- if (value === undefined && dataPath) {
2573
- value = getWidgetValue(state.widget.values, dataPath, widgetId);
2574
- }
2575
- // Extract value if it's a geo hierarchy object
2576
- if (value && typeof value === 'object' && !Array.isArray(value) && geoConfig) {
2577
- const hierarchy = value.hierarchy || value.geo_code_hierarchy_json?.hierarchy;
2578
- if (Array.isArray(hierarchy)) {
2579
- const levelData = hierarchy.find((l) => l.level === geoConfig.level);
2580
- if (levelData) {
2581
- return levelData.level_value_id;
2582
- }
2583
- }
2584
- // Extended fallbacks (matching useBaseWidget)
2585
- if ('geo_lowest_level_value_id' in value) {
2586
- return value.geo_lowest_level_value_id;
2587
- }
2588
- if ('lowest_level_value_id' in value) {
2589
- return value.lowest_level_value_id;
2590
- }
2591
- if (value.geo_code_hierarchy_json?.lowest_level_value_id) {
2592
- return value.geo_code_hierarchy_json.lowest_level_value_id;
2593
- }
2594
- if (value.geo_code_hierarchy_json?.geo_lowest_level_value_id) {
2595
- return value.geo_code_hierarchy_json.geo_lowest_level_value_id;
2596
- }
2597
- }
2598
- return value;
2599
- });
3049
+ const currentValue = useSelector((state) => geoConfig
3050
+ ? resolveGeoWidgetLevelValue(state.widget.values, widgetId, dataPath, geoConfig)
3051
+ : state.widget.values[widgetId]);
2600
3052
  // Memoize selector to avoid returning new array reference
2601
- const dataSourceOptions = useSelector((state) => state.widget.dataSources[widgetId] ?? EMPTY_DATA_SOURCE);
3053
+ const allDataSources = useSelector((state) => state.widget.dataSources);
3054
+ // Register parent chain for upstream-ancestor detection (grandparent → grandchild reset)
3055
+ useEffect(() => {
3056
+ if (!geoConfig || typeof dataPath !== 'string') {
3057
+ return;
3058
+ }
3059
+ registerGeoWidget(widgetId, geoConfig, dataPath);
3060
+ return () => unregisterGeoWidget(widgetId);
3061
+ }, [widgetId, geoConfig, dataPath]);
3062
+ // Keep in-memory builder in sync with persisted hierarchy (reload, cancel→re-edit, etc.)
3063
+ useEffect(() => {
3064
+ if (!geoConfig || typeof dataPath !== 'string') {
3065
+ return;
3066
+ }
3067
+ seedGeoHierarchyFromValues(values, dataPath, widgetId, groupId);
3068
+ }, [geoConfig, dataPath, widgetId, groupId, values]);
2602
3069
  useEffect(() => {
2603
3070
  if (!geoConfig || !eventBus || !dataSource || dataSource.type !== 'api') {
2604
3071
  return;
2605
3072
  }
2606
- const { level, isLastLevel, parentWidgetId } = geoConfig;
2607
- // Listen to parent widget changes
2608
- if (parentWidgetId) {
2609
- const handleParentChange = async (event) => {
2610
- if (event.widgetId !== parentWidgetId) {
2611
- return;
2612
- }
2613
- // CRITICAL: Use a small delay to ensure Redux state has been updated
2614
- // This prevents reading stale values from valuesRef
2615
- await new Promise(resolve => setTimeout(resolve, 0));
2616
- const currentValues = valuesRef.current;
2617
- const currentHandler = handlerRef.current;
2618
- // CRITICAL: Try to get parent value from event first, then from Redux
2619
- let parentValue = event.value;
2620
- if (parentValue === undefined || parentValue === null) {
2621
- parentValue = currentValues[parentWidgetId];
2622
- // If not found in top-level values, try to find it via dataPath or dependsOn
2623
- if (parentValue === undefined && dataSource.dependsOn) {
2624
- parentValue = getWidgetValue(currentValues, dataSource.dependsOn, '');
2625
- }
2626
- }
2627
- // Remove this level and all below from hierarchy
2628
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2629
- // Clear this widget's value
2630
- // CRITICAL: Only dispatch setValue for THIS widget, not for parent or other widgets
2631
- // setWidgetValue returns the entire updated state, but we only want to update this widget
2632
- if (dataPath) {
2633
- const updatedValues = setWidgetValue(currentValues, dataPath, widgetId, undefined);
2634
- // Only dispatch setValue for this widget's widgetId, not for parent or other widgets
2635
- // This prevents accidentally overwriting the parent widget's value
2636
- // The setWidgetValue function updates the nested structure, but we only want to
2637
- // update the top-level widgetId key, not other keys that might be in updatedValues
2638
- const newWidgetValue = updatedValues[widgetId];
2639
- if (newWidgetValue !== undefined) {
2640
- dispatch(setValue({ widgetId, value: newWidgetValue }));
2641
- }
2642
- else {
2643
- // If widgetId is not in updatedValues, the value was set in a nested path
2644
- // In this case, we need to use setValues to update the entire structure
2645
- // But we need to be careful not to overwrite the parent widget's value
2646
- // Only update keys that are related to this widget's dataPath
2647
- const dataPathStr = typeof dataPath === 'string' ? dataPath : '';
2648
- if (dataPathStr && !dataPathStr.startsWith(parentWidgetId + '.')) {
2649
- // Only update if dataPath doesn't start with parentWidgetId
2650
- // This ensures we don't accidentally overwrite the parent widget's value
2651
- dispatch(setValue({ widgetId, value: undefined }));
2652
- }
2653
- }
2654
- }
2655
- else {
2656
- dispatch(setValue({ widgetId, value: undefined }));
3073
+ const { level, isLastLevel, parentWidgetId: rawParentWidgetId } = geoConfig;
3074
+ const parentWidgetId = rawParentWidgetId || null;
3075
+ if (!parentWidgetId) {
3076
+ return;
3077
+ }
3078
+ const clearThisLevel = (baseValues) => {
3079
+ geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
3080
+ if (dataPath) {
3081
+ dispatch(setValues(applySharedGeoHierarchyToValues(baseValues, groupId, dataPath, widgetId)));
3082
+ }
3083
+ dispatch(setValue({ widgetId, value: GEO_LEVEL_CLEARED }));
3084
+ if (!isLastLevel) {
3085
+ eventBus.publish({
3086
+ type: 'widget:change',
3087
+ widgetId,
3088
+ value: GEO_LEVEL_CLEARED,
3089
+ timestamp: Date.now(),
3090
+ });
3091
+ }
3092
+ dispatch(setDataSource({ widgetId, data: [] }));
3093
+ };
3094
+ const handleParentChange = async (event) => {
3095
+ const isDirectParent = event.widgetId === parentWidgetId;
3096
+ const isAncestor = isUpstreamGeoAncestor(event.widgetId, widgetId, parentWidgetId);
3097
+ if (!isDirectParent && !isAncestor) {
3098
+ return;
3099
+ }
3100
+ await new Promise(resolve => setTimeout(resolve, 0));
3101
+ const currentValues = valuesRef.current;
3102
+ const currentHandler = handlerRef.current;
3103
+ // Grandparent (or higher) changed: clear this level; only immediate parent drives reload
3104
+ if (isAncestor && !isDirectParent) {
3105
+ clearThisLevel(currentValues);
3106
+ return;
3107
+ }
3108
+ const parentCleared = event.value === undefined ||
3109
+ event.value === null ||
3110
+ event.value === '' ||
3111
+ event.value === GEO_LEVEL_CLEARED;
3112
+ const isFirstParentEvent = lastDirectParentValueRef.current === undefined;
3113
+ const parentValueChanged = !isFirstParentEvent &&
3114
+ lastDirectParentValueRef.current !== event.value;
3115
+ lastDirectParentValueRef.current = event.value;
3116
+ if (!parentCleared && !parentValueChanged && !isFirstParentEvent) {
3117
+ return;
3118
+ }
3119
+ let parentValue = event.value;
3120
+ if (!parentCleared && (parentValue === undefined || parentValue === null)) {
3121
+ parentValue = currentValues[parentWidgetId];
3122
+ if (parentValue === undefined && dataSource.dependsOn) {
3123
+ parentValue = getWidgetValue(currentValues, dataSource.dependsOn, '');
2657
3124
  }
2658
- // Reload data source with new parent value
2659
- // CRITICAL: Use parentValue from Redux, not event.value
2660
- if (currentHandler && parentValue !== null && parentValue !== undefined) {
2661
- try {
2662
- // Merge the new parent value into current values for the API call
2663
- // This ensures getApiDataSource can find the dependency value
2664
- const updatedValues = {
2665
- ...currentValues,
2666
- [parentWidgetId]: parentValue, // Use Redux value, not event.value
2667
- };
2668
- // Extract level_id from widget-geo-config.level
2669
- const levelId = geoConfig.level;
2670
- const data = await getApiDataSource(dataSource, updatedValues, currentHandler, levelId);
2671
- // Transform to { value, label } format
2672
- const valueKey = dataSource.valueKey || 'level_value_id';
2673
- const labelKey = dataSource.labelKey || 'level_value_mnemonic';
2674
- const transformed = transformDataSourceOptions(data, valueKey, labelKey);
2675
- dispatch(setDataSource({ widgetId, data: transformed }));
2676
- }
2677
- catch (error) {
2678
- console.error('Error reloading geo data source:', error);
2679
- dispatch(setDataSource({ widgetId, data: [] }));
2680
- }
3125
+ }
3126
+ clearThisLevel(currentValues);
3127
+ if (currentHandler && parentValue !== null && parentValue !== undefined && parentValue !== '') {
3128
+ try {
3129
+ const updatedValues = {
3130
+ ...currentValues,
3131
+ [parentWidgetId]: parentValue,
3132
+ };
3133
+ const levelId = geoConfig.level;
3134
+ const data = await getApiDataSource(dataSource, updatedValues, currentHandler, levelId);
3135
+ const valueKey = dataSource.valueKey || 'level_value_id';
3136
+ const labelKey = dataSource.labelKey || 'level_value_mnemonic';
3137
+ const transformed = transformDataSourceOptions(data, valueKey, labelKey);
3138
+ dispatch(setDataSource({ widgetId, data: transformed }));
2681
3139
  }
2682
- else {
2683
- // If parent value is cleared, clear the data source and hierarchy
2684
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
3140
+ catch (error) {
3141
+ console.error('Error reloading geo data source:', error);
2685
3142
  dispatch(setDataSource({ widgetId, data: [] }));
2686
3143
  }
2687
- };
2688
- const unsubscribe = eventBus.subscribe('widget:change', handleParentChange);
2689
- return () => {
2690
- unsubscribe();
2691
- };
2692
- }
2693
- }, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch]);
3144
+ }
3145
+ };
3146
+ const unsubscribe = eventBus.subscribe('widget:change', handleParentChange);
3147
+ return () => {
3148
+ unsubscribe();
3149
+ };
3150
+ }, [geoConfig, eventBus, dataSource, widgetId, dataPath, dispatch, groupId]);
2694
3151
  // Handle value changes to build hierarchy
2695
3152
  useEffect(() => {
2696
- if (!geoConfig) {
3153
+ if (!geoConfig || typeof dataPath !== 'string') {
2697
3154
  return;
2698
3155
  }
2699
- // Skip if value is undefined (it might still be loading or rehydrating)
3156
+ const { level, isLastLevel } = geoConfig;
3157
+ const groupRegistrations = getGeoWidgetRegistrationsInGroup(groupId);
3158
+ const applyGroupRebuild = () => {
3159
+ const resolveMnemonic = createGeoLevelMnemonicResolver(valuesRef.current, allDataSources);
3160
+ rebuildGeoHierarchyFromRegistrations(groupId, valuesRef.current, groupRegistrations, resolveMnemonic);
3161
+ dispatch(setValues(applySharedGeoHierarchyToValues(valuesRef.current, groupId, dataPath, widgetId)));
3162
+ };
2700
3163
  // ONLY clear hierarchy if the value is explicitly null or empty string (user action)
2701
3164
  if (currentValue === null || currentValue === '') {
2702
- const { level } = geoConfig;
2703
3165
  geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2704
- // If we have a dataPath, we need to update Redux with the cleared hierarchy
2705
- if (dataPath) {
2706
- const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2707
- let finalUpdatedValues = valuesRef.current;
2708
- // Use logic similar to the build section below to update the dataPath
2709
- if (typeof dataPath === 'string' && dataPath.endsWith('.geo_code_hierarchy_json')) {
2710
- const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2711
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson?.geo_code_hierarchy_json);
2712
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, hierarchyJson?.geo_lowest_level_value_id);
2713
- }
2714
- else {
2715
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson?.geo_code_hierarchy_json);
2716
- }
2717
- dispatch(setValues(finalUpdatedValues));
3166
+ applyGroupRebuild();
3167
+ if (!isLastLevel && eventBus && lastCascadePublishRef.current !== GEO_LEVEL_CLEARED) {
3168
+ lastCascadePublishRef.current = GEO_LEVEL_CLEARED;
3169
+ eventBus.publish({
3170
+ type: 'widget:change',
3171
+ widgetId,
3172
+ value: GEO_LEVEL_CLEARED,
3173
+ timestamp: Date.now(),
3174
+ });
2718
3175
  }
2719
3176
  return;
2720
3177
  }
2721
3178
  if (currentValue === undefined) {
2722
- return; // Skip if undefined (still initializing)
2723
- }
2724
- const { level, isLastLevel } = geoConfig;
2725
- // Check if hierarchy is already built to prevent endless loops
2726
- if (dataPath) {
2727
- const currentHierarchy = getWidgetValue(valuesRef.current, dataPath, widgetId);
2728
- // If hierarchy JSON is already set and matches current value, skip rebuilding
2729
- if (currentHierarchy && typeof currentHierarchy === 'object') {
2730
- // Check if this specific level's value matches the hierarchy
2731
- const hierarchyArray = currentHierarchy.hierarchy || currentHierarchy.geo_code_hierarchy_json?.hierarchy;
2732
- if (Array.isArray(hierarchyArray)) {
2733
- const currentLevelValue = typeof currentValue === 'object'
2734
- ? (currentValue.level_value_id || currentValue.id || currentValue.value)
2735
- : currentValue;
2736
- const levelData = hierarchyArray.find((l) => l.level === geoConfig.level);
2737
- // If this level is already correctly represented in the hierarchy, skip rebuilding
2738
- // String conversion ensures comparison works for mixed types
2739
- if (levelData && String(levelData.level_value_id) === String(currentLevelValue)) {
2740
- return;
2741
- }
2742
- }
2743
- }
2744
- }
2745
- // Extract level_value_id and level_value_mnemonic from current value
2746
- // The value could be the ID itself or an object with id/name
2747
- let level_value_id;
2748
- let level_value_mnemonic;
2749
- if (typeof currentValue === 'string' || typeof currentValue === 'number') {
2750
- // Value is just the ID, need to find mnemonic from data source
2751
- level_value_id = String(currentValue);
2752
- // Try to get mnemonic from data source options
2753
- const option = dataSourceOptions.find((opt) => opt.value === currentValue);
2754
- level_value_mnemonic = option?.label || String(currentValue);
2755
- }
2756
- else if (currentValue && typeof currentValue === 'object') {
2757
- level_value_id = currentValue.level_value_id || currentValue.id || currentValue.value;
2758
- level_value_mnemonic = currentValue.level_value_mnemonic || currentValue.name || currentValue.label;
2759
- }
2760
- else {
2761
- return;
2762
- }
2763
- // When a widget's own value changes, remove this level and all below from hierarchy first
2764
- geoHierarchyBuilder.removeLevelAndBelow(level, groupId);
2765
- // Add level to hierarchy
2766
- geoHierarchyBuilder.addLevel(level, level_value_id, level_value_mnemonic, groupId);
2767
- // Build and store hierarchy JSON on every change
2768
- if (dataPath) {
2769
- const hierarchyJson = geoHierarchyBuilder.buildHierarchyJson(groupId);
2770
- if (hierarchyJson) {
2771
- // Fix: Avoid double nesting of geo_code_hierarchy_json
2772
- // If dataPath ends with .geo_code_hierarchy_json, we want to save the content directly to it
2773
- // and save the lowest level ID as a sibling
2774
- let finalUpdatedValues = valuesRef.current;
2775
- if (typeof dataPath === 'string' && dataPath.endsWith('.geo_code_hierarchy_json')) {
2776
- const prefix = dataPath.substring(0, dataPath.lastIndexOf('.'));
2777
- // Save hierarchy JSON content directly to dataPath (avoiding double nesting)
2778
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson.geo_code_hierarchy_json);
2779
- // Save lowest level ID as sibling
2780
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, `${prefix}.geo_lowest_level_value_id`, widgetId, hierarchyJson.geo_lowest_level_value_id);
2781
- }
2782
- else {
2783
- // Fallback if path doesn't follow the naming convention
2784
- finalUpdatedValues = setWidgetValue(finalUpdatedValues, dataPath, widgetId, hierarchyJson.geo_code_hierarchy_json);
2785
- }
2786
- // CRITICAL: Use setValues for deep merge instead of replacing root keys with setValue
2787
- // setWidgetValue returns the complete updated state object with all keys preserved
2788
- dispatch(setValues(finalUpdatedValues));
3179
+ const hasOwnValue = Object.prototype.hasOwnProperty.call(valuesRef.current, widgetId);
3180
+ if (!hasOwnValue) {
3181
+ return;
2789
3182
  }
2790
3183
  }
2791
- }, [geoConfig, currentValue, widgetId, dataPath, dispatch, dataSourceOptions]);
3184
+ applyGroupRebuild();
3185
+ }, [geoConfig, currentValue, widgetId, dataPath, dispatch, allDataSources, groupId, eventBus]);
2792
3186
  };
2793
3187
 
2794
3188
  class WidgetRegistry {
@@ -2913,98 +3307,6 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
2913
3307
  return (jsxRuntimeExports.jsx("div", { className: "widget-container", "data-widget-id": widgetContext.widgetId, style: { marginBottom: 0 }, children: widgetRegistry.render(config, widgetContext, defaultComponent) }));
2914
3308
  };
2915
3309
 
2916
- /**
2917
- * Custom hook for widget translations
2918
- * Provides translation function with widget-specific namespace and fallback support
2919
- */
2920
- const useWidgetTranslation = () => {
2921
- const { translate: translateFunction } = useWidgetContext();
2922
- /**
2923
- * Translate a key with flexible namespace support
2924
- * Supports translation keys in various formats and direct strings
2925
- *
2926
- * Translation key formats supported:
2927
- * - "widgets:common.addItem" - Namespaced key (for widget-specific translations)
2928
- * - "Name" - Direct string (will be looked up in flat translation structure)
2929
- * - "sections.personalDetails" - Nested key (for backward compatibility)
2930
- *
2931
- * With flat translation structure, direct strings like "Name" are automatically
2932
- * translated by looking them up in the translation resources.
2933
- *
2934
- * @param keyOrString - Translation key (e.g., "widgets:common.addItem") or direct string (e.g., "Name")
2935
- * @param options - Translation options (interpolation values, default value, etc.)
2936
- * @returns Translated string or original string if translation not found
2937
- */
2938
- const translate = (keyOrString, options) => {
2939
- if (!keyOrString) {
2940
- return options?.defaultValue || '';
2941
- }
2942
- // Use the provided translation function or fallback to the key
2943
- if (translateFunction) {
2944
- return translateFunction(keyOrString, options) || options?.defaultValue || keyOrString;
2945
- }
2946
- // Fallback to key if no translation function available
2947
- return options?.defaultValue || keyOrString;
2948
- };
2949
- /**
2950
- * Translate widget config property
2951
- * Attempts to translate the value, but if translation is not found,
2952
- * returns the original value as-is (graceful fallback)
2953
- *
2954
- * This function will:
2955
- * - Try to translate any string value
2956
- * - If translation exists, use the translated value
2957
- * - If translation doesn't exist (returns same value or throws), use original value
2958
- * - This prevents errors when literal strings like "XXXX-XXXX-XXXX" are used
2959
- */
2960
- const translateConfig = (value, fallback) => {
2961
- if (!value) {
2962
- return fallback || '';
2963
- }
2964
- // Try to translate the value
2965
- if (translateFunction) {
2966
- try {
2967
- // Pass defaultValue to ensure we get the original value if translation fails
2968
- const translated = translateFunction(value, { defaultValue: value });
2969
- // If translation returns empty, null, undefined, or the exact same value,
2970
- // it means no translation was found - return the original value
2971
- if (!translated || translated === value) {
2972
- return value;
2973
- }
2974
- // Translation found, return it
2975
- return translated;
2976
- }
2977
- catch (error) {
2978
- // If translation throws an error (e.g., missing key warning), return original value
2979
- return value;
2980
- }
2981
- }
2982
- // No translation function available, return value as-is
2983
- return value;
2984
- };
2985
- // No need of this getLanguage and changeLanguage functions
2986
- /**
2987
- * Get current language
2988
- */
2989
- // const getLanguage = (): string => {
2990
- // return i18n.language || 'en';
2991
- // };
2992
- /**
2993
- * Change language
2994
- */
2995
- // const changeLanguage = (lng: string): Promise<void> => {
2996
- // return i18n.changeLanguage(lng).then(() => undefined);
2997
- // };
2998
- return {
2999
- t: translate,
3000
- translate,
3001
- translateConfig,
3002
- // getLanguage,
3003
- // changeLanguage,
3004
- // i18n: null,
3005
- };
3006
- };
3007
-
3008
3310
  /**
3009
3311
  * Renders a panel with its nested panels or widgets
3010
3312
  *
@@ -3123,6 +3425,16 @@ const PanelRenderer = ({ panel, dataSourceRequestHandler, schemaData, onValueCha
3123
3425
  return (jsxRuntimeExports.jsx("div", { className: `panel panel-${orientation}`, "data-panel-id": panel['panel-id'], style: orientation === 'horizontal' ? { width: '100%' } : {}, children: content }));
3124
3426
  };
3125
3427
 
3428
+ /**
3429
+ * Field label: long text truncates with ellipsis; required asterisk always stays visible.
3430
+ */
3431
+ const WidgetFieldLabel = ({ label, required = false, className = '', title, }) => {
3432
+ const { translateConfig } = useWidgetTranslation();
3433
+ const translatedLabel = translateConfig(label);
3434
+ const tooltip = title !== undefined ? translateConfig(title) : translatedLabel;
3435
+ return (jsxRuntimeExports.jsxs("label", { className: `flex items-baseline min-w-0 max-w-full ${className}`, style: { fontFamily: 'Roboto, sans-serif' }, title: tooltip, children: [jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: translatedLabel }), required && jsxRuntimeExports.jsx("span", { className: "ml-1 shrink-0 text-red-500", children: "*" })] }));
3436
+ };
3437
+
3126
3438
  /**
3127
3439
  * Utility functions for file preview functionality
3128
3440
  */
@@ -3166,7 +3478,11 @@ const canPreviewInWeb = (file) => {
3166
3478
  return previewableExtensions.includes(extension.toLowerCase());
3167
3479
  };
3168
3480
 
3169
- var img$f = "data:image/png;base64,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";
3481
+ var img$h = "data:image/png;base64,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";
3482
+
3483
+ var img$g = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAgKADAAQAAAABAAAAgAAAAABIjgR3AAAM10lEQVR4Ae2de6wdVRXGAXlY21KgLRTatGmFVqCNiqAtD7G0YEKLFSIKVgSsIYFE+UPFV6T1TUiQGCCIaIxggmBqTWsgyqPVGpBHAbGRtkpBCqVPKwhSqlZ/X3Nv7ul45p691+yZ2fucWcmXc2bu2muv9e09+z3n7rtPejIOl6eAyX2fR/M5CgwFw8AIMBz8G7wGXgav9H3fwudasA6s6fv+Nz57VvaNPPL98G8aOKMPp/GpAg4pL2JseR8e4PM50EiNDBxA3ueAO8BW8N+K8Qz53QjeAxqpkIHjyesasAlUXeh5+ambWAQmgUZKYEBN/LngUZBXCDHc/w/+LQEngUYCMKCCVzO/CsRQwD4+/K7Pdz4asTAwh0QahfuQHqOuBozqthpxZGAsereBGAvT6tO/iOe7YDhoJIeB/bl/FXgVWImOPd3zxHYeaCTDgBZtVoLYCzCUf2rhhmY46NnL2UQe05QuVCF3svMn4p7Ws6VO4GryrwW7QSeyuvXv6u4uAj0nBxHxz0C3FqxvXFrYilpC7gUcSqRLwakVR/wS+WmDR92NNn36N37exHdtDqlPlm/jgTaQDgFVys1k9imgxaSulaOI7Cng+4T46u8gj1+AK4FW5Q4GvnI4CWaCq8FysBP4+uGrr1ZRrWNXyhii+gvwJcVVfyu2bwAzgJ7q0DIEg3PBneB14OqXr97d2NZGV1eJtmafAL5kuOjfj9154EBQlah7uAxoJO/io6+Opokhu13M1ScqmF8DXxI66d+LTT3tdcp+ZH4OeAR08tf379pqTl5E0GLgG/xg+quwF9sevJ7WC8HGwLF+FntJy1fwfrDC9Pnb37GlQV0Z/XsokjWT0JROa/8+seXpakZwFkhSTsdrnbfLC87n/sPYmZgQC5p5rA8U+2bsaPaUlIzGW52h8ynkdrpaJdQuWoqj4pH4vSwAB+JlBYi55cO9AVF/+CvQrkB97mmqde6A2SS/iYuvAZ+483QXpcLAggAB78DGe1MJ2MHPK9BRf55XuC73Na54u0NetaocRu5akHEJKE9HfV70gRpY/jBpig4Of4sNtSrRyi14llewLvdfJv0J0UZX3LH5mNC4xoWLPJ2Li7tRjgWNfIs0c2+Q/sxyXIvK6ufwJq9wXe5vI70GmNHJSjxyCSBP54LoIirPIa3y5fHgcj+67ePTCwZ0U3lcR2lZ09qHCnCmrWxtXUcjRdb6tT08JJpIqnNkAlltBy5PfDudhdW5OnhO6vvbOehyT3vsUwY339V//WgB7lR5hsfAzuICQXw1hgBq9uG+Avx9pmbf9xmNA7uMAehwyJvrDiCC/I/BB+tpI51HqFU+Te4uTX07nQ/U6nlcmet0dDuOXO6dWGcojxkdf5J0Ua9oVUyq5vX/MHKpzbJa5Dhydamh7XQ+VIvHcWd6vZHPLaSrZbfUusv1NA7rpFAjezMwlkvrWOD9e5sKf9WuwGYZs7mVdFoPb2RvBl7k8pd733K+OsNZM5CiXqSwjP61GzYmkA/daGYeQbXrMjvde7RqMs42Omqt4VXHV1d+6svVp3cq8OzfdfSu1KXhbBcw08jQXcZ0vZJMLeQSQ7A6LvY+QzrnJNkKcLJzyr0V79/7srlqw4BWBi1iLRNLXqZNjDWmnHovkdYELOcqlpVJVWsLoOVfHf3ylQd8E/SovjZ5VhtiL3VTrbUCWDPSqmEjbgxYuJqIab2GV4qEqABrS/GsO41auNIvrkwqi47WCmDNZF1ZznWhXet46a1lcdFaAUYYMtFPreu4eCNuDFgfFkvZOHnUWgEsp1C2OeXSKPUzYH1YLGXTn+egn0UrgLY6G3FnwMpXUwHcOY5acxfeCb5SSQUY6usV+q8Z0vR6EksrUEkF0Bs8vlLa/NTXkYT0LeclLWXjREnrGCCqmunkfXpK4vstBrdLa2lbK8CrBsdKa5oMvqSQRHxZzkxWUgH0WpKvlDY/9XUkEX0rX5VUAEsXcBTE9+LrX9b6Zl3R04JbKdLaBWw05KD0RxvS9WoS64bb+rIIa60A1nVqa1BlxRSzXStXlVQAy06VyJ4WM+OR+TbV4M8O0ui3FEsXnT/bCbIHEztd/6Z0z7ojA62ZaDDXic/s30s9GdzaBei4kl7s9JXpJLCsIvrmk7r+DAKwrAFYDpE4c9VaAZRolXPKAUXV7FMGLptvOQxYT1z/PsdekNvZCrDCaLV5I7gzcR/srNJW48G2d0u6OR672T7I5Xo76Q4qyaduMHu8kdetpLOsHDpzlm0BniflM86pBxR1mvjsgcvmW4aBSzLXrpc6ca0HsFL5Prm5PPVZnaWVeplOZhojbTRyemEdYc41OrubdJZ5bh0xVpnnJ418akp+cJWO9uelY8gvgewT7nJ9e7+R5nMPA1pbWQdcuMvq6J9M1SbXk3PWIZdrvQTZ7A0MFNvHjDyK60sHzFT/7YQCjv+8enejzFGLPlrDd3lwsjpa/rUsGgUl4imj8wpmTlBP0jT27QL8XRdDyJcUCOA50vby8vBk4rfsq+jh0WBa6WsX/arFsyDbPLle31h7BPU4oGnfwwV4i+rXVi4vEIgqSi3zWPKtU6wDaPGlp//ddTqfzVtHmPUrV65PfVZP+9jWY1BZX1K41nq/CjHLg+v14hiDvKJAQApcv3s7MsbAAvukmdMrwLWws3r6QSjtGUQnWszQnnTWYZ/rh0jfzYNCtXKbCnL0I9JHK+qXLL9v01pJtLLVjTuGOhmtgzStsfp+30r6USBq+R7e+QaW1V+OjRFRR+nn3CTU/wyycfpez/fLth7tw8jWuqvVSsjj2DminhCC5noS1raA1tgs3+8J6lXJxnSsSYMVS6CtaZ7FRlTTHU/eLkJfr9K1xmT5roM04z3zrl19YYDARZY2jj4PSj3xgv2QommxfsffUtjZNJouzgvpXFW2dIroPpANyHqtla8JVTlfIB+d6l0NrHFm032zgC+1Jx2DBxsCkqHm9AtAy6ixySgc+gEossCTLfzl2NP0OmnRooX6sGxwRa7XYE8j4hjIGY4f6qK2gSIxtUurh2ciSF5OJgLL2y7tSGm9p6nVAlDHuoFWLReC0JW7NT59Xw/GgeRlDhFoQJcNMMS1DkbcBmaDMgeLanGUh/Iqo0LncaHjYkeC5OV8IngD5AUa4r6emFvAR8DhoKjo6Npl4KegjGbeNeY/kr/GGbVIyKdqFhEsAeo7yxaRq/cX1gKNG/QkaZFKP3KhAaVajv2BfDkEDAMTwNvAZHAs0BJuLKLFMfGn3dOk5V14vxm41v5Gb4ArbZhV8fCUXsGOIYenm0pgeghWwFvth0HxobAMwcKtoHnC/Tm4F9604tgV8nGiUJ/cVAQ/Du6BszqmwKVUuqlYXdUDleAOYpwOXggU653Y0fS0K0T7B2oN6pxuldUKafZxXkspaQykeyHy+zF2xF3XyBgiuR3sBiEIqtOGFoy+Bdq9wDmN+6Eq+83YCjldx1z9oqZyGUixIug8hDaHxoLBRFNizetDVNLvDJZRyn97B87fBYqeNwxBcicbeuJvAmriXUV7JVqc6mTb5e9Jbx93ImwKCteBUH2nC6GuOuvx60tgJLDITBL9E7jmN5jely0OpJRGA55Tgdb8Qz05gxGa97fQm1BnEs/rIC8/n/tXYacnRCti2qFbBLQ4sgv4EOWjq379MXANUJ4HgtAyD4MhYtC46fJQzqU0utSmzingOKAuox+j+e4qqhSbwAawGjzRhz/wqQWrsuUCMvgJKDq/VyX4BNA0sZCkVAHyAtU0bBTQ57A+9G+qqNndCfTKlqZlLwA9hXXKpWT+Q1CUew2c5wMtGDWSGAML8DfEFFhd1/mJxd6428fAlXz6jFPydHUQR6eyGkmQgavxOa9gfe5rmjkzwfgbl2FAy8k+hZ2nq4Wq0xpG02Tg2kCVQEvPJ6ZJQW97rRmBNn3ynm6f+1rEemdv05lm9KoE2mTyKew83c3YOTZNGnrbay0Q6WBJXsH63Neax6TepjPN6A/A7aXAp7DzdP+KnQlp0tDbXmsv4m6QV7A+99dh58jepjPN6LUZthz4FHae7hrsHJEmDb3t9VDCXwnyCtbn/pPY0c/8NJIYA4fi7+PAp7DzdB/EzpDE4m/chQFteWv7Oq9gfe5rqtlIggxoIKcBnU9ht9PVNvLUBONvXIaBcWA9aFewPve+0bCZLgNa3NEij0+BZ3U1xdwjOoTZSFoMqAWYDbTcaxXNLvZIUwH6mUjrU/P6s8B2o9sbjOmaZJExoK1fbQFnm/hO1xdHFkfjTgEGppNWh147FXr/3zV+0CpjI13EgI6D6VhYfyHnfepE9KwuirsJpYWBGXzfCPIKXwdF5rboN1+7kAG9D/FF8AhQt6Bzgnr55etA7078n/wP+xBESorPFeEAAAAASUVORK5CYII=";
3484
+
3485
+ var img$f = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAACAKADAAQAAAABAAACAAAAAAAL+LWFAAAkvUlEQVR4Ae3d344sVb0H8I1GuMFofAD5p95443ucGwHJifgg8g7wDpoYJecCvOGEIHAFr2ACMeobqES4OCGBsxZ7L2emd/f8urtqVa0/n04qvWemu2rVp35rfX/Tu2fmwQM3AgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBA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AfCGPyVNIBYYqQnIa5cbAQILBF5Kz+29AfCd/4IC8NTpBEZpAl6c7so5YQIrC+T3aXyStl6bAN/5r1wQdjeFQO9NQF6zvMdsilJ1krUFcifdYwPgO//alWH/Iwv03AT8fOQL49wIbC3wbjpgT02A8N+6QhxvRIEem4D3R7wQzonAngLPp4Pnvw/QQxPgZf89K8WxRxPoqQnIa1Req9wIEFhZ4JW0v9YbAN/5r3zR7Y5AEuilCXjV1SJAoJ7A62nXrTYBvvOvd93tmUDrTUBem9wIEKgo8ETa92/S1loT4Dv/ihfdrgk8Emi1Cfh9Gp93/StTAhsI5EXgd2lrpQnIY8ljciNAoL6A+V/f2BEINC2QXwl4I217NwH5Zb88FjcCBLYTMP+3s3YkAs0K5N8U+I+0bd0IfJaO+ctmVQyMwBwC5v8c19lZEjgp8EL6Sv7Z262agPxXCv2oz8nL4QsENhUw/zfldjACbQrk7wY+TVutRiD/ek+/37vNa29UBMx/NUBgcoH8TtyX0/Zx2tb4U8J5Hx+lLS8u3uWbENwINCxg/jd8cQyNwJYCz6aDvZa2D9P2RdrOfWUgP/aDtP06bc+kzY0Agf4Enk1DNv/7u26bjdi7tzej3v1AT6YR/DRtP0rbc2n7QdqeTlu+fZ62/EbCv6ftL2n7c9q+TJsbAQJjCJj/Y1xHZ0GAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgssD/A1hw9f23AMmGAAAAAElFTkSuQmCC";
3170
3486
 
3171
3487
  var img$e = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAkCAYAAACe0YppAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAHqADAAQAAAABAAAAJAAAAAAh0JciAAACrUlEQVRYCcWXPW8TQRCG31kbpNBChGiiRJQ0tFDggPgBtKGhJgEJUGxDFCmJUBKwDUoV2jRxS2NEhZCgAPELKBAhIPFRpKIwUXIe3vFHFJzbs2zWupHWu7e7M8/OenbuVkDReUzoLjbZvGDP/ysiWJQSlpLsiN7HOBp4rco6oBC+Svicz2QWERYULSjh605Q3dvHnk/B138sg1EV1DrjtPVAC9j1eS6NPD5x8lko3rgKch3FfmvbOY2w1a3n89xxlWdsMuvP3UqDPhO2xPLF9NueL3Tbct0dIZ4jJdTh8iH4Irf9H/hQwLZ4edSCs7ltz/Tc4CvWNhka2IwbXDKYZLMDt4Brep61CUFE8Jt26iwj9KbcKGDW7GrDfnGi+csf8zwqoh7MY1nFjhLYBpziKTl3UIDRDthqp5gJ5zENZsrMCXlscQE3DoMO2sJ8wURFr08GBRtAythgZeWIRAVsSDtZBdvqI5QeHamBE7c6yqPKJND/G0sZvIqXmQpmfI57wVrEGI/CFINhIOGCp3lml/mS+B5nwAuWx/iqs5hnDr8ap9ijT7noF+5JPNR0vWAblAqWWVkJLqkFV2rgxK1mVG8ySC4Oss/NqC5j2qfrBdf5RSERrg8c1cBNvYOHsoYfcXAveISvNHpcZIrLxSkm9rXO8SvngZquF2yDTPolVlaCS2rBlRo4cav56fscgvNJ+8wYqDEt3k6aEzfmBVtU82P/Wq+oZiq/pfdQkqf4Fgfw9XnBFtXM1XeZq73nmGPK68871yfUFuMF2yBz9RorK8ElteDiHY2blYI4/ks7xuUCxofNJ2OizfiZZVTW2GGfKJNMkc9cFlUu5k/QRQiOYx9TfHFcMrtkvhWdw2l2fmDnWFCY39i2XWucrOAXb1A5ruKjf26wkfeEXrE71V+iV9IhdJQEvwAAAABJRU5ErkJggg==";
3172
3488
 
@@ -3468,7 +3784,7 @@ const deserializeValue = (value) => {
3468
3784
  };
3469
3785
 
3470
3786
  const FileInputWidget = ({ config }) => {
3471
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3787
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
3472
3788
  const { translate, translateConfig } = useWidgetTranslation();
3473
3789
  const accept = widgetConfig['widget-data-options']?.accept;
3474
3790
  const multiple = widgetConfig['widget-data-options']?.multiple || false;
@@ -3708,7 +4024,7 @@ const FileInputWidget = ({ config }) => {
3708
4024
  setPreviewFile(null);
3709
4025
  } })] }));
3710
4026
  }
3711
- 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
4027
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center gap-2 sm:space-x-4", children: [jsxRuntimeExports.jsxs("label", { className: `cursor-pointer inline-flex items-center justify-between gap-2 border border-gray-300 shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${!isEnabled
3712
4028
  ? 'opacity-50 cursor-not-allowed'
3713
4029
  : ''}`, style: {
3714
4030
  width: '100%',
@@ -3769,6 +4085,13 @@ const namespaceWidgetConfig = (widgetConfig, namespace) => {
3769
4085
  if (namespaced['widget-data-path']) {
3770
4086
  namespaced['widget-data-path'] = namespaceDataPath(namespaced['widget-data-path'], namespace);
3771
4087
  }
4088
+ // Namespace geo parent references so cascade events match namespaced widget-id
4089
+ if (namespaced['widget-geo-config']?.parentWidgetId) {
4090
+ namespaced['widget-geo-config'] = {
4091
+ ...namespaced['widget-geo-config'],
4092
+ parentWidgetId: `${namespace}__${namespaced['widget-geo-config'].parentWidgetId}`,
4093
+ };
4094
+ }
3772
4095
  // Recursively namespace nested widgets (for layout widgets)
3773
4096
  if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
3774
4097
  namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
@@ -3973,6 +4296,9 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3973
4296
  const isVisible = shouldShowWidget(widget['widget-data-options'], currentSchemaData);
3974
4297
  if (!isVisible)
3975
4298
  continue;
4299
+ const isEnabled = shouldEnableWidget(widget['widget-data-options'], currentSchemaData);
4300
+ if (!isEnabled)
4301
+ continue;
3976
4302
  const widgetId = widget['widget-id'];
3977
4303
  if (isTableLikeWidget(widget)) {
3978
4304
  const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
@@ -3982,7 +4308,8 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3982
4308
  continue;
3983
4309
  }
3984
4310
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3985
- const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
4311
+ const isRequired = shouldRequireWidget(widget['widget-data-options'], currentSchemaData, widget['widget-required'] ?? false);
4312
+ const errors = validateWidget(value, widget['widget-data-validation'], isRequired, skipRequired);
3986
4313
  if (errors.length > 0) {
3987
4314
  isValid = false;
3988
4315
  dispatch(setTouched({ widgetId, touched: true }));
@@ -4015,6 +4342,108 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
4015
4342
  return isValid;
4016
4343
  };
4017
4344
 
4345
+ const cloneValue = (value) => {
4346
+ if (value === undefined) {
4347
+ return undefined;
4348
+ }
4349
+ try {
4350
+ return structuredClone(value);
4351
+ }
4352
+ catch {
4353
+ return JSON.parse(JSON.stringify(value));
4354
+ }
4355
+ };
4356
+ const resolveNamespacedWidgetId = (widgetId, namespace) => namespace ? `${namespace}__${widgetId}` : widgetId;
4357
+ const resolveStoreDataPath = (dataPath, namespace) => {
4358
+ if (!dataPath) {
4359
+ return dataPath;
4360
+ }
4361
+ if (!namespace) {
4362
+ return dataPath;
4363
+ }
4364
+ if (typeof dataPath === 'string') {
4365
+ return `${namespace}.${dataPath}`;
4366
+ }
4367
+ return Object.fromEntries(Object.entries(dataPath).map(([key, path]) => [key, `${namespace}.${path}`]));
4368
+ };
4369
+ /**
4370
+ * Capture Redux widget values for a section at edit entry.
4371
+ * Used to restore exact pre-edit state on Cancel (schemaData may be stale or shared with Redux).
4372
+ */
4373
+ function captureSectionEditSnapshot(values, section, options) {
4374
+ const { namespace, sectionId, supportingDocuments = [] } = options ?? {};
4375
+ const dataPaths = [];
4376
+ const processedPaths = new Set();
4377
+ const widgetIds = {};
4378
+ const addPath = (path) => {
4379
+ if (!path || processedPaths.has(path)) {
4380
+ return;
4381
+ }
4382
+ processedPaths.add(path);
4383
+ dataPaths.push({
4384
+ path,
4385
+ value: cloneValue(getValueByPath(values, path)),
4386
+ });
4387
+ if (path.endsWith('.geo_code_hierarchy_json')) {
4388
+ const prefix = path.slice(0, -'.geo_code_hierarchy_json'.length);
4389
+ addPath(`${prefix}.geo_lowest_level_value_id`);
4390
+ }
4391
+ };
4392
+ collectWidgets(section.panels).forEach((widget) => {
4393
+ const widgetId = resolveNamespacedWidgetId(widget['widget-id'], namespace);
4394
+ const storeDataPath = resolveStoreDataPath(widget['widget-data-path'], namespace);
4395
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
4396
+ widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
4397
+ }
4398
+ else {
4399
+ widgetIds[widgetId] = { present: false };
4400
+ }
4401
+ if (typeof storeDataPath === 'string') {
4402
+ addPath(storeDataPath);
4403
+ }
4404
+ else if (storeDataPath && typeof storeDataPath === 'object') {
4405
+ Object.values(storeDataPath).forEach((path) => {
4406
+ if (typeof path === 'string') {
4407
+ addPath(path);
4408
+ }
4409
+ });
4410
+ }
4411
+ });
4412
+ supportingDocuments.forEach((doc, index) => {
4413
+ const widgetId = `supporting-doc-${sectionId ?? 'section'}-${index}`;
4414
+ const storeDataPath = namespace && doc['document-data-path']
4415
+ ? `${namespace}.${doc['document-data-path']}`
4416
+ : doc['document-data-path'];
4417
+ if (Object.prototype.hasOwnProperty.call(values, widgetId)) {
4418
+ widgetIds[widgetId] = { present: true, value: cloneValue(values[widgetId]) };
4419
+ }
4420
+ else {
4421
+ widgetIds[widgetId] = { present: false };
4422
+ }
4423
+ if (typeof storeDataPath === 'string') {
4424
+ addPath(storeDataPath);
4425
+ }
4426
+ });
4427
+ return { dataPaths, widgetIds };
4428
+ }
4429
+ /** Apply a section edit snapshot back onto the full Redux values object. */
4430
+ function applySectionEditSnapshot(currentValues, snapshot) {
4431
+ let result = currentValues;
4432
+ for (const { path, value } of snapshot.dataPaths) {
4433
+ result = setValueByPath(result, path, cloneValue(value));
4434
+ }
4435
+ for (const [widgetId, entry] of Object.entries(snapshot.widgetIds)) {
4436
+ if (entry.present) {
4437
+ result = { ...result, [widgetId]: cloneValue(entry.value) };
4438
+ }
4439
+ else {
4440
+ const { [widgetId]: _removed, ...rest } = result;
4441
+ result = rest;
4442
+ }
4443
+ }
4444
+ return result;
4445
+ }
4446
+
4018
4447
  /** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
4019
4448
  const READONLY_VALUE_ROW_ROOT_CLASSES = [
4020
4449
  'TextDisplayWidget',
@@ -4226,6 +4655,8 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4226
4655
  }, [forceExitEdit]); // eslint-disable-line react-hooks/exhaustive-deps
4227
4656
  const [isDocumentsExpanded, setIsDocumentsExpanded] = useState(true);
4228
4657
  const sectionRef = useRef(null);
4658
+ const baselineSnapshotRef = useRef(null);
4659
+ const editEntrySnapshotRef = useRef(null);
4229
4660
  const [sectionHeight, setSectionHeight] = useState(null);
4230
4661
  const [editSectionPosition, setEditSectionPosition] = useState(null);
4231
4662
  // Capture section position when entering edit mode and update on scroll
@@ -4290,6 +4721,18 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4290
4721
  panels: makePanelsEditable(sectionToRender.panels, widgetsEditable),
4291
4722
  };
4292
4723
  }, [sectionToRender, widgetsEditable]);
4724
+ const effectiveHideEditButton = hideEditButton ||
4725
+ section['section-hide-edit-button'] === true ||
4726
+ !collectWidgets(section.panels || []).some((w) => section['section-editable'] === true || w['widget-readonly'] !== true);
4727
+ const captureEditEntrySnapshot = useCallback(() => {
4728
+ const currentValues = store.getState().widget.values;
4729
+ const supportingDocuments = section['section-supporting-documents'] || [];
4730
+ editEntrySnapshotRef.current = captureSectionEditSnapshot(currentValues, section, {
4731
+ namespace,
4732
+ sectionId,
4733
+ supportingDocuments: hasSupportingDocuments ? supportingDocuments : [],
4734
+ });
4735
+ }, [store, section, namespace, sectionId, hasSupportingDocuments]);
4293
4736
  // Handle edit button click
4294
4737
  const handleEdit = () => {
4295
4738
  // Capture height BEFORE entering edit mode to preserve space
@@ -4297,6 +4740,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4297
4740
  const height = sectionRef.current.offsetHeight;
4298
4741
  setSectionHeight(height);
4299
4742
  }
4743
+ captureEditEntrySnapshot();
4300
4744
  setIsEditMode(true);
4301
4745
  onEditModeChange?.(originalSectionId, true);
4302
4746
  };
@@ -4471,8 +4915,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4471
4915
  }
4472
4916
  return { records, files };
4473
4917
  }, [originalSection, hasSupportingDocuments]);
4474
- // Capture baseline when entering edit mode (used for isDirty comparison)
4475
- const baselineSnapshotRef = useRef(null);
4476
4918
  // IntakeForm only: increment when baseline is updated after save - forces badge to update (refs don't trigger re-renders)
4477
4919
  const [intakeFormBaselineTrigger, setIntakeFormBaselineTrigger] = useState(0);
4478
4920
  // IntakeForm: treat as edit mode for dirty tracking when isDraft. RegistryView: use isEditMode.
@@ -4490,6 +4932,9 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4490
4932
  // Set baseline when entering edit mode; clear when leaving (baseline captured only on entry)
4491
4933
  useEffect(() => {
4492
4934
  if (effectiveEditModeForDirty) {
4935
+ if (!editEntrySnapshotRef.current) {
4936
+ captureEditEntrySnapshot();
4937
+ }
4493
4938
  const oldSchemaData = schemaData || contextSchemaData || {};
4494
4939
  if (namespace) {
4495
4940
  const namespacedSchema = getValueByPath(oldSchemaData, namespace);
@@ -4498,11 +4943,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4498
4943
  : buildSectionSnapshot(oldSchemaData);
4499
4944
  }
4500
4945
  else {
4501
- baselineSnapshotRef.current = buildSectionSnapshot(oldSchemaData);
4946
+ baselineSnapshotRef.current = buildSectionSnapshot(store.getState().widget.values, namespace);
4502
4947
  }
4503
4948
  }
4504
4949
  else {
4505
4950
  baselineSnapshotRef.current = null;
4951
+ editEntrySnapshotRef.current = null;
4506
4952
  onSectionDirtyChange?.(sectionId, false);
4507
4953
  }
4508
4954
  // eslint-disable-next-line react-hooks/exhaustive-deps -- baseline must be captured only when effectiveEditModeForDirty toggles
@@ -4528,56 +4974,103 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4528
4974
  // and handleCancel.
4529
4975
  const revertToOriginalValues = useCallback(() => {
4530
4976
  const sectionWidgets = collectWidgets(originalSection.panels);
4531
- const oldSchemaData = schemaData || contextSchemaData;
4532
4977
  const currentStoreValues = store.getState().widget.values;
4978
+ const snapshot = editEntrySnapshotRef.current;
4533
4979
  let newStoreValues = currentStoreValues;
4534
- sectionWidgets.forEach(widget => {
4535
- const originalWidgetId = widget['widget-id'];
4536
- const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4537
- const widgetId = namespacedWidgetId;
4538
- const originalDataPath = widget['widget-data-path'];
4539
- const storeDataPath = namespace && originalDataPath
4540
- ? (typeof originalDataPath === 'string'
4541
- ? `${namespace}.${originalDataPath}`
4542
- : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
4543
- : originalDataPath;
4544
- if (widgetId && originalDataPath) {
4545
- let oldValue;
4546
- if (typeof originalDataPath === 'object') {
4547
- oldValue = {};
4548
- Object.entries(originalDataPath).forEach(([key, path]) => {
4549
- if (typeof path === 'string') {
4550
- oldValue[key] = getValueByPath(oldSchemaData, path);
4980
+ if (snapshot) {
4981
+ newStoreValues = applySectionEditSnapshot(currentStoreValues, snapshot);
4982
+ }
4983
+ else {
4984
+ const oldSchemaData = schemaData || contextSchemaData;
4985
+ const processedGeoGroups = new Set();
4986
+ sectionWidgets.forEach((widget) => {
4987
+ const originalWidgetId = widget['widget-id'];
4988
+ const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
4989
+ const widgetId = namespacedWidgetId;
4990
+ const originalDataPath = widget['widget-data-path'];
4991
+ const storeDataPath = namespace && originalDataPath
4992
+ ? (typeof originalDataPath === 'string'
4993
+ ? `${namespace}.${originalDataPath}`
4994
+ : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
4995
+ : originalDataPath;
4996
+ const geoConfig = widget['widget-geo-config'];
4997
+ if (widgetId && originalDataPath) {
4998
+ let oldValue;
4999
+ if (typeof originalDataPath === 'object') {
5000
+ oldValue = {};
5001
+ Object.entries(originalDataPath).forEach(([key, path]) => {
5002
+ if (typeof path === 'string') {
5003
+ oldValue[key] = getValueByPath(oldSchemaData, path);
5004
+ }
5005
+ });
5006
+ }
5007
+ else if (typeof originalDataPath === 'string') {
5008
+ oldValue = getValueByPath(oldSchemaData, originalDataPath);
5009
+ }
5010
+ if (oldValue !== undefined) {
5011
+ newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
5012
+ if (geoConfig && typeof storeDataPath === 'string') {
5013
+ const groupId = getGeoGroupId(storeDataPath);
5014
+ const levelValue = resolveGeoWidgetLevelValue(newStoreValues, widgetId, storeDataPath, geoConfig);
5015
+ if (levelValue !== undefined && levelValue !== null && levelValue !== '') {
5016
+ newStoreValues = { ...newStoreValues, [widgetId]: levelValue };
5017
+ }
5018
+ else {
5019
+ const { [widgetId]: _removed, ...rest } = newStoreValues;
5020
+ newStoreValues = rest;
5021
+ }
5022
+ if (!processedGeoGroups.has(groupId)) {
5023
+ resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
5024
+ processedGeoGroups.add(groupId);
5025
+ }
5026
+ if (geoConfig.parentWidgetId) {
5027
+ dispatch(setDataSource({ widgetId, data: [] }));
5028
+ }
4551
5029
  }
4552
- });
4553
- }
4554
- else if (typeof originalDataPath === 'string') {
4555
- oldValue = getValueByPath(oldSchemaData, originalDataPath);
5030
+ else {
5031
+ newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
5032
+ }
5033
+ }
4556
5034
  }
4557
- if (oldValue !== undefined) {
5035
+ });
5036
+ if (hasSupportingDocuments) {
5037
+ const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
5038
+ originalSupportingDocuments.forEach((doc, index) => {
5039
+ const widgetId = `supporting-doc-${sectionId}-${index}`;
5040
+ const originalDataPath = doc['document-data-path'];
5041
+ const storeDataPath = namespace && originalDataPath
5042
+ ? `${namespace}.${originalDataPath}`
5043
+ : originalDataPath;
5044
+ const oldValue = getValueByPath(oldSchemaData, originalDataPath);
4558
5045
  newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4559
- // Also revert the widgetId-based entry — useBaseWidget.handleChange
4560
- // sets values[widgetId] during editing, and useBaseWidget.currentValue
4561
- // reads values[widgetId] first before falling through to the dataPath.
4562
- newStoreValues = { ...newStoreValues, [widgetId]: oldValue };
4563
- }
5046
+ });
4564
5047
  }
4565
- });
4566
- if (hasSupportingDocuments) {
4567
- const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4568
- originalSupportingDocuments.forEach((doc, index) => {
4569
- const widgetId = `supporting-doc-${sectionId}-${index}`;
4570
- const originalDataPath = doc['document-data-path'];
4571
- const storeDataPath = namespace && originalDataPath
4572
- ? `${namespace}.${originalDataPath}`
4573
- : originalDataPath;
4574
- const oldValue = getValueByPath(oldSchemaData, originalDataPath);
4575
- newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
4576
- });
4577
- }
4578
- if (newStoreValues !== currentStoreValues) {
4579
- dispatch(setValues(newStoreValues));
4580
5048
  }
5049
+ const processedGeoGroups = new Set();
5050
+ sectionWidgets.forEach((widget) => {
5051
+ const geoConfig = widget['widget-geo-config'];
5052
+ if (!geoConfig) {
5053
+ return;
5054
+ }
5055
+ const originalWidgetId = widget['widget-id'];
5056
+ const widgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
5057
+ const originalDataPath = widget['widget-data-path'];
5058
+ const storeDataPath = namespace && typeof originalDataPath === 'string'
5059
+ ? `${namespace}.${originalDataPath}`
5060
+ : originalDataPath;
5061
+ if (typeof storeDataPath !== 'string') {
5062
+ return;
5063
+ }
5064
+ const groupId = getGeoGroupId(storeDataPath);
5065
+ if (!processedGeoGroups.has(groupId)) {
5066
+ resetAndSeedGeoHierarchyFromValues(newStoreValues, storeDataPath, widgetId, groupId);
5067
+ processedGeoGroups.add(groupId);
5068
+ }
5069
+ if (geoConfig.parentWidgetId) {
5070
+ dispatch(setDataSource({ widgetId, data: [] }));
5071
+ }
5072
+ });
5073
+ dispatch(setValues(newStoreValues));
4581
5074
  }, [originalSection, schemaData, contextSchemaData, store, namespace, hasSupportingDocuments, sectionId, dispatch]);
4582
5075
  // Handle save button click
4583
5076
  const handleSave = async () => {
@@ -4591,7 +5084,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4591
5084
  // This ensures we use the original widget IDs and data paths
4592
5085
  const sectionWidgets = collectWidgets(originalSection.panels);
4593
5086
  const currentState = store.getState().widget;
4594
- const currentSchemaData = currentState.values || {};
5087
+ let currentSchemaData = currentState.values || {};
5088
+ const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
5089
+ if (geoRegistrations.length > 0) {
5090
+ currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
5091
+ dispatch(setValues(currentSchemaData));
5092
+ }
4595
5093
  const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4596
5094
  if (!isSectionValid) {
4597
5095
  return;
@@ -4657,7 +5155,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4657
5155
  if (isDraft !== false && store && onSectionSave) {
4658
5156
  const sectionWidgets = collectWidgets(originalSection.panels);
4659
5157
  const currentState = store.getState().widget;
4660
- const currentSchemaData = currentState.values || {};
5158
+ let currentSchemaData = currentState.values || {};
5159
+ const geoRegistrations = collectGeoWidgetRegistrationsFromWidgets(sectionWidgets, namespace);
5160
+ if (geoRegistrations.length > 0) {
5161
+ currentSchemaData = reconcileGeoHierarchiesInValues(currentSchemaData, geoRegistrations, currentState.dataSources || {});
5162
+ dispatch(setValues(currentSchemaData));
5163
+ }
4661
5164
  const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch, true);
4662
5165
  if (!isSectionValid)
4663
5166
  return;
@@ -5073,7 +5576,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5073
5576
  color: changeRequestType === 'new' ? 'var(--owt-color-bg, #FFFFFF)' : 'var(--owt-color-error, #B91C1C)',
5074
5577
  whiteSpace: 'nowrap',
5075
5578
  boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
5076
- }, 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: {
5579
+ }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && effectiveHideEditButton ? { paddingBottom: '30px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
5077
5580
  marginTop: '20px',
5078
5581
  paddingBottom: '30px',
5079
5582
  display: 'flex',
@@ -5121,7 +5624,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
5121
5624
  fontSize: '14px',
5122
5625
  color: 'var(--owt-color-text, #011627)',
5123
5626
  fontWeight: 'normal',
5124
- }, 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: {
5627
+ }, children: crViewData.approvedDate }))] })] })] })), mode === 'RegistryView' && !effectiveHideEditButton && (jsxRuntimeExports.jsx("hr", { className: "w-full", style: { height: '1px', marginTop: !isEditMode ? '10px' : 0, marginBottom: '14px', border: 'none', backgroundColor: 'var(--owt-color-border, #C4C4C4)' } })), mode === 'RegistryView' && !isEditMode && !effectiveHideEditButton && (jsxRuntimeExports.jsx("div", { className: "flex justify-center items-center", style: { marginBottom: '20px' }, children: jsxRuntimeExports.jsxs("button", { onClick: handleEdit, className: "font-normal inline-flex items-center gap-2 bg-transparent border-0 p-0 cursor-pointer hover:opacity-80", style: {
5125
5628
  fontFamily: 'Roboto, sans-serif',
5126
5629
  fontSize: '16px',
5127
5630
  color: 'var(--owt-color-text-muted, #727474)',
@@ -5927,7 +6430,7 @@ const JSONEditorPanel = ({ section, onChange, onReset, context = 'section', }) =
5927
6430
  'widget-data-format.dateConstraint': ['any', 'past-only', 'future-only'],
5928
6431
  'widget-data-format.dateTimeConstraint': ['any', 'past-only', 'future-only'],
5929
6432
  // Widget options
5930
- 'widget-data-options.action': ['show', 'hide', 'enable', 'disable'],
6433
+ 'widget-data-options.action': ['show', 'hide', 'enable', 'disable', 'require'],
5931
6434
  'widget-data-options.condition.operator': CONDITION_OPERATORS,
5932
6435
  };
5933
6436
  }, []);
@@ -7312,7 +7815,7 @@ const removeMask = (value, mask) => {
7312
7815
  };
7313
7816
 
7314
7817
  const TextInputWidget = ({ config }) => {
7315
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7818
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7316
7819
  const { translate, translateConfig } = useWidgetTranslation();
7317
7820
  // Track raw value separately for masking (to preserve unmasked value internally)
7318
7821
  const formatConfig = widgetConfig['widget-data-format'];
@@ -7449,7 +7952,7 @@ const TextInputWidget = ({ config }) => {
7449
7952
  const label = translateConfig(widgetConfig['widget-label']);
7450
7953
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] TextDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
7451
7954
  }
7452
- 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
7955
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntimeExports.jsx("input", { type: getInputType(), value: displayValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: formatConfig?.mask ? undefined : maxLength, inputMode: formatConfig?.currency
7453
7956
  ? 'decimal'
7454
7957
  : formatConfig?.characterType === 'numeric' || formatConfig?.characterType === 'numeric-decimal'
7455
7958
  ? 'numeric'
@@ -7472,7 +7975,7 @@ const NumberInputWidget = ({ config }) => {
7472
7975
  }
7473
7976
  return { ...config, 'widget-data-default': normalizedDefault };
7474
7977
  }, [config]);
7475
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
7978
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
7476
7979
  const { translate, translateConfig } = useWidgetTranslation();
7477
7980
  const formatConfig = widgetConfig['widget-data-format'];
7478
7981
  const validationConfig = widgetConfig['widget-data-validation'];
@@ -7603,7 +8106,7 @@ const NumberInputWidget = ({ config }) => {
7603
8106
  const display = numValue !== null ? formatNumber(numValue, formatConfig) : '';
7604
8107
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] NumberDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: `text-base text-gray-900 font-medium ${textAlignClass}`, title: String(display ?? ''), children: display }) })] }));
7605
8108
  }
7606
- 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 === ''))
8109
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, onKeyDown: handleKeyDown, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, maxLength: maxLength, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${textAlignClass} ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
7607
8110
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7608
8111
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), maxLength && (jsxRuntimeExports.jsxs("span", { className: `text-xs ml-2 flex-shrink-0 ${currentLength > maxLength
7609
8112
  ? 'text-red-500'
@@ -7611,7 +8114,7 @@ const NumberInputWidget = ({ config }) => {
7611
8114
  };
7612
8115
 
7613
8116
  const BooleanWidget = ({ config }) => {
7614
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8117
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7615
8118
  const { translate, translateConfig } = useWidgetTranslation();
7616
8119
  const formatConfig = widgetConfig['widget-data-format'];
7617
8120
  const representation = formatConfig?.booleanRepresentation || 'true-false';
@@ -7680,7 +8183,7 @@ const BooleanWidget = ({ config }) => {
7680
8183
  }
7681
8184
  // Render based on control type
7682
8185
  if (controlType === 'checkbox') {
7683
- 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] }))] })] }) }));
8186
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex items-baseline cursor-pointer gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), (currentValue === true || currentValue === false) && (jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: currentValue === true ? trueLabel : falseLabel }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7684
8187
  }
7685
8188
  if (controlType === 'radio') {
7686
8189
  const containerClass = orientation === 'horizontal'
@@ -7688,10 +8191,10 @@ const BooleanWidget = ({ config }) => {
7688
8191
  : 'flex flex-col items-start gap-2';
7689
8192
  const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
7690
8193
  const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
7691
- 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] }))] })] }) }));
8194
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: containerClass, onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === null, onChange: () => handleRadioChange(null), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: unsetLabel })] })), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === true, onChange: () => handleRadioChange(true), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: trueLabel })] }), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === false, onChange: () => handleRadioChange(false), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7692
8195
  }
7693
8196
  // Toggle/switch control type
7694
- 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
8197
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 sm:min-w-[150px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-3", onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(null), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === null
7695
8198
  ? 'bg-blue-600 text-white border-blue-600'
7696
8199
  : 'bg-white text-gray-700 border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`, style: { borderRadius: '15px' }, children: unsetLabel })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(true), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === true
7697
8200
  ? 'bg-blue-600 text-white border-blue-600'
@@ -7701,7 +8204,7 @@ const BooleanWidget = ({ config }) => {
7701
8204
  };
7702
8205
 
7703
8206
  const DateInputWidget = ({ config }) => {
7704
- const { value, error, touched, isEnabled, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
8207
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
7705
8208
  const formValues = useSelector((state) => state.widget.values);
7706
8209
  const { translateConfig } = useWidgetTranslation();
7707
8210
  const formatConfig = widgetConfig['widget-data-format'];
@@ -7902,7 +8405,7 @@ const DateInputWidget = ({ config }) => {
7902
8405
  }
7903
8406
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
7904
8407
  }
7905
- 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
8408
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDate : undefined, max: inputMethod === 'picker' ? effectiveMaxDate : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${showValidationError || showRequiredError
7906
8409
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7907
8410
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), showValidationError && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })] }) }));
7908
8411
  };
@@ -8192,7 +8695,7 @@ const validateDateTimeConstraints = (date, minDateTime, maxDateTime, constraint)
8192
8695
  };
8193
8696
 
8194
8697
  const DateTimeInputWidget = ({ config }) => {
8195
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8698
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8196
8699
  const { translate, translateConfig } = useWidgetTranslation();
8197
8700
  const formatConfig = widgetConfig['widget-data-format'];
8198
8701
  const optionsConfig = widgetConfig['widget-data-options'];
@@ -8345,29 +8848,33 @@ const DateTimeInputWidget = ({ config }) => {
8345
8848
  }
8346
8849
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] DateTimeDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8347
8850
  }
8348
- 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 === ''))
8851
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDateTime : undefined, max: inputMethod === 'picker' ? effectiveMaxDateTime : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8349
8852
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8350
8853
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8351
8854
  };
8352
8855
 
8353
8856
  const SelectWidget = ({ config }) => {
8354
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8857
+ const { value, geoDisplayLabel, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8355
8858
  const { translate, translateConfig } = useWidgetTranslation();
8356
8859
  // For readonly mode, render as display text showing only the selected label
8357
8860
  if (widgetConfig['widget-readonly']) {
8358
8861
  const label = translateConfig(widgetConfig['widget-label']);
8359
8862
  // Find the selected option's label
8360
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
8361
- const displayValue = selectedOption ? selectedOption.label : (value || '-');
8863
+ const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
8864
+ const displayValue = selectedOption
8865
+ ? translateConfig(selectedOption.label)
8866
+ : loading
8867
+ ? (geoDisplayLabel || '-')
8868
+ : (geoDisplayLabel || (value != null && value !== '' ? translateConfig(String(value)) : '-'));
8362
8869
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] SelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8363
8870
  }
8364
- 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 === ''))
8871
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8365
8872
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8366
- : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8873
+ : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: translateConfig(option.label) }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8367
8874
  };
8368
8875
 
8369
8876
  const RadioWidget = ({ config }) => {
8370
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8877
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8371
8878
  const { translate, translateConfig } = useWidgetTranslation();
8372
8879
  const formatConfig = widgetConfig['widget-data-format'];
8373
8880
  const layout = formatConfig?.layout || widgetConfig['widget-orientation'] || 'vertical';
@@ -8430,14 +8937,16 @@ const RadioWidget = ({ config }) => {
8430
8937
  if (widgetConfig['widget-readonly']) {
8431
8938
  const label = translateConfig(widgetConfig['widget-label']);
8432
8939
  const selectedOption = processedOptions.find(opt => opt.value === currentValue);
8433
- const displayValue = selectedOption ? selectedOption.label : (allowUnset && currentValue === null ? '-' : '');
8940
+ const displayValue = selectedOption
8941
+ ? translateConfig(selectedOption.label)
8942
+ : (allowUnset && currentValue === null ? '-' : '');
8434
8943
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] RadioDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8435
8944
  }
8436
- 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] }))] })] }) }));
8945
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null, onChange: handleUnset, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: option.value, checked: currentValue === option.value, onChange: (e) => handleChange(option.value), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, option.value)))] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8437
8946
  };
8438
8947
 
8439
8948
  const CheckboxWidget = ({ config }) => {
8440
- const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8949
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8441
8950
  const { translate, translateConfig } = useWidgetTranslation();
8442
8951
  const hasDataSource = !!widgetConfig['widget-data-source'];
8443
8952
  const formatConfig = widgetConfig['widget-data-format'];
@@ -8452,7 +8961,7 @@ const CheckboxWidget = ({ config }) => {
8452
8961
  const displayValue = isChecked ? 'Yes' : 'No';
8453
8962
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CheckboxDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8454
8963
  }
8455
- 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] }))] })] }) }));
8964
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex cursor-pointer items-baseline gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onChange(e.target.checked), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: isChecked ? 'Yes' : 'No' })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8456
8965
  }
8457
8966
  // Multiple checkboxes (with data source) - for array values
8458
8967
  // Process and sort options if needed
@@ -8522,7 +9031,7 @@ const CheckboxWidget = ({ config }) => {
8522
9031
  : '-';
8523
9032
  return (jsxRuntimeExports.jsxs("div", { className: "mb-3 CheckboxDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
8524
9033
  }
8525
- 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] }))] })] }) }));
9034
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `inline-flex cursor-pointer items-baseline gap-2 ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", value: option.value, checked: selectedValues.includes(option.value), onChange: (e) => handleCheckboxChange(option.value, e.target.checked), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: translateConfig(option.label) })] }, option.value)))) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8526
9035
  };
8527
9036
 
8528
9037
  const SimpleTableWidget = ({ config }) => {
@@ -8573,7 +9082,7 @@ const SimpleTableWidget = ({ config }) => {
8573
9082
  };
8574
9083
 
8575
9084
  const ArrayWidget = ({ config }) => {
8576
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
9085
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8577
9086
  const { translate, translateConfig } = useWidgetTranslation();
8578
9087
  const items = Array.isArray(value) ? value : [];
8579
9088
  const itemConfig = widgetConfig['widget-item'];
@@ -8597,7 +9106,7 @@ const ArrayWidget = ({ config }) => {
8597
9106
  newItems[index] = newValue;
8598
9107
  onChange(newItems);
8599
9108
  };
8600
- 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) => {
9109
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
8601
9110
  ({
8602
9111
  ...itemConfig,
8603
9112
  'widget-id': `${widgetConfig['widget-id']}-item-${index}`,
@@ -8608,7 +9117,7 @@ const ArrayWidget = ({ config }) => {
8608
9117
  };
8609
9118
 
8610
9119
  const IterableAccordionWidget = ({ config }) => {
8611
- const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
9120
+ const { value, error, touched, isEnabled, isRequired, onChange, config: widgetConfig, } = useBaseWidget({ config });
8612
9121
  const { translate, translateConfig } = useWidgetTranslation();
8613
9122
  const items = Array.isArray(value) ? value : [];
8614
9123
  const itemConfig = widgetConfig['widget-item'];
@@ -8657,7 +9166,7 @@ const IterableAccordionWidget = ({ config }) => {
8657
9166
  newItems[index] = newValue;
8658
9167
  onChange(newItems);
8659
9168
  };
8660
- 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) => {
9169
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px]", children: [jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start mb-2", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0 flex justify-between items-center", children: [jsxRuntimeExports.jsx("div", { className: "flex-1" }), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: addItem, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600", style: { borderRadius: '15px' }, children: addLabel }))] })] }), items.length === 0 ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300 rounded", children: [translate('common.noItems'), ". ", operations.add && !isReadonly && translate('common.clickToAdd', { label: addLabel })] })) : (jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((itemValue, index) => {
8661
9170
  const isCollapsed = collapsedItems[index] ?? defaultCollapsed;
8662
9171
  const parentPath = widgetConfig['widget-data-path'];
8663
9172
  const childPath = itemConfig['widget-data-path'];
@@ -8696,7 +9205,7 @@ const IterableAccordionWidget = ({ config }) => {
8696
9205
  };
8697
9206
 
8698
9207
  const PhoneInputWidget = ({ config }) => {
8699
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9208
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8700
9209
  const { translate, translateConfig } = useWidgetTranslation();
8701
9210
  // Use formatted value if available, otherwise raw value
8702
9211
  const displayValue = formattedValue !== undefined && formattedValue !== value
@@ -8707,13 +9216,13 @@ const PhoneInputWidget = ({ config }) => {
8707
9216
  const label = translateConfig(widgetConfig['widget-label']);
8708
9217
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] PhoneDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue || ''), children: displayValue || '-' }) })] }));
8709
9218
  }
8710
- 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 === ''))
9219
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: "tel", value: displayValue, onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
8711
9220
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8712
9221
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8713
9222
  };
8714
9223
 
8715
9224
  const CurrencyInputWidget = ({ config }) => {
8716
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9225
+ const { value, formattedValue, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
8717
9226
  const { translate, translateConfig } = useWidgetTranslation();
8718
9227
  // For input, use raw numeric value; formatted value is for display only
8719
9228
  const numericValue = typeof value === 'number' ? value : (value ? parseFloat(String(value)) : '');
@@ -8735,7 +9244,7 @@ const CurrencyInputWidget = ({ config }) => {
8735
9244
  const display = formattedValue || (value !== null && value !== undefined ? String(value) : '-');
8736
9245
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] CurrencyDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(display ?? ''), children: display }) })] }));
8737
9246
  }
8738
- 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 === ''))
9247
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: translateConfig(widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: "relative", children: [jsxRuntimeExports.jsx("input", { type: "text", inputMode: "decimal", value: numericValue, onChange: handleChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: translateConfig(widgetConfig['widget-data-placeholder']), className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (value === null || value === undefined || value === ''))
8739
9248
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
8740
9249
  : 'border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']) }), formattedValue && formattedValue !== String(value) && (jsxRuntimeExports.jsx("span", { className: "absolute right-3 top-2 text-gray-500 text-sm", children: formattedValue }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8741
9250
  };
@@ -8783,20 +9292,69 @@ const DisplayWidget = ({ config }) => {
8783
9292
  if (!label || label.trim() === '') {
8784
9293
  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 }));
8785
9294
  }
8786
- // With label, render as key-value pair (structure matches other readonly widgets for SectionRenderer ellipsis)
8787
- 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 }) })] }));
9295
+ // With label, render as key-value pair (structure matches other readonly widgets for SectionRenderer ellipsis)
9296
+ 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 }) })] }));
9297
+ };
9298
+
9299
+ const getDateColumnConstraintError = (column, cellValue, rowValues, translateConfig) => {
9300
+ const displayValue = cellValue && typeof cellValue === 'string' ? cellValue.split('T')[0] : '';
9301
+ if (!displayValue) {
9302
+ return null;
9303
+ }
9304
+ const optionsConfig = column['widget-data-options'];
9305
+ const formatConfig = column['widget-data-format'];
9306
+ const dateConstraint = formatConfig?.dateConstraint || 'any';
9307
+ const minDate = optionsConfig?.minDate;
9308
+ const maxDate = optionsConfig?.maxDate;
9309
+ const minDateField = optionsConfig?.minDateField;
9310
+ const maxDateField = optionsConfig?.maxDateField;
9311
+ const minDateMessage = optionsConfig?.minDateMessage
9312
+ ? translateConfig(optionsConfig.minDateMessage)
9313
+ : undefined;
9314
+ const maxDateMessage = optionsConfig?.maxDateMessage
9315
+ ? translateConfig(optionsConfig.maxDateMessage)
9316
+ : undefined;
9317
+ const resolveSiblingDate = (fieldRef) => {
9318
+ if (!fieldRef) {
9319
+ return undefined;
9320
+ }
9321
+ const raw = getValueByPath(rowValues, fieldRef) ?? rowValues[fieldRef];
9322
+ return resolveDateBoundFromFieldValue(raw);
9323
+ };
9324
+ const effectiveMinDate = mergeMinDateBounds(getMinDate(dateConstraint, minDate), resolveSiblingDate(minDateField));
9325
+ const effectiveMaxDate = mergeMaxDateBounds(getMaxDate(dateConstraint, maxDate), resolveSiblingDate(maxDateField));
9326
+ return validateDateConstraints(displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, { minDateMessage, maxDateMessage });
9327
+ };
9328
+ const isTableRowDataValid = (rowData, columns, tableReadonly, translateConfig) => {
9329
+ for (const col of columns) {
9330
+ if (tableReadonly || col['widget-readonly'] === true) {
9331
+ continue;
9332
+ }
9333
+ const columnKey = col['column-key'];
9334
+ const cellValue = rowData[columnKey];
9335
+ const widgetErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
9336
+ if (widgetErrors.length > 0) {
9337
+ return false;
9338
+ }
9339
+ if ((col.widget || 'text') === 'date') {
9340
+ const dateError = getDateColumnConstraintError(col, cellValue, rowData, translateConfig);
9341
+ if (dateError) {
9342
+ return false;
9343
+ }
9344
+ }
9345
+ }
9346
+ return true;
8788
9347
  };
8789
-
8790
9348
  const TableCellSelect = ({ config, value, onValueChange }) => {
8791
9349
  const { translate } = useWidgetTranslation();
8792
9350
  // Use useBaseWidget to get data source options (it handles loading)
8793
9351
  const { dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
8794
9352
  const isReadonly = config['widget-readonly'] || false;
8795
- 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: {
8796
- borderRadius: '10px',
8797
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8798
- backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8799
- }, children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') || 'Select' }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }));
9353
+ return (jsxRuntimeExports.jsxs("div", { className: "table-cell-field w-full", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onValueChange(e.target.value === '' ? undefined : e.target.value), disabled: isReadonly || loading, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly || loading ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
9354
+ borderRadius: '10px',
9355
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9356
+ backgroundColor: isReadonly || loading ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9357
+ }, 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' })] }));
8800
9358
  };
8801
9359
  const SelectDisplayValue$1 = ({ config, value }) => {
8802
9360
  const { dataSourceOptions, loading } = useBaseWidget({ config });
@@ -8806,7 +9364,7 @@ const SelectDisplayValue$1 = ({ config, value }) => {
8806
9364
  if (value === null || value === undefined || value === '') {
8807
9365
  return jsxRuntimeExports.jsx("span", { children: "-" });
8808
9366
  }
8809
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
9367
+ const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
8810
9368
  return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
8811
9369
  };
8812
9370
  const TableCellText = ({ config, value, onValueChange }) => {
@@ -8815,11 +9373,11 @@ const TableCellText = ({ config, value, onValueChange }) => {
8815
9373
  config['widget-data-format'];
8816
9374
  const maxLength = config['widget-data-validation']?.maxLength;
8817
9375
  const displayValue = value !== null && value !== undefined ? String(value) : '';
8818
- 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: {
8819
- borderRadius: '10px',
8820
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8821
- backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8822
- } }));
9376
+ 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: {
9377
+ borderRadius: '10px',
9378
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9379
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9380
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] }));
8823
9381
  };
8824
9382
  const TableCellNumber = ({ config, value, onValueChange }) => {
8825
9383
  const isReadonly = config['widget-readonly'] || false;
@@ -8842,11 +9400,11 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
8842
9400
  onValueChange(inputValue);
8843
9401
  }
8844
9402
  };
8845
- 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: {
8846
- borderRadius: '10px',
8847
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8848
- backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8849
- } }));
9403
+ 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: {
9404
+ borderRadius: '10px',
9405
+ borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
9406
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
9407
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] }));
8850
9408
  };
8851
9409
  const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8852
9410
  const { translateConfig } = useWidgetTranslation();
@@ -8905,13 +9463,15 @@ const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8905
9463
  setConstraintError(error);
8906
9464
  };
8907
9465
  const hasError = Boolean(constraintError);
8908
- 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: {
9466
+ 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: {
8909
9467
  borderRadius: '10px',
8910
9468
  borderColor: hasError
8911
9469
  ? 'var(--owt-color-error, #B91C1C)'
8912
9470
  : 'var(--owt-widget-input-border, #C4C4C4)',
8913
9471
  backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8914
- } }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-xs mt-0.5 leading-tight", children: constraintError }))] }));
9472
+ } }), jsxRuntimeExports.jsx("p", { className: "table-cell-field-error text-xs mt-0.5 leading-tight", style: {
9473
+ color: hasError ? 'var(--owt-color-error, #B91C1C)' : 'transparent',
9474
+ }, "aria-live": "polite", children: constraintError ?? '\u00a0' })] }));
8915
9475
  };
8916
9476
  const TableWidget = ({ config }) => {
8917
9477
  const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
@@ -8937,6 +9497,18 @@ const TableWidget = ({ config }) => {
8937
9497
  const isSectionEditMode = !isReadonly && operations.edit;
8938
9498
  // Check if any row is being edited (either manually or via section edit mode)
8939
9499
  const isAnyRowEditing = editingState !== null || isAdding;
9500
+ const canSaveEditingRow = useMemo(() => {
9501
+ if (!editingState) {
9502
+ return false;
9503
+ }
9504
+ return isTableRowDataValid(editingState.currentValue, columns, isReadonly, translateConfig);
9505
+ }, [editingState, columns, isReadonly, translateConfig]);
9506
+ const canSaveNewRow = useMemo(() => {
9507
+ if (!isAdding || !newRowData) {
9508
+ return false;
9509
+ }
9510
+ return isTableRowDataValid(newRowData, columns, isReadonly, translateConfig);
9511
+ }, [isAdding, newRowData, columns, isReadonly, translateConfig]);
8940
9512
  // Show confirmation dialog
8941
9513
  const showConfirmation = useCallback((message, onConfirm, onCancel) => {
8942
9514
  setConfirmationState({
@@ -9026,6 +9598,8 @@ const TableWidget = ({ config }) => {
9026
9598
  const saveEdit = useCallback(async () => {
9027
9599
  if (!editingState)
9028
9600
  return;
9601
+ if (!canSaveEditingRow)
9602
+ return;
9029
9603
  const rowData = editingState.currentValue;
9030
9604
  const rowIndex = editingState.rowIndex;
9031
9605
  setLoadingRowIndex(rowIndex);
@@ -9097,7 +9671,7 @@ const TableWidget = ({ config }) => {
9097
9671
  finally {
9098
9672
  setLoadingRowIndex(null);
9099
9673
  }
9100
- }, [editingState, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
9674
+ }, [editingState, canSaveEditingRow, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
9101
9675
  // Add new row
9102
9676
  const startAdd = useCallback(() => {
9103
9677
  // If there's an unsaved edit, cancel it first (no confirmation needed)
@@ -9115,6 +9689,8 @@ const TableWidget = ({ config }) => {
9115
9689
  const saveAdd = useCallback(async () => {
9116
9690
  if (!isAdding || !newRowData)
9117
9691
  return;
9692
+ if (!canSaveNewRow)
9693
+ return;
9118
9694
  setLoadingRowIndex(-1); // Use -1 to indicate new row
9119
9695
  try {
9120
9696
  let savedRow = { ...newRowData };
@@ -9145,7 +9721,7 @@ const TableWidget = ({ config }) => {
9145
9721
  finally {
9146
9722
  setLoadingRowIndex(null);
9147
9723
  }
9148
- }, [isAdding, newRowData, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode]);
9724
+ }, [isAdding, newRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, translate, isSectionEditMode]);
9149
9725
  // Delete row
9150
9726
  const deleteRow = useCallback(async (rowIndex) => {
9151
9727
  if (isAnyRowEditing) {
@@ -9423,6 +9999,22 @@ const TableWidget = ({ config }) => {
9423
9999
  box-shadow: 0 0 0 1px var(--owt-widget-input-focus-border, #F07B1A);
9424
10000
  border-color: var(--owt-widget-input-focus-border, #F07B1A);
9425
10001
  }
10002
+
10003
+ /* Keep inputs and action buttons top-aligned when a cell shows validation text */
10004
+ .${tableWidgetId} tr.table-row-editing td {
10005
+ vertical-align: top;
10006
+ }
10007
+
10008
+ .${tableWidgetId} .table-cell-field-error {
10009
+ min-height: 1.125rem;
10010
+ }
10011
+
10012
+ .${tableWidgetId} .table-cell-actions {
10013
+ display: flex;
10014
+ flex-direction: row;
10015
+ gap: 0.5rem;
10016
+ align-items: flex-start;
10017
+ }
9426
10018
  ` }), 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: {
9427
10019
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9428
10020
  border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
@@ -9441,7 +10033,7 @@ const TableWidget = ({ config }) => {
9441
10033
  }, 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) => {
9442
10034
  const isEditing = isRowEditing(rowIndex);
9443
10035
  const isLoading = loadingRowIndex === rowIndex;
9444
- return (jsxRuntimeExports.jsxs("tr", { className: isLoading ? 'opacity-50' : '', style: {
10036
+ return (jsxRuntimeExports.jsxs("tr", { className: `${isLoading ? 'opacity-50' : ''}${isEditing ? ' table-row-editing' : ''}`, style: {
9445
10037
  borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
9446
10038
  backgroundColor: isEditing
9447
10039
  ? 'var(--owt-widget-table-editing-row-bg, #FBE6AA)'
@@ -9452,7 +10044,7 @@ const TableWidget = ({ config }) => {
9452
10044
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rowIndex, col, row) }, col['column-key']));
9453
10045
  }), ((operations.edit || operations.remove) && !isReadonly) || isEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: isEditing ? (
9454
10046
  // Show OK (Save)/Cancel buttons when row is being edited (works in both section edit mode and normal mode)
9455
- 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: {
10047
+ 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: {
9456
10048
  display: 'inline-block',
9457
10049
  minWidth: '60px',
9458
10050
  backgroundColor: 'var(--owt-color-success, #16A34A)',
@@ -9479,7 +10071,7 @@ const TableWidget = ({ config }) => {
9479
10071
  backgroundColor: 'transparent',
9480
10072
  border: 'none',
9481
10073
  }, children: translate('common.remove') || 'Delete' }))] })) })) : null] }, rowIndex));
9482
- }), 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: {
10074
+ }), 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: {
9483
10075
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9484
10076
  backgroundColor: 'var(--owt-color-success, #16A34A)',
9485
10077
  color: 'var(--owt-color-bg, #FFFFFF)',
@@ -9495,6 +10087,23 @@ const TableWidget = ({ config }) => {
9495
10087
  }, children: translate('common.cancel') || 'Cancel' })] }) })] }))] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] })] }));
9496
10088
  };
9497
10089
 
10090
+ const isUnsetRowValue = (value) => value === null || value === undefined || value === '';
10091
+ /** Match TableWidget cell styling for add / update / delete rows */
10092
+ const getRowCellStyle = (editAction) => {
10093
+ if (editAction === 'ADD') {
10094
+ return { color: 'var(--owt-color-success, #16A34A)' };
10095
+ }
10096
+ if (editAction === 'DELETE') {
10097
+ return {
10098
+ color: 'var(--owt-color-error, #B91C1C)',
10099
+ textDecoration: 'line-through',
10100
+ };
10101
+ }
10102
+ if (editAction === 'UPDATE') {
10103
+ return { color: 'var(--owt-color-warning, #F59E0B)' };
10104
+ }
10105
+ return {};
10106
+ };
9498
10107
  // Display select value label in view mode
9499
10108
  const SelectDisplayValue = ({ config, value }) => {
9500
10109
  const { dataSourceOptions, loading } = useBaseWidget({ config });
@@ -9502,53 +10111,57 @@ const SelectDisplayValue = ({ config, value }) => {
9502
10111
  return jsxRuntimeExports.jsx("span", { children: "-" });
9503
10112
  if (value === null || value === undefined || value === '')
9504
10113
  return jsxRuntimeExports.jsx("span", { children: "-" });
9505
- const selectedOption = dataSourceOptions.find((option) => option.value === value);
10114
+ const selectedOption = dataSourceOptions.find((option) => option.value === value || String(option.value) === String(value));
9506
10115
  return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
9507
10116
  };
10117
+ /** Isolated dialog field — avoids re-running data-source effects when sibling fields update. */
10118
+ const DialogTableField = memo(function DialogTableField({ col, cellWidgetId, dialogRowValues, isReadonly, }) {
10119
+ const widgetType = col.widget || 'text';
10120
+ const fieldConfig = useMemo(() => {
10121
+ return {
10122
+ ...col,
10123
+ widget: widgetType,
10124
+ 'widget-type': col['widget-type'] || 'input',
10125
+ 'widget-id': cellWidgetId,
10126
+ 'widget-label': col['widget-label'],
10127
+ 'widget-readonly': isReadonly || col['widget-readonly'] === true,
10128
+ 'widget-data-path': undefined,
10129
+ 'widget-data-default': col['widget-data-default'],
10130
+ 'widget-data-options': undefined,
10131
+ 'widget-required': shouldRequireWidget(col['widget-data-options'], dialogRowValues, col['widget-required']),
10132
+ };
10133
+ }, [col, cellWidgetId, dialogRowValues, isReadonly, widgetType]);
10134
+ return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig }) }));
10135
+ });
9508
10136
  /**
9509
10137
  * Dialog table widget:
9510
10138
  * - Table displays a subset of columns (n out of x)
9511
10139
  * - Add/Edit happens in a modal dialog that shows ALL columns as a form
9512
- *
9513
- * Usage in schema:
9514
- * {
9515
- * "widget": "dialog-table",
9516
- * "widget-type": "table",
9517
- * "widget-label": "Household Members",
9518
- * "widget-id": "householdMembers",
9519
- * "widget-data-path": "household.members",
9520
- * "widget-data-columns": [ ...all columns... ],
9521
- * "widget-data-visible-columns": ["firstName", "lastName", "dob"], // optional override; default = by column flag
9522
- * // Per-column control (recommended): set "column-visible-in-table": false to hide it in the table
9523
- * "widget-data-operations": { "add": true, "edit": true, "remove": true }
9524
- * }
9525
10140
  */
9526
10141
  const DialogTableWidget = ({ config }) => {
9527
10142
  const { value, error, touched, isEnabled, onChange, config: widgetConfig } = useBaseWidget({ config });
9528
10143
  const { translate, translateConfig } = useWidgetTranslation();
9529
10144
  const dispatch = useDispatch();
9530
- const storeValues = useSelector((state) => state.widget?.values ?? {});
9531
10145
  const rows = Array.isArray(value) ? value : [];
9532
10146
  const columns = widgetConfig['widget-data-columns'] || [];
9533
10147
  const operations = widgetConfig['widget-data-operations'] || {};
9534
10148
  const isReadonly = widgetConfig['widget-readonly'] || false;
10149
+ // Soft-delete (keep row, red + strikethrough) whenever remove is allowed — matches TableWidget
10150
+ const shouldSoftDeleteOnRemove = !isReadonly && !!operations.remove;
9535
10151
  const visibleColumnKeys = widgetConfig['widget-data-visible-columns'];
9536
10152
  const visibleColumns = useMemo(() => {
9537
- // 1) If explicit list provided, it wins
9538
10153
  if (Array.isArray(visibleColumnKeys) && visibleColumnKeys.length > 0) {
9539
10154
  const keySet = new Set(visibleColumnKeys);
9540
10155
  return columns.filter((c) => keySet.has(c['column-key']));
9541
10156
  }
9542
- // 2) Otherwise decide per column (default = visible)
9543
10157
  return columns.filter((c) => c['column-visible-in-table'] !== false);
9544
10158
  }, [columns, visibleColumnKeys]);
9545
10159
  const [dialogOpen, setDialogOpen] = useState(false);
9546
10160
  const [dialogMode, setDialogMode] = useState('add');
9547
10161
  const [activeRowIndex, setActiveRowIndex] = useState(null);
9548
- const [formData, setFormData] = useState({});
9549
- /** Unique per dialog open so Redux widget ids don't reuse stale values across rows/add sessions */
9550
10162
  const dialogSessionRef = useRef(0);
9551
10163
  const [dialogSessionId, setDialogSessionId] = useState(0);
10164
+ const membersWidgetId = widgetConfig['widget-id'];
9552
10165
  const addDialogTitle = translateConfig(widgetConfig['widget-data-dialog-title-add']) ||
9553
10166
  translate('table.addRecordDialog') ||
9554
10167
  'Add record';
@@ -9559,19 +10172,35 @@ const DialogTableWidget = ({ config }) => {
9559
10172
  const emptyRow = {};
9560
10173
  columns.forEach((col) => {
9561
10174
  const key = col['column-key'];
9562
- emptyRow[key] = col['widget-data-default'] ?? '';
10175
+ if (col['widget-data-default'] !== undefined) {
10176
+ emptyRow[key] = col['widget-data-default'];
10177
+ }
10178
+ else if (col.widget === 'checkbox') {
10179
+ emptyRow[key] = false;
10180
+ }
9563
10181
  });
9564
10182
  return emptyRow;
9565
10183
  }, [columns]);
9566
- const dialogFieldWidgetId = useCallback((columnKey) => `${widgetConfig['widget-id']}-dlg-${dialogSessionId}-${columnKey}`, [widgetConfig, dialogSessionId]);
10184
+ const dialogFieldWidgetId = useCallback((sessionId, columnKey) => `${membersWidgetId}-dlg-${sessionId}-${columnKey}`, [membersWidgetId]);
9567
10185
  const resetDialogWidgets = useCallback((sessionId) => {
9568
10186
  if (sessionId <= 0)
9569
10187
  return;
9570
10188
  columns.forEach((col) => {
9571
- const wid = `${widgetConfig['widget-id']}-dlg-${sessionId}-${col['column-key']}`;
9572
- dispatch(resetWidget(wid));
10189
+ dispatch(resetWidget(dialogFieldWidgetId(sessionId, col['column-key'])));
10190
+ });
10191
+ }, [columns, dialogFieldWidgetId, dispatch]);
10192
+ const seedDialogReduxValues = useCallback((sessionId, rowData) => {
10193
+ const seeds = {};
10194
+ columns.forEach((col) => {
10195
+ const key = col['column-key'];
10196
+ if (rowData[key] !== undefined) {
10197
+ seeds[dialogFieldWidgetId(sessionId, key)] = rowData[key];
10198
+ }
9573
10199
  });
9574
- }, [columns, widgetConfig, dispatch]);
10200
+ if (Object.keys(seeds).length > 0) {
10201
+ dispatch(setValues(seeds));
10202
+ }
10203
+ }, [columns, dialogFieldWidgetId, dispatch]);
9575
10204
  const beginDialogSession = useCallback(() => {
9576
10205
  dialogSessionRef.current += 1;
9577
10206
  const nextSession = dialogSessionRef.current;
@@ -9580,15 +10209,21 @@ const DialogTableWidget = ({ config }) => {
9580
10209
  }, []);
9581
10210
  const openAddDialog = useCallback(() => {
9582
10211
  resetDialogWidgets(dialogSessionId);
9583
- beginDialogSession();
10212
+ const sessionId = beginDialogSession();
10213
+ seedDialogReduxValues(sessionId, buildEmptyRow());
9584
10214
  setDialogMode('add');
9585
10215
  setActiveRowIndex(null);
9586
- setFormData(buildEmptyRow());
9587
10216
  setDialogOpen(true);
9588
- }, [buildEmptyRow, beginDialogSession, resetDialogWidgets, dialogSessionId]);
10217
+ }, [
10218
+ buildEmptyRow,
10219
+ beginDialogSession,
10220
+ resetDialogWidgets,
10221
+ dialogSessionId,
10222
+ seedDialogReduxValues,
10223
+ ]);
9589
10224
  const openEditDialog = useCallback((rowIndex) => {
9590
10225
  resetDialogWidgets(dialogSessionId);
9591
- beginDialogSession();
10226
+ const sessionId = beginDialogSession();
9592
10227
  const row = rows[rowIndex] || {};
9593
10228
  const nextFormData = buildEmptyRow();
9594
10229
  columns.forEach((col) => {
@@ -9596,44 +10231,71 @@ const DialogTableWidget = ({ config }) => {
9596
10231
  if (row[key] !== undefined)
9597
10232
  nextFormData[key] = row[key];
9598
10233
  });
10234
+ seedDialogReduxValues(sessionId, nextFormData);
9599
10235
  setDialogMode('edit');
9600
10236
  setActiveRowIndex(rowIndex);
9601
- setFormData(nextFormData);
9602
10237
  setDialogOpen(true);
9603
- }, [rows, columns, buildEmptyRow, resetDialogWidgets, dialogSessionId, beginDialogSession]);
10238
+ }, [
10239
+ rows,
10240
+ columns,
10241
+ buildEmptyRow,
10242
+ resetDialogWidgets,
10243
+ dialogSessionId,
10244
+ beginDialogSession,
10245
+ seedDialogReduxValues,
10246
+ ]);
9604
10247
  const closeDialog = useCallback(() => {
9605
10248
  const sessionToClear = dialogSessionId;
9606
10249
  setDialogOpen(false);
9607
10250
  setActiveRowIndex(null);
9608
- setFormData({});
9609
10251
  resetDialogWidgets(sessionToClear);
9610
10252
  setDialogSessionId(0);
9611
10253
  }, [dialogSessionId, resetDialogWidgets]);
9612
- const updateField = useCallback((columnKey, newValue) => {
9613
- setFormData((prev) => ({ ...prev, [columnKey]: newValue }));
9614
- }, []);
9615
- const collectMergedRowPayload = useCallback(() => {
9616
- const merged = { ...formData };
10254
+ const dialogStoreValues = useSelector((state) => {
10255
+ if (dialogSessionId <= 0) {
10256
+ return {};
10257
+ }
10258
+ const values = state.widget?.values ?? {};
10259
+ const row = {};
9617
10260
  columns.forEach((col) => {
9618
10261
  const k = col['column-key'];
9619
- const wid = dialogFieldWidgetId(k);
9620
- const fromStore = storeValues[wid];
9621
- if (fromStore !== undefined)
9622
- merged[k] = fromStore;
10262
+ const wid = dialogFieldWidgetId(dialogSessionId, k);
10263
+ if (values[wid] !== undefined) {
10264
+ row[k] = values[wid];
10265
+ }
10266
+ });
10267
+ return row;
10268
+ });
10269
+ const dialogRowValues = dialogStoreValues;
10270
+ const collectMergedRowPayload = useCallback(() => dialogStoreValues, [dialogStoreValues]);
10271
+ const finalizeDialogRowPayload = useCallback((raw) => {
10272
+ const result = {};
10273
+ columns.forEach((col) => {
10274
+ const key = col['column-key'];
10275
+ if (!shouldShowWidget(col['widget-data-options'], raw)) {
10276
+ return;
10277
+ }
10278
+ const val = raw[key];
10279
+ if (!isUnsetRowValue(val)) {
10280
+ result[key] = val;
10281
+ }
9623
10282
  });
9624
- return merged;
9625
- }, [formData, columns, storeValues, dialogFieldWidgetId]);
10283
+ return result;
10284
+ }, [columns]);
9626
10285
  const saveDialog = useCallback(() => {
9627
10286
  const payload = collectMergedRowPayload();
9628
10287
  let hasErrors = false;
9629
10288
  columns.forEach((col) => {
9630
10289
  const key = col['column-key'];
9631
- const cellWidgetId = dialogFieldWidgetId(key);
10290
+ const cellWidgetId = dialogFieldWidgetId(dialogSessionId, key);
9632
10291
  const isColReadonly = isReadonly || col['widget-readonly'] === true;
9633
10292
  if (isColReadonly)
9634
10293
  return;
10294
+ if (!shouldShowWidget(col['widget-data-options'], payload))
10295
+ return;
9635
10296
  const cellValue = payload[key];
9636
- const validationErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
10297
+ const isRequired = shouldRequireWidget(col['widget-data-options'], payload, col['widget-required']);
10298
+ const validationErrors = validateWidget(cellValue, col['widget-data-validation'], isRequired);
9637
10299
  if (validationErrors && validationErrors.length > 0) {
9638
10300
  hasErrors = true;
9639
10301
  dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
@@ -9646,8 +10308,9 @@ const DialogTableWidget = ({ config }) => {
9646
10308
  if (hasErrors) {
9647
10309
  return;
9648
10310
  }
10311
+ const cleaned = finalizeDialogRowPayload(payload);
9649
10312
  if (dialogMode === 'add') {
9650
- const savedRow = { ...payload, edit_action: 'ADD' };
10313
+ const savedRow = { ...cleaned, edit_action: 'ADD' };
9651
10314
  onChange([...rows, savedRow]);
9652
10315
  closeDialog();
9653
10316
  return;
@@ -9657,15 +10320,43 @@ const DialogTableWidget = ({ config }) => {
9657
10320
  const currentRow = newRows[activeRowIndex] || {};
9658
10321
  const wasDeleted = currentRow.edit_action === 'DELETE';
9659
10322
  const editAction = wasDeleted ? 'UPDATE' : (currentRow.edit_action ?? 'UPDATE');
9660
- newRows[activeRowIndex] = { ...currentRow, ...payload, edit_action: editAction };
10323
+ const merged = { ...currentRow, ...cleaned, edit_action: editAction };
10324
+ columns.forEach((col) => {
10325
+ const key = col['column-key'];
10326
+ if (!(key in cleaned)) {
10327
+ delete merged[key];
10328
+ }
10329
+ });
10330
+ newRows[activeRowIndex] = merged;
9661
10331
  onChange(newRows);
9662
10332
  closeDialog();
9663
10333
  }
9664
- }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
10334
+ }, [
10335
+ collectMergedRowPayload,
10336
+ finalizeDialogRowPayload,
10337
+ dialogMode,
10338
+ onChange,
10339
+ rows,
10340
+ closeDialog,
10341
+ activeRowIndex,
10342
+ columns,
10343
+ dialogSessionId,
10344
+ dialogFieldWidgetId,
10345
+ isReadonly,
10346
+ dispatch,
10347
+ ]);
9665
10348
  const deleteRow = useCallback((rowIndex) => {
9666
- const newRows = rows.filter((_, i) => i !== rowIndex);
9667
- onChange(newRows);
9668
- }, [rows, onChange]);
10349
+ if (shouldSoftDeleteOnRemove) {
10350
+ const newRows = [...rows];
10351
+ newRows[rowIndex] = {
10352
+ ...newRows[rowIndex],
10353
+ edit_action: 'DELETE',
10354
+ };
10355
+ onChange(newRows);
10356
+ return;
10357
+ }
10358
+ onChange(rows.filter((_, i) => i !== rowIndex));
10359
+ }, [rows, onChange, shouldSoftDeleteOnRemove]);
9669
10360
  const getDisplayValue = useCallback((rowIndex, column) => {
9670
10361
  const key = column['column-key'];
9671
10362
  const cellValue = rows[rowIndex]?.[key];
@@ -9673,11 +10364,12 @@ const DialogTableWidget = ({ config }) => {
9673
10364
  if (cellValue === null || cellValue === undefined || cellValue === '')
9674
10365
  return '-';
9675
10366
  if (widgetType === 'select')
9676
- return null; // handled by SelectDisplayValue
10367
+ return null;
9677
10368
  if (column['widget-data-format'])
9678
10369
  return formatValue(cellValue, column['widget-data-format'], column.widget);
9679
10370
  return String(cellValue);
9680
10371
  }, [rows]);
10372
+ const visibleDialogColumns = useMemo(() => columns.filter((col) => shouldShowWidget(col['widget-data-options'], dialogRowValues)), [columns, dialogRowValues]);
9681
10373
  const tableWidgetId = `dialog-table-widget-${widgetConfig['widget-id']}`;
9682
10374
  const columnSpan = widgetConfig['widget-column-span'] || 2;
9683
10375
  const minWidth = columnSpan * 200;
@@ -9705,37 +10397,40 @@ const DialogTableWidget = ({ config }) => {
9705
10397
  }, children: translate('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
9706
10398
  borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
9707
10399
  borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
9708
- }, 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: {
9709
- borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
9710
- backgroundColor: row?.edit_action === 'DELETE'
9711
- ? 'var(--owt-widget-table-deleted-row-bg, #FEE2E2)'
9712
- : undefined,
9713
- }, children: [visibleColumns.map((col) => {
9714
- const key = col['column-key'];
9715
- const widgetType = col.widget || 'text';
9716
- const displayValue = getDisplayValue(rowIndex, col);
9717
- if (widgetType === 'select' && displayValue === null) {
9718
- const displayConfig = {
9719
- ...col,
9720
- 'widget-id': `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`,
9721
- 'widget-label': '',
9722
- 'widget-readonly': true,
9723
- 'widget-data-path': undefined,
9724
- };
9725
- 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));
9726
- }
9727
- return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", children: displayValue }) }, key));
9728
- }), ((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: {
9729
- borderRadius: 'var(--owt-btn-border-radius, 10px)',
9730
- color: 'var(--owt-color-primary-dark, #F07B1A)',
9731
- backgroundColor: 'transparent',
9732
- border: 'none',
9733
- }, 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: {
9734
- borderRadius: 'var(--owt-btn-border-radius, 10px)',
9735
- color: 'var(--owt-color-error, #B91C1C)',
9736
- backgroundColor: 'transparent',
9737
- border: 'none',
9738
- }, 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: {
10400
+ }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translateConfig(col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: translate('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
10401
+ const cellStyle = getRowCellStyle(row?.edit_action);
10402
+ return (jsxRuntimeExports.jsxs("tr", { style: {
10403
+ borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
10404
+ backgroundColor: row?.edit_action === 'DELETE'
10405
+ ? 'var(--owt-widget-table-deleted-row-bg, #FEE2E2)'
10406
+ : undefined,
10407
+ }, children: [visibleColumns.map((col) => {
10408
+ const key = col['column-key'];
10409
+ const widgetType = col.widget || 'text';
10410
+ const displayValue = getDisplayValue(rowIndex, col);
10411
+ if (widgetType === 'select' && displayValue === null) {
10412
+ const displayConfig = {
10413
+ ...col,
10414
+ 'widget-id': `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`,
10415
+ 'widget-label': '',
10416
+ 'widget-readonly': true,
10417
+ 'widget-data-path': undefined,
10418
+ };
10419
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: displayConfig, value: row?.[key] }) }) }, key));
10420
+ }
10421
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: displayValue }) }, key));
10422
+ }), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [operations.edit && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => openEditDialog(rowIndex), disabled: !isEnabled, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
10423
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
10424
+ color: 'var(--owt-color-primary-dark, #F07B1A)',
10425
+ backgroundColor: 'transparent',
10426
+ border: 'none',
10427
+ }, children: translate('common.edit') || 'Edit' })), operations.remove && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => deleteRow(rowIndex), disabled: !isEnabled, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
10428
+ borderRadius: 'var(--owt-btn-border-radius, 10px)',
10429
+ color: 'var(--owt-color-error, #B91C1C)',
10430
+ backgroundColor: 'transparent',
10431
+ border: 'none',
10432
+ }, children: translate('common.remove') || 'Delete' }))] }) }))] }, rowIndex));
10433
+ })] })] }) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-sm mt-1", style: { color: 'var(--owt-widget-error-color, #B91C1C)' }, children: error[0] }))] }), dialogOpen && (jsxRuntimeExports.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, children: jsxRuntimeExports.jsxs("div", { className: "rounded-lg p-6 w-full mx-4", style: {
9739
10434
  maxWidth: '900px',
9740
10435
  backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
9741
10436
  borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
@@ -9746,22 +10441,10 @@ const DialogTableWidget = ({ config }) => {
9746
10441
  cursor: 'pointer',
9747
10442
  fontSize: '20px',
9748
10443
  lineHeight: 1,
9749
- }, "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) => {
10444
+ }, "aria-label": "Close", children: "\u00D7" })] }), jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", style: { maxHeight: '70vh', overflow: 'auto' }, children: visibleDialogColumns.map((col) => {
9750
10445
  const key = col['column-key'];
9751
- const widgetType = col.widget || 'text';
9752
- const cellWidgetId = dialogFieldWidgetId(key);
9753
- const initialValue = formData[key] ?? col['widget-data-default'] ?? '';
9754
- const fieldConfig = {
9755
- ...col,
9756
- widget: widgetType,
9757
- 'widget-type': col['widget-type'] || 'input',
9758
- 'widget-id': cellWidgetId,
9759
- 'widget-label': col['widget-label'],
9760
- 'widget-readonly': isReadonly || col['widget-readonly'] === true,
9761
- 'widget-data-path': undefined,
9762
- 'widget-data-default': initialValue,
9763
- };
9764
- return (jsxRuntimeExports.jsx("div", { className: "min-w-0", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: fieldConfig, schemaData: { [cellWidgetId]: initialValue }, onValueChange: (_widgetId, newValue) => updateField(key, newValue) }) }, `${dialogSessionId}-${key}`));
10446
+ const cellWidgetId = dialogFieldWidgetId(dialogSessionId, key);
10447
+ return (jsxRuntimeExports.jsx(DialogTableField, { col: col, cellWidgetId: cellWidgetId, dialogRowValues: dialogRowValues, isReadonly: isReadonly }, `${dialogSessionId}-${key}`));
9765
10448
  }) }, `dialog-fields-${dialogSessionId}`), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3 mt-6", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: closeDialog, className: "px-4 py-2 text-sm font-medium", style: {
9766
10449
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
9767
10450
  border: '1px solid var(--owt-btn-secondary-border, #C4C4C4)',
@@ -9951,13 +10634,13 @@ const ProfileWidget = ({ config }) => {
9951
10634
  if (placeholder) {
9952
10635
  placeholder.style.display = 'flex';
9953
10636
  }
9954
- } })) : 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 })] }))] })] })] }));
10637
+ } })) : null, jsxRuntimeExports.jsx("div", { className: "profile-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$h, alt: "Profile Placeholder" }) })] }), jsxRuntimeExports.jsxs("div", { className: "profile-info", children: [displayName && (jsxRuntimeExports.jsx("div", { className: "profile-name", children: displayName })), idValue && (jsxRuntimeExports.jsxs("div", { className: "profile-id", children: [showIdLabel && (jsxRuntimeExports.jsx("span", { className: "profile-id-label", children: "ID :" })), jsxRuntimeExports.jsx("span", { className: "profile-id-value", children: idValue })] }))] })] })] }));
9955
10638
  };
9956
10639
 
9957
10640
  const TextAreaWidget = ({ config }) => {
9958
10641
  // Check readonly early from original config
9959
10642
  const isReadonly = config['widget-readonly'] || false;
9960
- const { value, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
10643
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
9961
10644
  const { translate, translateConfig } = useWidgetTranslation();
9962
10645
  const formatConfig = widgetConfig['widget-data-format'] || {};
9963
10646
  const validationConfig = widgetConfig['widget-data-validation'] || {};
@@ -10005,7 +10688,6 @@ const TextAreaWidget = ({ config }) => {
10005
10688
  ? translateConfig(widgetConfig['widget-label'])
10006
10689
  : '';
10007
10690
  // Check if required
10008
- const isRequired = widgetConfig['widget-required'] || false;
10009
10691
  // Error display
10010
10692
  const hasError = touched && error && error.length > 0;
10011
10693
  const errorMessage = hasError ? error[0] : '';
@@ -10023,7 +10705,7 @@ const TextAreaWidget = ({ config }) => {
10023
10705
  border: 'none',
10024
10706
  }, children: displayValue }) })] }));
10025
10707
  }
10026
- 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
10708
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: label, required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { style: { position: 'relative' }, children: [jsxRuntimeExports.jsx("textarea", { id: widgetConfig['widget-id'], rows: rows, value: getStringValue(), onChange: handleChange, onBlur: onBlur, disabled: !isEnabled, placeholder: placeholder, className: `w-full px-3 py-2 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
10027
10709
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
10028
10710
  : 'border-gray-300'} ${!isEnabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: {
10029
10711
  borderRadius: '10px',
@@ -10169,17 +10851,16 @@ const HeaderSectionWidget = ({ config }) => {
10169
10851
  result = searchIn(schemaData);
10170
10852
  return result;
10171
10853
  }, [paths, values, schemaData]);
10172
- const imageVal = findValue('image');
10173
10854
  const imageUrlVal = findValue('imageUrl');
10174
10855
  const [previewUrl, setPreviewUrl] = useState(null);
10175
10856
  useEffect(() => {
10176
- if (imageVal instanceof File) {
10177
- const url = URL.createObjectURL(imageVal);
10857
+ if (imageUrlVal instanceof File) {
10858
+ const url = URL.createObjectURL(imageUrlVal);
10178
10859
  setPreviewUrl(url);
10179
10860
  return () => URL.revokeObjectURL(url);
10180
10861
  }
10181
10862
  setPreviewUrl(null);
10182
- }, [imageVal]);
10863
+ }, [imageUrlVal]);
10183
10864
  const displayImageUrl = previewUrl || (typeof imageUrlVal === 'string' && imageUrlVal ? imageUrlVal : null);
10184
10865
  const displayName = findValue('name') || '';
10185
10866
  const functionalId = findValue('functionalId') || '';
@@ -10298,14 +10979,16 @@ const HeaderSectionWidget = ({ config }) => {
10298
10979
  const fileInputRef = useRef(null);
10299
10980
  const handleImageUpload = useCallback((e) => {
10300
10981
  const file = e.target.files?.[0];
10301
- if (!file)
10982
+ if (!file || !paths.imageUrl)
10302
10983
  return;
10303
- updateFieldValue('image', file);
10984
+ dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, file)));
10304
10985
  e.target.value = '';
10305
- }, [updateFieldValue]);
10986
+ }, [paths.imageUrl, values, dispatch]);
10306
10987
  const handleImageDelete = useCallback(() => {
10307
- updateFieldValue('image', '');
10308
- }, [updateFieldValue]);
10988
+ if (!paths.imageUrl)
10989
+ return;
10990
+ dispatch(setValues(setValueByPath({ ...values }, paths.imageUrl, null)));
10991
+ }, [paths.imageUrl, values, dispatch]);
10309
10992
  // ── Scoped class for CSS isolation ────────────────────────────
10310
10993
  const cls = `header-section-widget-${widgetConfig['widget-id']}`;
10311
10994
  // ── RENDER ────────────────────────────────────────────────────
@@ -10614,7 +11297,7 @@ const HeaderSectionWidget = ({ config }) => {
10614
11297
  .parentElement?.querySelector('.hdr-avatar-placeholder');
10615
11298
  if (placeholder)
10616
11299
  placeholder.style.display = 'flex';
10617
- } })) : 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: () => {
11300
+ } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$h, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", title: `${getLabel('functionalId')} :`, children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: functionalId || '-', children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", title: getLabel('status'), children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, title: statusLabel, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: "-", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: getLabel('select') }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", title: `${getLabel('statusReason')} :`, children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: statusReason || '-', children: statusReason || '-' })) : (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [jsxRuntimeExports.jsx("input", { type: "text", className: `hdr-input ${(!isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing) ? 'hdr-input--error' : ''}`, value: statusReason, placeholder: getLabel('enterReason'), required: isStatusChanged, "aria-required": isStatusChanged, "aria-invalid": !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing, onBlur: () => {
10618
11301
  if (isReasonMissing)
10619
11302
  setShowReasonRequired(true);
10620
11303
  }, onChange: (e) => {
@@ -11359,6 +12042,621 @@ const IdAuthenticationWidget = ({ config, schemaData: propSchemaData }) => {
11359
12042
  }, children: "Close" })] }), jsxRuntimeExports.jsx("iframe", { className: "overlay-iframe", src: overlayUrl, title: "Authentication" })] }) })) : null, jsxRuntimeExports.jsx("div", { className: "auth-content", children: jsxRuntimeExports.jsxs("div", { className: "auth-grid", children: [jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Foundational ID:" }), jsxRuntimeExports.jsx("div", { className: "auth-value auth-value--foundational", children: displayText(foundationalId) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authenticated on:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDateTime(lastAuthenticatedOn) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Expiry date:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: tryFormatDate(expiryDate) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Last authentication status:" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsxs("div", { className: "auth-status", "aria-label": `Authentication status: ${statusLabel}`, children: [jsxRuntimeExports.jsx("span", { className: "auth-dot" }), jsxRuntimeExports.jsx("span", { children: statusLabel })] }) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", children: "Authentication token (PSUT):" }), jsxRuntimeExports.jsx("div", { className: "auth-value", children: jsxRuntimeExports.jsx("div", { className: "auth-token", children: psut ? String(psut) : '-' }) })] }), jsxRuntimeExports.jsxs("div", { className: "auth-cell auth-cell--action", children: [jsxRuntimeExports.jsx("div", { className: "auth-label", "aria-hidden": true }), jsxRuntimeExports.jsxs("div", { className: "auth-value", children: [jsxRuntimeExports.jsx("button", { type: "button", className: "auth-button", onClick: onAuthenticate, disabled: buttonDisabled, children: buttonBusy ? 'Loading…' : 'Authenticate' }), authError ? (jsxRuntimeExports.jsx("div", { className: "auth-error", style: { marginTop: 8 }, children: authError })) : null] })] })] }) })] })] }));
11360
12043
  };
11361
12044
 
12045
+ /**
12046
+ * Register lookup widget — searchable popup to select a record from any register.
12047
+ * Reads ID from widget-data-path, finds matching register row by internal_record_id, shows display fields.
12048
+ *
12049
+ * Example (individual → household link):
12050
+ *
12051
+ * {
12052
+ * "widget": "register-lookup",
12053
+ * "widget-id": "link_internal_record_id",
12054
+ * "widget-type": "input",
12055
+ * "widget-label": "Household",
12056
+ * "widget-required": true,
12057
+ * "widget-data-path": "<section_register_ids>.link_internal_record_id",
12058
+ * "widget-data-source": {
12059
+ * "type": "api",
12060
+ * "method": "POST",
12061
+ * "params": {
12062
+ * "register_id": "<target_register_id>"
12063
+ * },
12064
+ * "service": "register",
12065
+ * "endpoint": "records"
12066
+ * },
12067
+ * "widget-lookup-config": {
12068
+ * "page_size": 10,
12069
+ * "action_label": "Click to Search Household",
12070
+ * "search_placeholder": "Search by name or ID...",
12071
+ * "select_record_label": "Select Household"
12072
+ * }
12073
+ * }
12074
+ */
12075
+ const normalizeDisplayFields = (row) => {
12076
+ if (!Array.isArray(row.display_fields))
12077
+ return [];
12078
+ return [...row.display_fields]
12079
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
12080
+ .map((f) => ({
12081
+ label: String(f.field_name ?? ''),
12082
+ value: f.value !== null && f.value !== undefined ? String(f.value) : '-',
12083
+ }));
12084
+ };
12085
+ const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
12086
+ const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
12087
+ const totalPages = typeof pagination.number_of_pages === 'number'
12088
+ ? Math.max(1, pagination.number_of_pages)
12089
+ : totalItems > 0
12090
+ ? Math.max(1, Math.ceil(totalItems / size))
12091
+ : 1;
12092
+ return { totalItems, totalPages, currentPage: pagination.current_page ?? fallbackPage };
12093
+ };
12094
+ const RecordDisplayPanel = ({ row, widgetIdPrefix, className = '', }) => {
12095
+ const { translateConfig } = useWidgetTranslation();
12096
+ const columnFields = normalizeDisplayFields(row).filter((f) => f.label !== 'record_name' && f.label !== 'functional_record_id' && f.label !== 'internal_record_id');
12097
+ const fieldSlot = (widgetId, label, value) => (jsxRuntimeExports.jsx("div", { className: "min-w-0 overflow-hidden", children: jsxRuntimeExports.jsx(WidgetRenderer, { config: {
12098
+ widget: 'display',
12099
+ 'widget-type': 'input',
12100
+ 'widget-id': widgetId,
12101
+ 'widget-label': label,
12102
+ 'widget-readonly': true,
12103
+ 'widget-data-default': value,
12104
+ }, schemaData: { [widgetId]: value } }) }, widgetId));
12105
+ const optionalSlot = (field, slot) => field
12106
+ ? fieldSlot(`${widgetIdPrefix}-${field.label}`, translateConfig(field.label), field.value)
12107
+ : jsxRuntimeExports.jsx("div", { className: "mb-[10px] invisible text-base", children: "\u00A0" }, slot);
12108
+ const column = (showDivider, isFirst, children) => (jsxRuntimeExports.jsxs("div", { className: "relative min-w-0 overflow-hidden", style: { paddingRight: showDivider ? '40px' : undefined, paddingLeft: isFirst ? undefined : '40px' }, children: [children, showDivider && (jsxRuntimeExports.jsx("div", { className: "absolute right-0 top-0 bottom-[5px] w-px", style: { backgroundColor: 'var(--owt-panel-divider-color, #C4C4C4)' } }))] }));
12109
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
12110
+ .register-lookup-record-panel .DisplayFieldWidget,
12111
+ .register-lookup-record-panel .widget-container {
12112
+ min-width: 0 !important;
12113
+ overflow: hidden !important;
12114
+ }
12115
+ .register-lookup-record-panel .DisplayFieldWidget > .flex-1 {
12116
+ min-width: 0 !important;
12117
+ overflow: hidden !important;
12118
+ }
12119
+ .register-lookup-record-panel .DisplayFieldWidget > .text-base.text-gray-600 {
12120
+ width: 50% !important;
12121
+ min-width: 50% !important;
12122
+ max-width: 50% !important;
12123
+ flex-shrink: 0 !important;
12124
+ overflow: hidden !important;
12125
+ text-overflow: ellipsis !important;
12126
+ white-space: nowrap !important;
12127
+ }
12128
+ .register-lookup-record-panel .DisplayFieldWidget > .flex-1 > .text-gray-900 {
12129
+ overflow: hidden;
12130
+ text-overflow: ellipsis;
12131
+ white-space: nowrap;
12132
+ }
12133
+ ` }), jsxRuntimeExports.jsxs("div", { className: `register-lookup-record-panel grid w-full min-w-0 ${className}`, style: { gridTemplateColumns: 'repeat(3, minmax(200px, 1fr))' }, children: [column(true, true, (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [fieldSlot(`${widgetIdPrefix}-record_name`, translateConfig('record_name'), row.record_name == null || row.record_name === '' ? '-' : String(row.record_name)), fieldSlot(`${widgetIdPrefix}-functional_record_id`, translateConfig('functional_record_id'), row.functional_record_id == null || row.functional_record_id === '' ? '-' : String(row.functional_record_id))] }))), column(true, false, [0, 1, 2].map((i) => optionalSlot(columnFields[i], `mid-${i}`))), column(false, false, [0, 1, 2].map((i) => optionalSlot(columnFields[i + 3], `right-${i}`)))] })] }));
12134
+ };
12135
+ const PaginationFooter = ({ currentPage, totalPages, totalCount, pageSize, onPageChange, onPrev, onNext, translate, embedded, }) => {
12136
+ const [pageInput, setPageInput] = useState(String(currentPage));
12137
+ useEffect(() => {
12138
+ setPageInput(String(currentPage));
12139
+ }, [currentPage]);
12140
+ const pageStart = totalCount === 0 ? 0 : (currentPage - 1) * pageSize + 1;
12141
+ const pageEnd = totalCount === 0 ? 0 : Math.min(currentPage * pageSize, totalCount);
12142
+ const commitPage = () => {
12143
+ const parsed = parseInt(pageInput, 10);
12144
+ if (!Number.isFinite(parsed)) {
12145
+ setPageInput(String(currentPage));
12146
+ return;
12147
+ }
12148
+ const page = Math.min(Math.max(1, parsed), totalPages);
12149
+ setPageInput(String(page));
12150
+ if (page !== currentPage)
12151
+ onPageChange(page);
12152
+ };
12153
+ return (jsxRuntimeExports.jsxs("div", { className: embedded
12154
+ ? 'flex flex-wrap items-center gap-3 flex-1 min-w-0'
12155
+ : 'flex flex-wrap items-center justify-between gap-3 px-5 py-3 flex-shrink-0 border-t border-gray-200', children: [jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-600", children: [totalCount === 1
12156
+ ? translate('common.record', { count: totalCount, defaultValue: `${totalCount} record` })
12157
+ : translate('common.records', { count: totalCount, defaultValue: `${totalCount} records` }), totalCount > 0 && ` · ${pageStart}-${pageEnd}`] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: onPrev, disabled: currentPage <= 1, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: translate('common.previous', { defaultValue: 'Prev' }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-sm text-gray-600", children: [jsxRuntimeExports.jsx("span", { children: translate('common.page', { defaultValue: 'Page' }) }), jsxRuntimeExports.jsx("input", { type: "text", inputMode: "numeric", value: pageInput, onChange: (e) => setPageInput(e.target.value.replace(/\D/g, '')), onKeyDown: (e) => {
12158
+ if (e.key === 'Enter') {
12159
+ e.preventDefault();
12160
+ commitPage();
12161
+ }
12162
+ }, onBlur: commitPage, className: "w-10 h-8 text-center text-sm text-gray-900 outline-none rounded-[10px] border border-gray-300 bg-white", "aria-label": translate('common.pageNumber', { defaultValue: 'Page number' }) }), jsxRuntimeExports.jsx("span", { children: translate('common.ofPages', { total: totalPages, defaultValue: `of ${totalPages}` }) })] }), jsxRuntimeExports.jsx("button", { type: "button", onClick: onNext, disabled: currentPage >= totalPages, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: translate('common.next', { defaultValue: 'Next' }) })] })] }));
12163
+ };
12164
+ const ResultsTable = ({ rows, selectedRowKey, onRowClick, onRowDoubleClick, }) => {
12165
+ const { translateConfig } = useWidgetTranslation();
12166
+ const columns = rows.length === 0
12167
+ ? []
12168
+ : [
12169
+ { key: 'record_name', header: 'record_name' },
12170
+ ...normalizeDisplayFields(rows[0]).map((f) => ({ key: f.label, header: f.label })),
12171
+ ];
12172
+ return (jsxRuntimeExports.jsx("div", { className: "overflow-auto h-full", children: jsxRuntimeExports.jsxs("table", { className: "w-full text-sm border-collapse", children: [jsxRuntimeExports.jsx("thead", { className: "sticky top-0 z-[1] bg-gray-50", children: jsxRuntimeExports.jsx("tr", { children: columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "text-left px-4 py-2 text-sm font-medium text-gray-600 whitespace-nowrap border-b border-gray-200 bg-gray-50", children: translateConfig(col.header) }, col.key))) }) }), jsxRuntimeExports.jsx("tbody", { children: rows.map((row, idx) => {
12173
+ const tableRowKey = row.internal_record_id ?? idx;
12174
+ const isSelected = selectedRowKey != null && tableRowKey === selectedRowKey;
12175
+ return (jsxRuntimeExports.jsx("tr", { onClick: () => onRowClick(row), onDoubleClick: () => onRowDoubleClick?.(row), className: `cursor-pointer border-b border-gray-100 transition-colors ${isSelected ? 'bg-blue-100' : 'hover:bg-blue-50'}`, children: columns.map((col) => {
12176
+ const cellValue = col.key === 'record_name'
12177
+ ? row.record_name != null
12178
+ ? String(row.record_name)
12179
+ : '-'
12180
+ : normalizeDisplayFields(row).find((f) => f.label === col.key)?.value ?? '-';
12181
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: cellValue }, col.key));
12182
+ }) }, tableRowKey));
12183
+ }) })] }) }));
12184
+ };
12185
+ const RegisterLookupWidget = ({ config }) => {
12186
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
12187
+ const { translate, translateConfig } = useWidgetTranslation();
12188
+ const { dataSourceRequestHandler } = useWidgetContext();
12189
+ const dataSource = widgetConfig['widget-data-source'];
12190
+ const lookupConfig = widgetConfig['widget-lookup-config'];
12191
+ const pageSize = lookupConfig?.page_size ?? 10;
12192
+ const widgetIdPrefix = `${widgetConfig['widget-id'] || 'register-lookup'}-record`;
12193
+ const [isOpen, setIsOpen] = useState(false);
12194
+ const [searchText, setSearchText] = useState('');
12195
+ const [searchResults, setSearchResults] = useState([]);
12196
+ const [currentPage, setCurrentPage] = useState(1);
12197
+ const [totalPages, setTotalPages] = useState(1);
12198
+ const [totalCount, setTotalCount] = useState(null);
12199
+ const [pendingRow, setPendingRow] = useState(null);
12200
+ const [appliedRecord, setAppliedRecord] = useState(null);
12201
+ const [isHydrating, setIsHydrating] = useState(false);
12202
+ const [modalPos, setModalPos] = useState({ x: 80, y: 80 });
12203
+ const [modalSize, setModalSize] = useState({ w: 860, h: 520 });
12204
+ const isDragging = useRef(false);
12205
+ const dragOrigin = useRef({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
12206
+ const searchInputRef = useRef(null);
12207
+ const hydratedValueRef = useRef(null);
12208
+ const fetchRecords = useCallback(async (text, page, size) => {
12209
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler) {
12210
+ return { rows: [], pagination: {} };
12211
+ }
12212
+ const result = await dataSourceRequestHandler(dataSource.service, dataSource.endpoint, dataSource.method, {
12213
+ ...(dataSource.params || {}),
12214
+ search_text: text,
12215
+ current_page: page,
12216
+ page_size: size,
12217
+ }, { headers: dataSource.headers });
12218
+ return {
12219
+ rows: (result?.records ?? []),
12220
+ pagination: (result?.pagination ?? {}),
12221
+ };
12222
+ }, [dataSource, dataSourceRequestHandler]);
12223
+ const findRecordByValue = useCallback(async (recordValue) => {
12224
+ const target = String(recordValue).trim();
12225
+ if (!target)
12226
+ return null;
12227
+ const hydratePageSize = lookupConfig?.hydrate_page_size ?? 50;
12228
+ let page = 1;
12229
+ let totalPages = 1;
12230
+ while (page <= totalPages) {
12231
+ const { rows, pagination } = await fetchRecords('', page, hydratePageSize);
12232
+ const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
12233
+ if (match)
12234
+ return match;
12235
+ totalPages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
12236
+ if (page >= totalPages)
12237
+ break;
12238
+ page += 1;
12239
+ }
12240
+ return null;
12241
+ }, [fetchRecords, lookupConfig?.hydrate_page_size]);
12242
+ const runSearch = useCallback(async (text, page = 1) => {
12243
+ try {
12244
+ const { rows, pagination } = await fetchRecords(text, page, pageSize);
12245
+ const parsed = parsePagination(pagination, rows.length, pageSize, page);
12246
+ setSearchResults(rows);
12247
+ setTotalCount(parsed.totalItems);
12248
+ setTotalPages(parsed.totalPages);
12249
+ setCurrentPage(parsed.currentPage);
12250
+ }
12251
+ catch {
12252
+ setSearchResults([]);
12253
+ setTotalCount(null);
12254
+ setTotalPages(1);
12255
+ setCurrentPage(1);
12256
+ }
12257
+ }, [fetchRecords, pageSize]);
12258
+ useEffect(() => {
12259
+ const onMove = (e) => {
12260
+ if (!isDragging.current)
12261
+ return;
12262
+ setModalPos({
12263
+ x: dragOrigin.current.posX + (e.clientX - dragOrigin.current.mouseX),
12264
+ y: dragOrigin.current.posY + (e.clientY - dragOrigin.current.mouseY),
12265
+ });
12266
+ };
12267
+ const onUp = () => { isDragging.current = false; };
12268
+ document.addEventListener('mousemove', onMove);
12269
+ document.addEventListener('mouseup', onUp);
12270
+ return () => {
12271
+ document.removeEventListener('mousemove', onMove);
12272
+ document.removeEventListener('mouseup', onUp);
12273
+ };
12274
+ }, []);
12275
+ const hasValue = value !== null && value !== undefined && value !== '';
12276
+ const applySelection = (row) => {
12277
+ hydratedValueRef.current = row.internal_record_id;
12278
+ onChange(row.internal_record_id);
12279
+ setAppliedRecord(row);
12280
+ setIsOpen(false);
12281
+ };
12282
+ const openLookup = () => {
12283
+ const w = Math.min(Math.round(window.innerWidth * 0.82), 940);
12284
+ const h = Math.min(Math.round(window.innerHeight * 0.72), 560);
12285
+ setModalPos({ x: Math.round((window.innerWidth - w) / 2), y: Math.round((window.innerHeight - h) / 2) });
12286
+ setModalSize({ w, h });
12287
+ setIsOpen(true);
12288
+ setSearchText('');
12289
+ setSearchResults([]);
12290
+ setTotalCount(null);
12291
+ setCurrentPage(1);
12292
+ setTotalPages(1);
12293
+ setPendingRow(appliedRecord);
12294
+ setTimeout(() => searchInputRef.current?.focus(), 50);
12295
+ runSearch('', 1);
12296
+ };
12297
+ useEffect(() => {
12298
+ if (!hasValue) {
12299
+ hydratedValueRef.current = null;
12300
+ setAppliedRecord(null);
12301
+ setIsHydrating(false);
12302
+ return;
12303
+ }
12304
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler)
12305
+ return;
12306
+ if (hydratedValueRef.current === value)
12307
+ return;
12308
+ let cancelled = false;
12309
+ setIsHydrating(true);
12310
+ setAppliedRecord(null);
12311
+ (async () => {
12312
+ try {
12313
+ const match = await findRecordByValue(value);
12314
+ if (cancelled)
12315
+ return;
12316
+ hydratedValueRef.current = value;
12317
+ setAppliedRecord(match);
12318
+ }
12319
+ catch {
12320
+ if (!cancelled) {
12321
+ hydratedValueRef.current = value;
12322
+ setAppliedRecord(null);
12323
+ }
12324
+ }
12325
+ finally {
12326
+ if (!cancelled)
12327
+ setIsHydrating(false);
12328
+ }
12329
+ })();
12330
+ return () => {
12331
+ cancelled = true;
12332
+ setIsHydrating(false);
12333
+ };
12334
+ }, [hasValue, value, dataSource, dataSourceRequestHandler, findRecordByValue]);
12335
+ const isReadonly = !!widgetConfig['widget-readonly'];
12336
+ const label = translateConfig(widgetConfig['widget-label']);
12337
+ const hasError = (touched && error.length > 0) ||
12338
+ (widgetConfig['widget-required'] && !hasValue);
12339
+ const actionLabel = translateConfig(String(lookupConfig?.action_label ?? `Select ${label}`));
12340
+ const searchPlaceholder = translateConfig(String(lookupConfig?.search_placeholder ?? 'Search...'));
12341
+ const selectRecordLabel = translateConfig(String(lookupConfig?.select_record_label ?? `Select ${label}`));
12342
+ const hydratedPanel = isHydrating ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading', { defaultValue: 'Loading...' }) })) : appliedRecord ? (jsxRuntimeExports.jsx(RecordDisplayPanel, { row: appliedRecord, widgetIdPrefix: `${widgetIdPrefix}-${isReadonly ? 'readonly' : 'applied'}` })) : null;
12343
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] w-full", children: [hasValue ? (jsxRuntimeExports.jsxs("div", { className: "w-full", children: [hydratedPanel, !isReadonly && isEnabled && (jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: openLookup, className: "text-sm underline p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 rounded", style: { color: 'var(--owt-color-info, #2563eb)' }, children: translate('common.change', { defaultValue: 'Change' }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: (e) => {
12344
+ e.stopPropagation();
12345
+ hydratedValueRef.current = null;
12346
+ onChange(null);
12347
+ setAppliedRecord(null);
12348
+ setPendingRow(null);
12349
+ }, className: "text-sm underline text-red-500 p-0 border-0 bg-transparent cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1 rounded", children: translate('common.remove', { defaultValue: 'Remove' }) })] })), !isReadonly && touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : !isReadonly ? (jsxRuntimeExports.jsxs("div", { className: "w-full min-w-0", children: [jsxRuntimeExports.jsxs("button", { type: "button", disabled: !isEnabled, onClick: openLookup, title: actionLabel, className: `flex items-center gap-2 w-full sm:w-[180px] max-w-full px-3 h-[30px] text-sm border rounded-[10px] shadow-sm transition-colors ${hasError ? 'border-red-500 text-gray-700' : 'border-gray-300 text-gray-700'} ${!isEnabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white cursor-pointer'}`, children: [jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-50 flex-shrink-0" }), jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: actionLabel }), isRequired && jsxRuntimeExports.jsx("span", { className: "shrink-0 text-red-500", children: "*" })] }), touched && error.length > 0 && jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })] })) : null, !isReadonly && isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => { setIsOpen(false); onBlur(); } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
12350
+ position: 'fixed',
12351
+ top: modalPos.y,
12352
+ left: modalPos.x,
12353
+ width: modalSize.w,
12354
+ height: modalSize.h,
12355
+ zIndex: 51,
12356
+ resize: 'both',
12357
+ minWidth: 340,
12358
+ minHeight: 260,
12359
+ maxWidth: '96vw',
12360
+ maxHeight: '92vh',
12361
+ backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
12362
+ borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
12363
+ boxShadow: '0 24px 64px rgba(0,0,0,0.28)',
12364
+ }, onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsxs("div", { onMouseDown: (e) => {
12365
+ e.preventDefault();
12366
+ isDragging.current = true;
12367
+ dragOrigin.current = { mouseX: e.clientX, mouseY: e.clientY, posX: modalPos.x, posY: modalPos.y };
12368
+ }, className: "flex items-center justify-between px-5 py-4 flex-shrink-0 select-none border-b border-gray-200 cursor-grab", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: translate('common.selectTitle', { label, defaultValue: `Select ${label}` }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => { setIsOpen(false); onBlur(); }, onMouseDown: (e) => e.stopPropagation(), className: "p-0 border-0 bg-transparent cursor-pointer", "aria-label": translate('common.close', { defaultValue: 'Close' }), children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "", className: "w-5 h-5 opacity-60" }) })] }), jsxRuntimeExports.jsx("div", { className: "px-5 py-3 flex-shrink-0 border-b border-gray-200", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-3 h-[30px] border border-gray-300 rounded-[10px] bg-white", children: [jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", value: searchText, onChange: (e) => setSearchText(e.target.value), onKeyDown: (e) => {
12369
+ if (e.key === 'Enter') {
12370
+ e.preventDefault();
12371
+ runSearch(searchText, 1);
12372
+ }
12373
+ }, placeholder: searchPlaceholder, className: "flex-1 outline-none text-sm text-gray-900 bg-transparent" }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => runSearch(searchText, 1), "aria-label": translate('common.search', { defaultValue: 'Search' }), className: "flex-shrink-0 p-0 border-0 bg-transparent cursor-pointer", children: jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-40" }) })] }) }), jsxRuntimeExports.jsx("div", { className: "overflow-auto flex-1", children: searchResults.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-center text-sm text-gray-500 py-10", children: searchText
12374
+ ? translate('common.noResults', { defaultValue: 'No results found' })
12375
+ : translate('common.searchHint', { defaultValue: 'Type and press Enter or click search' }) })) : (jsxRuntimeExports.jsx(ResultsTable, { rows: searchResults, selectedRowKey: pendingRow?.internal_record_id ?? null, onRowClick: setPendingRow, onRowDoubleClick: applySelection })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsx(PaginationFooter, { embedded: true, currentPage: currentPage, totalPages: totalPages, totalCount: totalCount, pageSize: pageSize, onPageChange: (page) => runSearch(searchText, page), onPrev: () => currentPage > 1 && runSearch(searchText, currentPage - 1), onNext: () => currentPage < totalPages && runSearch(searchText, currentPage + 1), translate: translate })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] }))] }));
12376
+ };
12377
+
12378
+ const MultiSelectWidget = ({ config }) => {
12379
+ const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
12380
+ const { translate, translateConfig } = useWidgetTranslation();
12381
+ const [isOpen, setIsOpen] = useState(false);
12382
+ const [isListPopupOpen, setIsListPopupOpen] = useState(false);
12383
+ const [searchQuery, setSearchQuery] = useState('');
12384
+ const [dropdownPosition, setDropdownPosition] = useState(null);
12385
+ const [listPopupPosition, setListPopupPosition] = useState(null);
12386
+ const [mounted, setMounted] = useState(false);
12387
+ const containerRef = useRef(null);
12388
+ const triggerRef = useRef(null);
12389
+ const dropdownRef = useRef(null);
12390
+ const listPopupRef = useRef(null);
12391
+ const moreButtonRef = useRef(null);
12392
+ const searchInputRef = useRef(null);
12393
+ const formatConfig = widgetConfig['widget-data-format'];
12394
+ const sortOptions = formatConfig?.sortOptions ?? false;
12395
+ useEffect(() => {
12396
+ setMounted(true);
12397
+ }, []);
12398
+ const updateDropdownPosition = useCallback(() => {
12399
+ const trigger = triggerRef.current;
12400
+ if (!trigger)
12401
+ return;
12402
+ const rect = trigger.getBoundingClientRect();
12403
+ const gap = 4;
12404
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
12405
+ const spaceAbove = rect.top - gap;
12406
+ const openDown = spaceBelow >= 160 || spaceBelow >= spaceAbove;
12407
+ const availableSpace = openDown ? spaceBelow : spaceAbove;
12408
+ const maxHeight = Math.min(320, Math.max(160, availableSpace - 8));
12409
+ setDropdownPosition(openDown
12410
+ ? {
12411
+ top: rect.bottom + gap,
12412
+ left: rect.left,
12413
+ width: rect.width,
12414
+ maxHeight,
12415
+ placement: 'bottom',
12416
+ }
12417
+ : {
12418
+ bottom: window.innerHeight - rect.top + gap,
12419
+ left: rect.left,
12420
+ width: rect.width,
12421
+ maxHeight,
12422
+ placement: 'top',
12423
+ });
12424
+ }, []);
12425
+ const updateListPopupPosition = useCallback(() => {
12426
+ const anchor = moreButtonRef.current;
12427
+ if (!anchor)
12428
+ return;
12429
+ const rect = anchor.getBoundingClientRect();
12430
+ const gap = 4;
12431
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
12432
+ const spaceAbove = rect.top - gap;
12433
+ const openDown = spaceBelow >= 120 || spaceBelow >= spaceAbove;
12434
+ const availableSpace = openDown ? spaceBelow : spaceAbove;
12435
+ const maxHeight = Math.min(280, Math.max(120, availableSpace - 8));
12436
+ setListPopupPosition(openDown
12437
+ ? {
12438
+ top: rect.bottom + gap,
12439
+ left: rect.left,
12440
+ width: Math.max(rect.width, 220),
12441
+ maxHeight,
12442
+ placement: 'bottom',
12443
+ }
12444
+ : {
12445
+ bottom: window.innerHeight - rect.top + gap,
12446
+ left: rect.left,
12447
+ width: Math.max(rect.width, 220),
12448
+ maxHeight,
12449
+ placement: 'top',
12450
+ });
12451
+ }, []);
12452
+ useEffect(() => {
12453
+ if (!isOpen) {
12454
+ setDropdownPosition(null);
12455
+ setSearchQuery('');
12456
+ return;
12457
+ }
12458
+ updateDropdownPosition();
12459
+ const handleResize = () => updateDropdownPosition();
12460
+ window.addEventListener('resize', handleResize);
12461
+ return () => {
12462
+ window.removeEventListener('resize', handleResize);
12463
+ };
12464
+ }, [isOpen, updateDropdownPosition]);
12465
+ useEffect(() => {
12466
+ if (!isListPopupOpen) {
12467
+ setListPopupPosition(null);
12468
+ return;
12469
+ }
12470
+ updateListPopupPosition();
12471
+ const handleResize = () => updateListPopupPosition();
12472
+ window.addEventListener('resize', handleResize);
12473
+ return () => {
12474
+ window.removeEventListener('resize', handleResize);
12475
+ };
12476
+ }, [isListPopupOpen, updateListPopupPosition]);
12477
+ useEffect(() => {
12478
+ if (!isOpen && !isListPopupOpen)
12479
+ return;
12480
+ const handleScroll = (event) => {
12481
+ const target = event.target;
12482
+ if (dropdownRef.current?.contains(target))
12483
+ return;
12484
+ if (listPopupRef.current?.contains(target))
12485
+ return;
12486
+ if (isOpen)
12487
+ setIsOpen(false);
12488
+ if (isListPopupOpen)
12489
+ setIsListPopupOpen(false);
12490
+ };
12491
+ window.addEventListener('scroll', handleScroll, true);
12492
+ return () => window.removeEventListener('scroll', handleScroll, true);
12493
+ }, [isOpen, isListPopupOpen]);
12494
+ useEffect(() => {
12495
+ if (!isOpen && !isListPopupOpen)
12496
+ return;
12497
+ const handleClickOutside = (event) => {
12498
+ const target = event.target;
12499
+ if (isOpen) {
12500
+ if (containerRef.current?.contains(target))
12501
+ return;
12502
+ if (dropdownRef.current?.contains(target))
12503
+ return;
12504
+ setIsOpen(false);
12505
+ }
12506
+ if (isListPopupOpen) {
12507
+ if (listPopupRef.current?.contains(target))
12508
+ return;
12509
+ if (moreButtonRef.current?.contains(target))
12510
+ return;
12511
+ setIsListPopupOpen(false);
12512
+ }
12513
+ };
12514
+ document.addEventListener('mousedown', handleClickOutside);
12515
+ return () => document.removeEventListener('mousedown', handleClickOutside);
12516
+ }, [isOpen, isListPopupOpen]);
12517
+ useEffect(() => {
12518
+ if (isOpen && searchInputRef.current) {
12519
+ searchInputRef.current.focus();
12520
+ }
12521
+ }, [isOpen]);
12522
+ const processedOptions = useMemo(() => {
12523
+ let options = dataSourceOptions.map((opt) => {
12524
+ const rawLabel = String(opt.label ?? opt.value ?? '');
12525
+ return {
12526
+ value: opt.value,
12527
+ label: translateConfig(rawLabel),
12528
+ rawLabel,
12529
+ };
12530
+ });
12531
+ if (sortOptions) {
12532
+ options.sort((a, b) => a.label.localeCompare(b.label));
12533
+ }
12534
+ return options;
12535
+ }, [dataSourceOptions, sortOptions, translateConfig]);
12536
+ const filteredOptions = useMemo(() => {
12537
+ if (!searchQuery.trim())
12538
+ return processedOptions;
12539
+ const q = searchQuery.trim().toLowerCase();
12540
+ return processedOptions.filter((opt) => opt.label.toLowerCase().includes(q) ||
12541
+ opt.rawLabel.toLowerCase().includes(q));
12542
+ }, [processedOptions, searchQuery]);
12543
+ const selectedValues = useMemo(() => {
12544
+ if (value === null || value === undefined)
12545
+ return [];
12546
+ if (Array.isArray(value))
12547
+ return value;
12548
+ return [value];
12549
+ }, [value]);
12550
+ const allFilteredSelected = useMemo(() => {
12551
+ if (filteredOptions.length === 0)
12552
+ return false;
12553
+ return filteredOptions.every((opt) => selectedValues.includes(opt.value));
12554
+ }, [filteredOptions, selectedValues]);
12555
+ const handleToggle = useCallback((optionValue, checked) => {
12556
+ if (checked) {
12557
+ onChange([...selectedValues, optionValue]);
12558
+ }
12559
+ else {
12560
+ onChange(selectedValues.filter((v) => v !== optionValue));
12561
+ }
12562
+ }, [selectedValues, onChange]);
12563
+ const handleSelectAll = useCallback(() => {
12564
+ const filteredVals = filteredOptions.map((o) => o.value);
12565
+ const merged = Array.from(new Set([...selectedValues, ...filteredVals]));
12566
+ onChange(merged);
12567
+ }, [filteredOptions, selectedValues, onChange]);
12568
+ const handleClearAll = useCallback(() => {
12569
+ onChange([]);
12570
+ }, [onChange]);
12571
+ const selectedLabels = useMemo(() => {
12572
+ return selectedValues.map((val) => {
12573
+ const opt = processedOptions.find((o) => o.value === val);
12574
+ return opt ? opt.label : translateConfig(String(val));
12575
+ });
12576
+ }, [selectedValues, processedOptions, translateConfig]);
12577
+ const fullSelectionText = selectedLabels.join(', ');
12578
+ const visibleLabels = selectedLabels.slice(0, 5);
12579
+ const overflowCount = Math.max(0, selectedLabels.length - 10);
12580
+ const disabled = !isEnabled || loading || widgetConfig['widget-readonly'];
12581
+ const renderSelectedLabels = (options) => {
12582
+ if (selectedLabels.length === 0)
12583
+ return null;
12584
+ const readonly = options?.readonly ?? false;
12585
+ return (jsxRuntimeExports.jsxs("div", { className: `flex flex-wrap gap-1 ${readonly ? '' : 'mt-1.5'}`, children: [visibleLabels.map((label, index) => (jsxRuntimeExports.jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded-md bg-blue-50 px-2 py-0.5 text-xs text-blue-800", title: label, children: [jsxRuntimeExports.jsx("span", { className: "truncate", children: label }), !readonly && !disabled && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleToggle(selectedValues[index], false), className: "shrink-0 text-blue-600 hover:text-blue-900 focus:outline-none", "aria-label": translate('common.removeItem', {
12586
+ label,
12587
+ defaultValue: `Remove ${label}`,
12588
+ }), children: "\u00D7" }))] }, `${selectedValues[index]}-${label}`))), overflowCount > 0 && (jsxRuntimeExports.jsx("button", { ref: moreButtonRef, type: "button", onClick: () => setIsListPopupOpen((prev) => !prev), className: "inline-flex items-center rounded-md bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-1 focus:ring-blue-500", children: translate('common.moreSelected', {
12589
+ count: overflowCount,
12590
+ defaultValue: `+${overflowCount} more`,
12591
+ }) }))] }));
12592
+ };
12593
+ const listPopupPanel = isListPopupOpen && listPopupPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: listPopupRef, className: "fixed z-[201] bg-white border border-gray-300 shadow-lg", style: {
12594
+ ...(listPopupPosition.placement === 'bottom'
12595
+ ? { top: listPopupPosition.top }
12596
+ : { bottom: listPopupPosition.bottom }),
12597
+ left: listPopupPosition.left,
12598
+ width: listPopupPosition.width,
12599
+ maxWidth: '320px',
12600
+ maxHeight: listPopupPosition.maxHeight,
12601
+ borderRadius: '10px',
12602
+ display: 'flex',
12603
+ flexDirection: 'column',
12604
+ }, children: [jsxRuntimeExports.jsx("div", { className: "px-3 py-2 text-xs font-semibold text-gray-500 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: translate('common.allSelected', {
12605
+ count: selectedLabels.length,
12606
+ defaultValue: `All selected (${selectedLabels.length})`,
12607
+ }) }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", children: selectedLabels.map((label, index) => (jsxRuntimeExports.jsx("div", { className: "px-3 py-1.5 text-sm text-gray-700", title: label, children: label }, `${selectedValues[index]}-${label}`))) })] })) : null;
12608
+ if (widgetConfig['widget-readonly']) {
12609
+ const fieldLabel = widgetConfig['widget-label'];
12610
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] MultiSelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [fieldLabel && (jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", label: fieldLabel })), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [selectedLabels.length === 0 ? (jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", children: "-" })) : (renderSelectedLabels({ readonly: true })), mounted && listPopupPanel && typeof document !== 'undefined'
12611
+ ? createPortal(listPopupPanel, document.body)
12612
+ : null] })] }));
12613
+ }
12614
+ const optionsMaxHeight = dropdownPosition
12615
+ ? Math.min(280, dropdownPosition.maxHeight - 100)
12616
+ : 280;
12617
+ const dropdownPanel = isOpen && dropdownPosition && mounted ? (jsxRuntimeExports.jsxs("div", { ref: dropdownRef, className: "fixed z-[200] bg-white border border-gray-300 shadow-lg", style: {
12618
+ ...(dropdownPosition.placement === 'bottom'
12619
+ ? { top: dropdownPosition.top }
12620
+ : { bottom: dropdownPosition.bottom }),
12621
+ left: dropdownPosition.left,
12622
+ width: dropdownPosition.width,
12623
+ maxWidth: '280px',
12624
+ maxHeight: dropdownPosition.maxHeight,
12625
+ borderRadius: '10px',
12626
+ display: 'flex',
12627
+ flexDirection: 'column',
12628
+ }, children: [jsxRuntimeExports.jsx("div", { className: "px-3 pt-2 pb-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", placeholder: translate('common.searchPlaceholder', { defaultValue: 'Search...' }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full h-[28px] px-2 text-sm border border-gray-300 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500", style: { borderRadius: '6px' } }) }), jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 py-1 shrink-0", style: { borderBottom: '1px solid #e5e7eb' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: allFilteredSelected ? () => {
12629
+ const filteredVals = new Set(filteredOptions.map((o) => o.value));
12630
+ onChange(selectedValues.filter((v) => !filteredVals.has(v)));
12631
+ } : handleSelectAll, className: "text-xs font-medium text-blue-600 hover:text-blue-800 focus:outline-none", children: allFilteredSelected
12632
+ ? translate('common.deselectAll', { defaultValue: 'Deselect All' })
12633
+ : translate('common.selectAll', { defaultValue: 'Select All' }) }), selectedValues.length > 0 && (jsxRuntimeExports.jsx("button", { type: "button", onClick: handleClearAll, className: "text-xs font-medium text-red-500 hover:text-red-700 focus:outline-none", children: translate('common.clearAll', { defaultValue: 'Clear All' }) }))] }), jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1 min-h-0 py-1 overscroll-contain", style: { maxHeight: `${Math.max(80, optionsMaxHeight)}px` }, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 px-3 py-2", children: translate('common.loading') })) : filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-400 px-3 py-2", children: translate('common.noOptionsFound', { defaultValue: 'No options found' }) })) : (filteredOptions.map((option) => {
12634
+ const isChecked = selectedValues.includes(option.value);
12635
+ return (jsxRuntimeExports.jsxs("label", { className: `flex items-center gap-2 px-3 py-1 cursor-pointer hover:bg-blue-50 ${isChecked ? 'bg-blue-50/60' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => handleToggle(option.value, e.target.checked), className: "h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700 leading-normal select-none", children: option.label })] }, option.value));
12636
+ })) })] })) : null;
12637
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: widgetConfig['widget-label'] ?? '', required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", ref: containerRef, children: [jsxRuntimeExports.jsxs("button", { ref: triggerRef, type: "button", onClick: () => {
12638
+ if (!disabled)
12639
+ setIsOpen((prev) => !prev);
12640
+ }, onBlur: () => {
12641
+ if (!isOpen)
12642
+ onBlur();
12643
+ }, disabled: disabled, className: `w-full sm:w-[280px] max-w-full h-[30px] px-3 border shadow-sm text-left flex items-center justify-between gap-2 ${(touched && error.length > 0) ||
12644
+ (widgetConfig['widget-required'] && selectedValues.length === 0)
12645
+ ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
12646
+ : 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white cursor-pointer'} focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500`, style: { borderRadius: '10px' }, title: selectedValues.length > 0
12647
+ ? fullSelectionText
12648
+ : translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("span", { className: `truncate text-sm ${selectedValues.length === 0 ? 'text-gray-400' : 'text-gray-900'}`, children: selectedLabels.length === 0
12649
+ ? translate('common.select', { defaultValue: 'Select...' })
12650
+ : translate('common.selectedCount', {
12651
+ count: selectedLabels.length,
12652
+ defaultValue: `${selectedLabels.length} selected`,
12653
+ }) }), jsxRuntimeExports.jsx("svg", { className: `w-4 h-4 flex-shrink-0 text-gray-500 transition-transform ${isOpen ? 'rotate-180' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), mounted && dropdownPanel && typeof document !== 'undefined'
12654
+ ? createPortal(dropdownPanel, document.body)
12655
+ : null, selectedLabels.length > 0 && renderSelectedLabels(), mounted && listPopupPanel && typeof document !== 'undefined'
12656
+ ? createPortal(listPopupPanel, document.body)
12657
+ : null, touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] })), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') }))] })] }) }));
12658
+ };
12659
+
11362
12660
  /**
11363
12661
  * Register all default/generic widgets
11364
12662
  * This is called automatically when the package is imported
@@ -11406,6 +12704,10 @@ const registerDefaultWidgets = () => {
11406
12704
  widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
11407
12705
  // ID Authentication widget for OIDC-based foundational ID authentication (view-only + action)
11408
12706
  widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
12707
+ // Register lookup widget — searchable popup to select a record from any register
12708
+ widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
12709
+ // Multi-select widget — searchable dropdown with checkbox-style options, select all, and clear all
12710
+ widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
11409
12711
  };
11410
12712
  // Auto-register on import
11411
12713
  registerDefaultWidgets();
@@ -11468,6 +12770,27 @@ var enTranslations = {
11468
12770
  "common.sectionSaved": "Saved",
11469
12771
  "common.sectionModified": "Modified and not saved",
11470
12772
  "common.supportedDocuments": "Supported Documents",
12773
+ "common.searchPlaceholder": "Search...",
12774
+ "common.selectAll": "Select All",
12775
+ "common.deselectAll": "Deselect All",
12776
+ "common.clearAll": "Clear All",
12777
+ "common.noOptionsFound": "No options found",
12778
+ "common.allSelected": "All selected ({{count}})",
12779
+ "common.moreSelected": "+{{count}} more",
12780
+ "common.selectedCount": "{{count}} selected",
12781
+ "common.removeItem": "Remove {{label}}",
12782
+ "common.selectAction": "Select {{label}}",
12783
+ "common.selectTitle": "Select {{label}}",
12784
+ "common.change": "Change",
12785
+ "common.noResults": "No results found",
12786
+ "common.searchHint": "Type and press Enter or click search",
12787
+ "common.record": "{{count}} record",
12788
+ "common.records": "{{count}} records",
12789
+ "common.page": "Page",
12790
+ "common.ofPages": "of {{total}}",
12791
+ "common.pageNumber": "Page number",
12792
+ "common.close": "Close",
12793
+ "common.search": "Search",
11471
12794
  "table.addRecord": "Add New Record",
11472
12795
  "table.confirm": "Confirm Action",
11473
12796
  "table.discard": "Discard & Continue",
@@ -11708,13 +13031,10 @@ const translateWidgetConfig = (widgetConfig, translate) => {
11708
13031
  ...dataSource,
11709
13032
  options: dataSource.options.map((option) => {
11710
13033
  if (option.label && typeof option.label === 'string') {
11711
- const optionLabel = option.label;
11712
- if (isTranslationKey(optionLabel)) {
11713
- return {
11714
- ...option,
11715
- label: translate(optionLabel, { defaultValue: optionLabel }),
11716
- };
11717
- }
13034
+ return {
13035
+ ...option,
13036
+ label: translate(option.label, { defaultValue: option.label }),
13037
+ };
11718
13038
  }
11719
13039
  return option;
11720
13040
  }),
@@ -11767,5 +13087,5 @@ const translateUISchema = (schema, translate) => {
11767
13087
  };
11768
13088
  };
11769
13089
 
11770
- export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, initI18n, isAllowedKey, normalizeNumericDefault, parseDataPath, parseNumber, registerDefaultWidgets, removeMask, resetAll, resetWidget, resolveTheme, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
13090
+ export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, FileInputWidget, GEO_LEVEL_CLEARED, HeaderSectionWidget, IdAuthenticationWidget, IterableAccordionWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, applySharedGeoHierarchyToValues, collectGeoWidgetRegistrationsFromWidgets, createGeoLevelMnemonicResolver, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getCachedApiDataSource, getFormattedNumberLength, getGeoDescendantWidgetIds, getGeoGroupId, getGeoWidgetRegistrationsInGroup, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, initI18n, isAllowedKey, isUpstreamGeoAncestor, normalizeNumericDefault, normalizeOptionRules, orderGeoWidgetRegistrations, parseDataPath, parseNumber, rebuildGeoHierarchyFromRegistrations, reconcileGeoHierarchiesInValues, registerDefaultWidgets, registerGeoWidget, registerGeoWidgetParent, removeMask, resetAll, resetAndSeedGeoHierarchyFromValues, resetWidget, resolveGeoWidgetLevelLabel, resolveGeoWidgetLevelValue, resolveTheme, resolveWidgetIdValue, seedGeoHierarchyFromValues, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, unregisterGeoWidget, unregisterGeoWidgetParent, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
11771
13091
  //# sourceMappingURL=index.esm.js.map