@openg2p/registry-widgets 1.1.0 → 1.1.2-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -673,6 +673,21 @@ const getFormattedNumberLength = (value, format) => {
673
673
  const formatted = formatNumber(typeof value === 'string' ? parseFloat(value) : value, format);
674
674
  return formatted.length;
675
675
  };
676
+ const normalizeNumericDefault = (defaultValue, format) => {
677
+ if (defaultValue === undefined) {
678
+ return undefined;
679
+ }
680
+ if (defaultValue === null || defaultValue === '') {
681
+ return null;
682
+ }
683
+ const numValue = typeof defaultValue === 'number'
684
+ ? defaultValue
685
+ : parseNumber(String(defaultValue), format);
686
+ if (numValue === null || isNaN(numValue)) {
687
+ return undefined;
688
+ }
689
+ return applyDecimalPrecision(numValue, format);
690
+ };
676
691
 
677
692
  /**
678
693
  * Date input utilities for parsing, formatting, and validation
@@ -808,6 +823,40 @@ const parseDateFromFormat = (dateString, format) => {
808
823
  }
809
824
  return parseDate(dateString);
810
825
  };
826
+ /**
827
+ * Resolve a stored date value (ISO or parseable string) to YYYY-MM-DD for comparisons.
828
+ */
829
+ const resolveDateBoundFromFieldValue = (fieldValue) => {
830
+ if (fieldValue == null || fieldValue === '') {
831
+ return undefined;
832
+ }
833
+ const iso = formatDateToISO(fieldValue);
834
+ return iso || undefined;
835
+ };
836
+ /**
837
+ * Pick the stricter (later) minimum when combining static and field-based bounds.
838
+ */
839
+ const mergeMinDateBounds = (boundA, boundB) => {
840
+ if (!boundA) {
841
+ return boundB;
842
+ }
843
+ if (!boundB) {
844
+ return boundA;
845
+ }
846
+ return boundA > boundB ? boundA : boundB;
847
+ };
848
+ /**
849
+ * Pick the stricter (earlier) maximum when combining static and field-based bounds.
850
+ */
851
+ const mergeMaxDateBounds = (boundA, boundB) => {
852
+ if (!boundA) {
853
+ return boundB;
854
+ }
855
+ if (!boundB) {
856
+ return boundA;
857
+ }
858
+ return boundA < boundB ? boundA : boundB;
859
+ };
811
860
  /**
812
861
  * Get min date based on constraint type
813
862
  */
@@ -850,10 +899,7 @@ const getMaxDate = (constraint, maxDate) => {
850
899
  }
851
900
  return undefined;
852
901
  };
853
- /**
854
- * Validate date constraints
855
- */
856
- const validateDateConstraints = (date, minDate, maxDate, constraint) => {
902
+ const validateDateConstraints = (date, minDate, maxDate, constraint, messages) => {
857
903
  if (!date)
858
904
  return null;
859
905
  const dateObj = date instanceof Date ? date : parseDate(date);
@@ -872,15 +918,12 @@ const validateDateConstraints = (date, minDate, maxDate, constraint) => {
872
918
  return 'Date must be in the future';
873
919
  }
874
920
  }
875
- // Check minDate
876
- const effectiveMinDate = getMinDate(constraint, minDate);
877
- if (effectiveMinDate && dateISO < effectiveMinDate) {
878
- return `Date must be on or after ${effectiveMinDate}`;
921
+ // minDate / maxDate are effective bounds (static + field-based), resolved by the caller
922
+ if (minDate && dateISO < minDate) {
923
+ return messages?.minDateMessage ?? `Date must be on or after ${minDate}`;
879
924
  }
880
- // Check maxDate
881
- const effectiveMaxDate = getMaxDate(constraint, maxDate);
882
- if (effectiveMaxDate && dateISO > effectiveMaxDate) {
883
- return `Date must be on or before ${effectiveMaxDate}`;
925
+ if (maxDate && dateISO > maxDate) {
926
+ return messages?.maxDateMessage ?? `Date must be on or before ${maxDate}`;
884
927
  }
885
928
  return null;
886
929
  };
@@ -3794,6 +3837,116 @@ const namespaceSectionConfig = (section, namespace) => {
3794
3837
  return namespaced;
3795
3838
  };
3796
3839
 
3840
+ /** Table-style widgets that bind to an array path in the store / schema. */
3841
+ function isTableLikeWidget(widget) {
3842
+ const w = widget.widget;
3843
+ /** widget-type union in types omits legacy values like simple-table still used at runtime */
3844
+ const t = widget['widget-type'];
3845
+ return (w === 'table' ||
3846
+ w === 'dialog-table' ||
3847
+ w === 'simple-table' ||
3848
+ t === 'table' ||
3849
+ t === 'simple-table');
3850
+ }
3851
+ /**
3852
+ * Resolve `records` for section save payloads.
3853
+ * - Back-compat: path ending in `.records` (e.g. `regId.records`)
3854
+ * - Else: first array snapshot at a string `widget-data-path` on a table-like widget
3855
+ * (e.g. `household.members` for dialog-table)
3856
+ */
3857
+ function extractTableRecordsFromSnapshot(snapshot, sectionWidgets) {
3858
+ const convention = Object.entries(snapshot).find(([key, value]) => key.endsWith('.records') && Array.isArray(value));
3859
+ if (convention) {
3860
+ return convention[1];
3861
+ }
3862
+ const tablePaths = [];
3863
+ sectionWidgets.forEach((widget) => {
3864
+ if (!isTableLikeWidget(widget))
3865
+ return;
3866
+ const p = widget['widget-data-path'];
3867
+ if (typeof p === 'string' && p.length > 0) {
3868
+ tablePaths.push(p);
3869
+ }
3870
+ else if (p && typeof p === 'object') {
3871
+ Object.values(p).forEach((sub) => {
3872
+ if (typeof sub === 'string' && sub.length > 0)
3873
+ tablePaths.push(sub);
3874
+ });
3875
+ }
3876
+ });
3877
+ for (const path of tablePaths) {
3878
+ const val = snapshot[path];
3879
+ if (Array.isArray(val)) {
3880
+ return val;
3881
+ }
3882
+ }
3883
+ return [];
3884
+ }
3885
+
3886
+ const isColumnRequired = (column, skipRequired) => {
3887
+ if (skipRequired)
3888
+ return false;
3889
+ const validation = column['widget-data-validation'];
3890
+ return !!(column['widget-required'] || validation?.required);
3891
+ };
3892
+ const validateTableLikeWidget = (widget, currentSchemaData, dispatch, skipRequired) => {
3893
+ const widgetId = widget['widget-id'];
3894
+ if (!widgetId)
3895
+ return true;
3896
+ const columns = (widget['widget-data-columns'] || []);
3897
+ const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3898
+ const rows = Array.isArray(value)
3899
+ ? value
3900
+ : [];
3901
+ const activeRows = rows.filter((row) => row?.edit_action !== 'DELETE');
3902
+ const rowErrors = [];
3903
+ let isValid = true;
3904
+ if (!skipRequired && widget['widget-required'] && activeRows.length === 0) {
3905
+ dispatch(setTouched({ widgetId, touched: true }));
3906
+ dispatch(setError({ widgetId, errors: ['At least one record is required'] }));
3907
+ return false;
3908
+ }
3909
+ const hasRequiredColumns = columns.some((col) => isColumnRequired(col, skipRequired));
3910
+ if (!skipRequired && hasRequiredColumns && activeRows.length === 0) {
3911
+ dispatch(setTouched({ widgetId, touched: true }));
3912
+ dispatch(setError({
3913
+ widgetId,
3914
+ errors: ['Add at least one record and fill all required fields'],
3915
+ }));
3916
+ return false;
3917
+ }
3918
+ activeRows.forEach((row, rowIndex) => {
3919
+ columns.forEach((col) => {
3920
+ if (col['widget-readonly'])
3921
+ return;
3922
+ const key = col['column-key'];
3923
+ if (!key)
3924
+ return;
3925
+ const required = isColumnRequired(col, skipRequired);
3926
+ const cellValue = row[key];
3927
+ const errors = validateWidget(cellValue, col['widget-data-validation'], required, skipRequired);
3928
+ if (errors.length > 0) {
3929
+ isValid = false;
3930
+ const label = col['widget-label'] || key;
3931
+ rowErrors.push(`Row ${rowIndex + 1}, ${label}: ${errors[0]}`);
3932
+ }
3933
+ });
3934
+ });
3935
+ if (!isValid) {
3936
+ dispatch(setTouched({ widgetId, touched: true }));
3937
+ dispatch(setError({
3938
+ widgetId,
3939
+ errors: rowErrors.length > 0
3940
+ ? rowErrors.slice(0, 5)
3941
+ : ['Please fix required fields in the table'],
3942
+ }));
3943
+ }
3944
+ else {
3945
+ dispatch(setTouched({ widgetId, touched: false }));
3946
+ dispatch(setError({ widgetId, errors: [] }));
3947
+ }
3948
+ return isValid;
3949
+ };
3797
3950
  const collectWidgets = (panels) => {
3798
3951
  let widgets = [];
3799
3952
  panels.forEach((panel) => {
@@ -3822,6 +3975,13 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3822
3975
  if (!isVisible)
3823
3976
  continue;
3824
3977
  const widgetId = widget['widget-id'];
3978
+ if (isTableLikeWidget(widget)) {
3979
+ const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
3980
+ if (!tableValid) {
3981
+ isValid = false;
3982
+ }
3983
+ continue;
3984
+ }
3825
3985
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3826
3986
  const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
3827
3987
  if (errors.length > 0) {
@@ -3856,52 +4016,6 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3856
4016
  return isValid;
3857
4017
  };
3858
4018
 
3859
- /** Table-style widgets that bind to an array path in the store / schema. */
3860
- function isTableLikeWidget(widget) {
3861
- const w = widget.widget;
3862
- /** widget-type union in types omits legacy values like simple-table still used at runtime */
3863
- const t = widget['widget-type'];
3864
- return (w === 'table' ||
3865
- w === 'dialog-table' ||
3866
- w === 'simple-table' ||
3867
- t === 'table' ||
3868
- t === 'simple-table');
3869
- }
3870
- /**
3871
- * Resolve `records` for section save payloads.
3872
- * - Back-compat: path ending in `.records` (e.g. `regId.records`)
3873
- * - Else: first array snapshot at a string `widget-data-path` on a table-like widget
3874
- * (e.g. `household.members` for dialog-table)
3875
- */
3876
- function extractTableRecordsFromSnapshot(snapshot, sectionWidgets) {
3877
- const convention = Object.entries(snapshot).find(([key, value]) => key.endsWith('.records') && Array.isArray(value));
3878
- if (convention) {
3879
- return convention[1];
3880
- }
3881
- const tablePaths = [];
3882
- sectionWidgets.forEach((widget) => {
3883
- if (!isTableLikeWidget(widget))
3884
- return;
3885
- const p = widget['widget-data-path'];
3886
- if (typeof p === 'string' && p.length > 0) {
3887
- tablePaths.push(p);
3888
- }
3889
- else if (p && typeof p === 'object') {
3890
- Object.values(p).forEach((sub) => {
3891
- if (typeof sub === 'string' && sub.length > 0)
3892
- tablePaths.push(sub);
3893
- });
3894
- }
3895
- });
3896
- for (const path of tablePaths) {
3897
- const val = snapshot[path];
3898
- if (Array.isArray(val)) {
3899
- return val;
3900
- }
3901
- }
3902
- return [];
3903
- }
3904
-
3905
4019
  /** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
3906
4020
  const READONLY_VALUE_ROW_ROOT_CLASSES = [
3907
4021
  'TextDisplayWidget',
@@ -7348,7 +7462,18 @@ const TextInputWidget = ({ config }) => {
7348
7462
  };
7349
7463
 
7350
7464
  const NumberInputWidget = ({ config }) => {
7351
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7465
+ const resolvedConfig = React.useMemo(() => {
7466
+ const rawDefault = config['widget-data-default'];
7467
+ if (rawDefault === undefined) {
7468
+ return config;
7469
+ }
7470
+ const normalizedDefault = normalizeNumericDefault(rawDefault, config['widget-data-format']);
7471
+ if (normalizedDefault === undefined || normalizedDefault === rawDefault) {
7472
+ return config;
7473
+ }
7474
+ return { ...config, 'widget-data-default': normalizedDefault };
7475
+ }, [config]);
7476
+ const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config: resolvedConfig });
7352
7477
  const { translate, translateConfig } = useWidgetTranslation();
7353
7478
  const formatConfig = widgetConfig['widget-data-format'];
7354
7479
  const validationConfig = widgetConfig['widget-data-validation'];
@@ -7511,6 +7636,8 @@ const BooleanWidget = ({ config }) => {
7511
7636
  return labels[representation];
7512
7637
  }, [representation, formatConfig, translateConfig]);
7513
7638
  const { trueLabel, falseLabel } = getLabels();
7639
+ const unsetLabel = React.useMemo(() => translateConfig(formatConfig?.booleanUnsetLabel || 'Not set'), [formatConfig?.booleanUnsetLabel, translateConfig]);
7640
+ const radioGroupName = `${widgetConfig['widget-id'] ?? 'boolean'}__${React.useId().replace(/:/g, '')}`;
7514
7641
  // Determine current value (handle null/undefined)
7515
7642
  const currentValue = React.useMemo(() => {
7516
7643
  if (value === null || value === undefined) {
@@ -7542,7 +7669,7 @@ const BooleanWidget = ({ config }) => {
7542
7669
  const label = translateConfig(widgetConfig['widget-label']);
7543
7670
  let displayValue = '';
7544
7671
  if (currentValue === null) {
7545
- displayValue = '-';
7672
+ displayValue = '';
7546
7673
  }
7547
7674
  else if (currentValue === true) {
7548
7675
  displayValue = trueLabel;
@@ -7554,18 +7681,20 @@ const BooleanWidget = ({ config }) => {
7554
7681
  }
7555
7682
  // Render based on control type
7556
7683
  if (controlType === 'checkbox') {
7557
- 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("label", { className: "flex items-center cursor-pointer", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: currentValue === true ? trueLabel : (currentValue === false ? falseLabel : '-') })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7684
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("label", { className: "inline-flex items-baseline cursor-pointer gap-2", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: currentValue === true, onChange: handleCheckboxChange, onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), (currentValue === true || currentValue === false) && (jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: currentValue === true ? trueLabel : falseLabel }))] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7558
7685
  }
7559
7686
  if (controlType === 'radio') {
7560
7687
  const containerClass = orientation === 'horizontal'
7561
- ? 'flex flex-row space-x-4'
7562
- : 'flex flex-col space-y-2';
7563
- 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: containerClass, onBlur: onBlur, 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: () => handleRadioChange(null), 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: "-" })] })), 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 === true, onChange: () => handleRadioChange(true), 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: trueLabel })] }), 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 === false, onChange: () => handleRadioChange(false), 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: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7688
+ ? 'flex flex-row flex-wrap items-baseline gap-x-4 gap-y-2'
7689
+ : 'flex flex-col items-start gap-2';
7690
+ const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
7691
+ const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
7692
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("div", { className: containerClass, onBlur: onBlur, children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === null, onChange: () => handleRadioChange(null), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: unsetLabel })] })), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === true, onChange: () => handleRadioChange(true), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: trueLabel })] }), jsxRuntimeExports.jsxs("label", { className: `inline-flex items-baseline gap-2 cursor-pointer ${optionDisabledClass}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: radioGroupName, checked: currentValue === false, onChange: () => handleRadioChange(false), disabled: radioDisabled, className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-base text-gray-700 leading-normal", children: falseLabel })] })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7564
7693
  }
7565
7694
  // Toggle/switch control type
7566
- 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 sm:min-w-[150px] 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 space-x-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
+ 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
7567
7696
  ? 'bg-blue-600 text-white border-blue-600'
7568
- : '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: "-" })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(true), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === true
7697
+ : '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
7569
7698
  ? 'bg-blue-600 text-white border-blue-600'
7570
7699
  : '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: trueLabel }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleChange(false), disabled: !isEnabled || widgetConfig['widget-readonly'], className: `px-3 py-1 text-sm border ${currentValue === false
7571
7700
  ? 'bg-blue-600 text-white border-blue-600'
@@ -7573,42 +7702,76 @@ const BooleanWidget = ({ config }) => {
7573
7702
  };
7574
7703
 
7575
7704
  const DateInputWidget = ({ config }) => {
7576
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7577
- const { translate, translateConfig } = useWidgetTranslation();
7705
+ const { value, error, touched, isEnabled, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
7706
+ const formValues = reactRedux.useSelector((state) => state.widget.values);
7707
+ const { translateConfig } = useWidgetTranslation();
7578
7708
  const formatConfig = widgetConfig['widget-data-format'];
7579
7709
  const optionsConfig = widgetConfig['widget-data-options'];
7580
7710
  const dateFormat = formatConfig?.dateFormat || 'YYYY-MM-DD';
7581
- const inputMethod = formatConfig?.inputMethod || 'picker'; // Default to picker for better UX
7711
+ const inputMethod = formatConfig?.inputMethod || 'picker';
7582
7712
  const dateConstraint = formatConfig?.dateConstraint || 'any';
7583
7713
  const minDate = optionsConfig?.minDate;
7584
7714
  const maxDate = optionsConfig?.maxDate;
7715
+ const minDateField = optionsConfig?.minDateField;
7716
+ const maxDateField = optionsConfig?.maxDateField;
7717
+ const minDateMessage = optionsConfig?.minDateMessage
7718
+ ? translateConfig(optionsConfig.minDateMessage)
7719
+ : undefined;
7720
+ const maxDateMessage = optionsConfig?.maxDateMessage
7721
+ ? translateConfig(optionsConfig.maxDateMessage)
7722
+ : undefined;
7585
7723
  const defaultToToday = widgetConfig['widget-data-default'] === 'today';
7586
- // Track manual input value (for manual/hybrid modes)
7587
7724
  const [manualInputValue, setManualInputValue] = React.useState('');
7588
7725
  const [isFocused, setIsFocused] = React.useState(false);
7589
- // Initialize default value to today if configured
7726
+ const fieldMinDate = React.useMemo(() => {
7727
+ if (!minDateField) {
7728
+ return undefined;
7729
+ }
7730
+ return resolveDateBoundFromFieldValue(getValueByPath(formValues, minDateField));
7731
+ }, [formValues, minDateField]);
7732
+ const fieldMaxDate = React.useMemo(() => {
7733
+ if (!maxDateField) {
7734
+ return undefined;
7735
+ }
7736
+ return resolveDateBoundFromFieldValue(getValueByPath(formValues, maxDateField));
7737
+ }, [formValues, maxDateField]);
7738
+ const effectiveMinDate = React.useMemo(() => {
7739
+ const staticMin = getMinDate(dateConstraint, minDate);
7740
+ return mergeMinDateBounds(staticMin, fieldMinDate);
7741
+ }, [dateConstraint, minDate, fieldMinDate]);
7742
+ const effectiveMaxDate = React.useMemo(() => {
7743
+ const staticMax = getMaxDate(dateConstraint, maxDate);
7744
+ return mergeMaxDateBounds(staticMax, fieldMaxDate);
7745
+ }, [dateConstraint, maxDate, fieldMaxDate]);
7746
+ const constraintMessages = React.useMemo(() => ({ minDateMessage, maxDateMessage }), [minDateMessage, maxDateMessage]);
7747
+ const runDateConstraintValidation = React.useCallback((dateValue) => {
7748
+ if (!dateValue) {
7749
+ return null;
7750
+ }
7751
+ return validateDateConstraints(dateValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
7752
+ }, [effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages]);
7590
7753
  React.useEffect(() => {
7591
7754
  if (defaultToToday && (value === null || value === undefined || value === '')) {
7592
7755
  const todayISO = formatDateToISO(new Date());
7593
7756
  onChange(todayISO);
7594
7757
  }
7595
7758
  }, [defaultToToday, value, onChange]);
7596
- // Get effective min/max dates
7597
- const effectiveMinDate = React.useMemo(() => {
7598
- return getMinDate(dateConstraint, minDate);
7599
- }, [dateConstraint, minDate]);
7600
- const effectiveMaxDate = React.useMemo(() => {
7601
- return getMaxDate(dateConstraint, maxDate);
7602
- }, [dateConstraint, maxDate]);
7603
- // Convert ISO value to display format
7759
+ // Re-validate when a relative bound field changes (e.g. start date set after end date)
7760
+ React.useEffect(() => {
7761
+ if (!value) {
7762
+ return;
7763
+ }
7764
+ const constraintError = runDateConstraintValidation(value);
7765
+ if (constraintError) {
7766
+ setError([constraintError]);
7767
+ }
7768
+ }, [fieldMinDate, fieldMaxDate, value, runDateConstraintValidation, setError]);
7604
7769
  const getDisplayValue = React.useCallback(() => {
7605
- // For picker mode, always use YYYY-MM-DD
7606
7770
  if (inputMethod === 'picker') {
7607
7771
  if (!value)
7608
7772
  return '';
7609
7773
  return formatDateToISO(value);
7610
7774
  }
7611
- // For manual/hybrid modes, use custom format
7612
7775
  if (isFocused && manualInputValue) {
7613
7776
  return manualInputValue;
7614
7777
  }
@@ -7619,7 +7782,6 @@ const DateInputWidget = ({ config }) => {
7619
7782
  }
7620
7783
  return formatDateToString(value, dateFormat);
7621
7784
  }, [value, inputMethod, dateFormat, isFocused, manualInputValue]);
7622
- // Initialize manual input value
7623
7785
  React.useEffect(() => {
7624
7786
  if (!isFocused && value) {
7625
7787
  if (dateFormat === 'YYYY-MM-DD') {
@@ -7630,66 +7792,81 @@ const DateInputWidget = ({ config }) => {
7630
7792
  }
7631
7793
  }
7632
7794
  }, [value, dateFormat, isFocused]);
7633
- // Handle input change
7795
+ const applyConstraintError = React.useCallback((dateValue) => {
7796
+ const constraintError = runDateConstraintValidation(dateValue);
7797
+ setError(constraintError ? [constraintError] : []);
7798
+ }, [runDateConstraintValidation, setError]);
7634
7799
  const handleChange = React.useCallback((e) => {
7635
7800
  const inputValue = e.target.value;
7636
7801
  if (inputMethod === 'picker') {
7637
- // Picker mode: input is always YYYY-MM-DD
7638
7802
  if (inputValue) {
7639
7803
  const date = parseDate(inputValue);
7640
7804
  if (date) {
7641
- onChange(formatDateToISO(date));
7805
+ const iso = formatDateToISO(date);
7806
+ onChange(iso);
7807
+ applyConstraintError(iso);
7642
7808
  }
7643
7809
  else {
7644
7810
  onChange('');
7811
+ setError([]);
7645
7812
  }
7646
7813
  }
7647
7814
  else {
7648
7815
  onChange('');
7816
+ setError([]);
7649
7817
  }
7650
7818
  }
7651
7819
  else {
7652
- // Manual/hybrid mode: parse custom format
7653
7820
  setManualInputValue(inputValue);
7654
7821
  if (inputValue) {
7655
7822
  const date = parseDateFromFormat(inputValue, dateFormat);
7656
7823
  if (date) {
7657
- // Validate constraints
7658
- const constraintError = validateDateConstraints(date, minDate, maxDate, dateConstraint);
7659
- if (!constraintError) {
7660
- onChange(formatDateToISO(date));
7661
- }
7662
- else {
7663
- // Still update the value but validation will catch it
7664
- onChange(formatDateToISO(date));
7665
- }
7824
+ const iso = formatDateToISO(date);
7825
+ onChange(iso);
7826
+ applyConstraintError(iso);
7666
7827
  }
7667
7828
  }
7668
7829
  else {
7669
7830
  onChange('');
7831
+ setError([]);
7670
7832
  }
7671
7833
  }
7672
- }, [inputMethod, dateFormat, onChange, minDate, maxDate, dateConstraint]);
7673
- // Handle blur - validate and format
7834
+ }, [inputMethod, dateFormat, onChange, applyConstraintError, setError]);
7674
7835
  const handleBlur = React.useCallback(() => {
7675
7836
  setIsFocused(false);
7676
7837
  if (inputMethod !== 'picker' && manualInputValue) {
7677
7838
  const date = parseDateFromFormat(manualInputValue, dateFormat);
7678
7839
  if (date) {
7679
- // Format the value according to the format
7680
7840
  const formatted = formatDateToString(date, dateFormat);
7681
7841
  setManualInputValue(formatted);
7682
- onChange(formatDateToISO(date));
7842
+ const iso = formatDateToISO(date);
7843
+ onChange(iso);
7844
+ applyConstraintError(iso);
7683
7845
  }
7684
7846
  else {
7685
- // Invalid date, clear it
7686
7847
  setManualInputValue('');
7687
7848
  onChange('');
7849
+ setError([]);
7688
7850
  }
7689
7851
  }
7690
7852
  onBlur();
7691
- }, [inputMethod, manualInputValue, dateFormat, onChange, onBlur]);
7692
- // Handle focus
7853
+ if (value) {
7854
+ const constraintError = runDateConstraintValidation(value);
7855
+ if (constraintError) {
7856
+ setError([constraintError]);
7857
+ }
7858
+ }
7859
+ }, [
7860
+ inputMethod,
7861
+ manualInputValue,
7862
+ dateFormat,
7863
+ onChange,
7864
+ onBlur,
7865
+ applyConstraintError,
7866
+ value,
7867
+ runDateConstraintValidation,
7868
+ setError,
7869
+ ]);
7693
7870
  const handleFocus = React.useCallback(() => {
7694
7871
  setIsFocused(true);
7695
7872
  if (value) {
@@ -7701,15 +7878,15 @@ const DateInputWidget = ({ config }) => {
7701
7878
  }
7702
7879
  }
7703
7880
  }, [value, dateFormat]);
7704
- // Determine placeholder
7705
7881
  const placeholder = React.useMemo(() => {
7706
- const hasValue = getDisplayValue() && getDisplayValue().trim().length > 0;
7882
+ const display = getDisplayValue();
7883
+ const hasValue = display && display.trim().length > 0;
7707
7884
  const placeholderText = translateConfig(widgetConfig['widget-data-placeholder']);
7708
- return hasValue ? undefined : (placeholderText || dateFormat);
7885
+ return hasValue ? undefined : placeholderText || dateFormat;
7709
7886
  }, [getDisplayValue, widgetConfig, translateConfig, dateFormat]);
7710
- // Determine input type
7711
7887
  const inputType = inputMethod === 'picker' ? 'date' : 'text';
7712
- // For readonly mode, render as display text
7888
+ const showRequiredError = widgetConfig['widget-required'] && (!value || value === '');
7889
+ const showValidationError = touched && error.length > 0;
7713
7890
  if (widgetConfig['widget-readonly']) {
7714
7891
  const label = translateConfig(widgetConfig['widget-label']);
7715
7892
  let displayValue = '';
@@ -7726,9 +7903,9 @@ const DateInputWidget = ({ config }) => {
7726
7903
  }
7727
7904
  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 }) })] }));
7728
7905
  }
7729
- 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 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
7906
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" })] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("input", { type: inputType, value: getDisplayValue(), onChange: handleChange, onBlur: handleBlur, onFocus: handleFocus, disabled: !isEnabled || widgetConfig['widget-readonly'], placeholder: placeholder, min: inputMethod === 'picker' ? effectiveMinDate : undefined, max: inputMethod === 'picker' ? effectiveMaxDate : undefined, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${showValidationError || showRequiredError
7730
7907
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7731
- : '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] }))] })] }) }));
7908
+ : '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] })] })] }) }));
7732
7909
  };
7733
7910
 
7734
7911
  /**
@@ -8276,7 +8453,7 @@ const CheckboxWidget = ({ config }) => {
8276
8453
  const displayValue = isChecked ? 'Yes' : 'No';
8277
8454
  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 }) })] }));
8278
8455
  }
8279
- 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("label", { className: "flex items-center cursor-pointer", children: [jsxRuntimeExports.jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onChange(e.target.checked), onBlur: onBlur, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-sm 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
+ 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] }))] })] }) }));
8280
8457
  }
8281
8458
  // Multiple checkboxes (with data source) - for array values
8282
8459
  // Process and sort options if needed
@@ -8319,7 +8496,7 @@ const CheckboxWidget = ({ config }) => {
8319
8496
  switch (layout) {
8320
8497
  case 'horizontal':
8321
8498
  return {
8322
- className: 'flex flex-row flex-wrap gap-4',
8499
+ className: 'flex flex-row flex-wrap items-baseline gap-4',
8323
8500
  style: undefined,
8324
8501
  };
8325
8502
  case 'grid':
@@ -8332,7 +8509,7 @@ const CheckboxWidget = ({ config }) => {
8332
8509
  case 'vertical':
8333
8510
  default:
8334
8511
  return {
8335
- className: 'flex flex-col space-y-2',
8512
+ className: 'flex flex-col gap-2',
8336
8513
  style: undefined,
8337
8514
  };
8338
8515
  }
@@ -8346,7 +8523,7 @@ const CheckboxWidget = ({ config }) => {
8346
8523
  : '-';
8347
8524
  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 }) })] }));
8348
8525
  }
8349
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("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: `flex items-center cursor-pointer ${!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: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), 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] }))] })] }) }));
8526
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-baseline", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium leading-normal text-gray-700 md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0 sm:pt-0.5", style: { fontFamily: 'Roboto, sans-serif' }, title: translateConfig(widgetConfig['widget-label']), children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `inline-flex cursor-pointer items-baseline gap-2 ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "checkbox", value: option.value, checked: selectedValues.includes(option.value), onChange: (e) => handleCheckboxChange(option.value, e.target.checked), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "relative top-[0.2em] h-4 w-4 shrink-0 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" }), jsxRuntimeExports.jsx("span", { className: "text-base leading-normal text-gray-700", children: translateConfig(option.label) })] }, option.value)))) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
8350
8527
  };
8351
8528
 
8352
8529
  const SimpleTableWidget = ({ config }) => {
@@ -8672,16 +8849,70 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
8672
8849
  backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8673
8850
  } }));
8674
8851
  };
8675
- const TableCellDate = ({ config, value, onValueChange }) => {
8852
+ const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8853
+ const { translateConfig } = useWidgetTranslation();
8676
8854
  const isReadonly = config['widget-readonly'] || false;
8677
8855
  const placeholder = config['widget-data-placeholder'] || '';
8856
+ const optionsConfig = config['widget-data-options'];
8857
+ const formatConfig = config['widget-data-format'];
8858
+ const dateConstraint = formatConfig?.dateConstraint || 'any';
8859
+ const minDate = optionsConfig?.minDate;
8860
+ const maxDate = optionsConfig?.maxDate;
8861
+ const minDateField = optionsConfig?.minDateField;
8862
+ const maxDateField = optionsConfig?.maxDateField;
8863
+ const minDateMessage = optionsConfig?.minDateMessage
8864
+ ? translateConfig(optionsConfig.minDateMessage)
8865
+ : undefined;
8866
+ const maxDateMessage = optionsConfig?.maxDateMessage
8867
+ ? translateConfig(optionsConfig.maxDateMessage)
8868
+ : undefined;
8869
+ const [constraintError, setConstraintError] = React.useState(null);
8870
+ const resolveSiblingDate = (fieldRef) => {
8871
+ if (!fieldRef || !rowValues) {
8872
+ return undefined;
8873
+ }
8874
+ const raw = getValueByPath(rowValues, fieldRef) ?? rowValues[fieldRef];
8875
+ return resolveDateBoundFromFieldValue(raw);
8876
+ };
8877
+ const fieldMinDate = React.useMemo(() => resolveSiblingDate(minDateField), [minDateField, rowValues]);
8878
+ const fieldMaxDate = React.useMemo(() => resolveSiblingDate(maxDateField), [maxDateField, rowValues]);
8879
+ const effectiveMinDate = React.useMemo(() => {
8880
+ const staticMin = getMinDate(dateConstraint, minDate);
8881
+ return mergeMinDateBounds(staticMin, fieldMinDate);
8882
+ }, [dateConstraint, minDate, fieldMinDate]);
8883
+ const effectiveMaxDate = React.useMemo(() => {
8884
+ const staticMax = getMaxDate(dateConstraint, maxDate);
8885
+ return mergeMaxDateBounds(staticMax, fieldMaxDate);
8886
+ }, [dateConstraint, maxDate, fieldMaxDate]);
8887
+ const constraintMessages = React.useMemo(() => ({ minDateMessage, maxDateMessage }), [minDateMessage, maxDateMessage]);
8678
8888
  // input type="date" requires YYYY-MM-DD format
8679
8889
  const displayValue = value && typeof value === 'string' ? value.split('T')[0] : '';
8680
- return (jsxRuntimeExports.jsx("input", { type: "date", value: displayValue, onChange: (e) => onValueChange(e.target.value), disabled: isReadonly, placeholder: placeholder, className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} table-cell-input`, style: {
8681
- borderRadius: '10px',
8682
- borderColor: 'var(--owt-widget-input-border, #C4C4C4)',
8683
- backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8684
- } }));
8890
+ React.useEffect(() => {
8891
+ if (!displayValue) {
8892
+ setConstraintError(null);
8893
+ return;
8894
+ }
8895
+ const error = validateDateConstraints(displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
8896
+ setConstraintError(error);
8897
+ }, [displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages]);
8898
+ const handleChange = (e) => {
8899
+ const nextValue = e.target.value;
8900
+ onValueChange(nextValue);
8901
+ if (!nextValue) {
8902
+ setConstraintError(null);
8903
+ return;
8904
+ }
8905
+ const error = validateDateConstraints(nextValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
8906
+ setConstraintError(error);
8907
+ };
8908
+ const hasError = Boolean(constraintError);
8909
+ return (jsxRuntimeExports.jsxs("div", { className: "w-full", children: [jsxRuntimeExports.jsx("input", { type: "date", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: effectiveMinDate, max: effectiveMaxDate, title: constraintError || translateConfig(config['widget-data-tooltip']), className: `w-full h-[28px] px-2 text-sm border focus:outline-none ${isReadonly ? 'cursor-not-allowed' : ''} ${hasError ? 'border-red-500' : ''} table-cell-input`, style: {
8910
+ borderRadius: '10px',
8911
+ borderColor: hasError
8912
+ ? 'var(--owt-color-error, #B91C1C)'
8913
+ : 'var(--owt-widget-input-border, #C4C4C4)',
8914
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8915
+ } }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-xs mt-0.5 leading-tight", children: constraintError }))] }));
8685
8916
  };
8686
8917
  const TableWidget = ({ config }) => {
8687
8918
  const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
@@ -9040,11 +9271,21 @@ const TableWidget = ({ config }) => {
9040
9271
  });
9041
9272
  }
9042
9273
  }, [isAdding, newRowData, columns, widgetConfig, rows.length, dispatch]);
9274
+ const getRowValuesForEdit = React.useCallback((rowIndex) => {
9275
+ if (editingState && editingState.rowIndex === rowIndex) {
9276
+ return editingState.currentValue ?? {};
9277
+ }
9278
+ if (isAdding && rowIndex === rows.length && newRowData) {
9279
+ return newRowData;
9280
+ }
9281
+ return rows[rowIndex] ?? {};
9282
+ }, [editingState, isAdding, rows, newRowData]);
9043
9283
  // Lightweight cell renderer for table cells (no labels, compact)
9044
9284
  const renderTableCell = React.useCallback((rowIndex, column, cellValue, isReadonly) => {
9045
9285
  const columnKey = column['column-key'];
9046
9286
  const widgetType = column.widget || 'text';
9047
9287
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
9288
+ const rowValues = getRowValuesForEdit(rowIndex);
9048
9289
  // Use lightweight cell config (no label, minimal styling)
9049
9290
  const cellConfig = {
9050
9291
  ...column,
@@ -9067,7 +9308,7 @@ const TableWidget = ({ config }) => {
9067
9308
  return jsxRuntimeExports.jsx(TableCellNumber, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9068
9309
  }
9069
9310
  else if (widgetType === 'date') {
9070
- return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9311
+ return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, rowValues: rowValues, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9071
9312
  }
9072
9313
  // For other widget types, use WidgetRenderer but with compact styling
9073
9314
  return (jsxRuntimeExports.jsx("div", { className: "table-cell-widget", style: { margin: 0, padding: 0 }, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
@@ -9075,7 +9316,7 @@ const TableWidget = ({ config }) => {
9075
9316
  }, onValueChange: (widgetId, newValue) => {
9076
9317
  updateCellValue(columnKey, newValue, rowIndex);
9077
9318
  } }) }));
9078
- }, [widgetConfig, updateCellValue]);
9319
+ }, [widgetConfig, updateCellValue, getRowValuesForEdit]);
9079
9320
  // Render cell content (widget in edit mode, formatted value in view mode)
9080
9321
  const renderCell = React.useCallback((rowIndex, column, row) => {
9081
9322
  const columnKey = column['column-key'];
@@ -9385,6 +9626,27 @@ const DialogTableWidget = ({ config }) => {
9385
9626
  }, [formData, columns, storeValues, dialogFieldWidgetId]);
9386
9627
  const saveDialog = React.useCallback(() => {
9387
9628
  const payload = collectMergedRowPayload();
9629
+ let hasErrors = false;
9630
+ columns.forEach((col) => {
9631
+ const key = col['column-key'];
9632
+ const cellWidgetId = dialogFieldWidgetId(key);
9633
+ const isColReadonly = isReadonly || col['widget-readonly'] === true;
9634
+ if (isColReadonly)
9635
+ return;
9636
+ const cellValue = payload[key];
9637
+ const validationErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
9638
+ if (validationErrors && validationErrors.length > 0) {
9639
+ hasErrors = true;
9640
+ dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
9641
+ dispatch(setTouched({ widgetId: cellWidgetId, touched: true }));
9642
+ }
9643
+ else {
9644
+ dispatch(setError({ widgetId: cellWidgetId, errors: [] }));
9645
+ }
9646
+ });
9647
+ if (hasErrors) {
9648
+ return;
9649
+ }
9388
9650
  if (dialogMode === 'add') {
9389
9651
  const savedRow = { ...payload, edit_action: 'ADD' };
9390
9652
  onChange([...rows, savedRow]);
@@ -9400,7 +9662,7 @@ const DialogTableWidget = ({ config }) => {
9400
9662
  onChange(newRows);
9401
9663
  closeDialog();
9402
9664
  }
9403
- }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex]);
9665
+ }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
9404
9666
  const deleteRow = React.useCallback((rowIndex) => {
9405
9667
  const newRows = rows.filter((_, i) => i !== rowIndex);
9406
9668
  onChange(newRows);
@@ -10226,20 +10488,29 @@ const HeaderSectionWidget = ({ config }) => {
10226
10488
  .${cls} .hdr-field-row {
10227
10489
  display: flex;
10228
10490
  align-items: flex-start;
10229
- gap: 0.5rem;
10230
10491
  font-size: 1rem;
10231
10492
  line-height: 1.6;
10232
10493
  }
10233
10494
 
10234
10495
  .${cls} .hdr-field-label {
10496
+ width: 50%;
10497
+ flex: 0 0 50%;
10235
10498
  color: rgba(0, 0, 0, 0.5);
10236
10499
  font-weight: 400;
10237
10500
  white-space: nowrap;
10501
+ overflow: hidden;
10502
+ text-overflow: ellipsis;
10503
+ padding-right: 4px;
10238
10504
  }
10239
10505
 
10240
10506
  .${cls} .hdr-field-value {
10507
+ width: 50%;
10508
+ flex: 0 0 50%;
10241
10509
  color: var(--owt-color-text, #111827);
10242
10510
  font-weight: 500;
10511
+ white-space: nowrap;
10512
+ overflow: hidden;
10513
+ text-overflow: ellipsis;
10243
10514
  }
10244
10515
 
10245
10516
  .${cls} .hdr-status-badge {
@@ -10249,24 +10520,38 @@ const HeaderSectionWidget = ({ config }) => {
10249
10520
  font-size: 0.75rem;
10250
10521
  font-weight: 600;
10251
10522
  color: #fff;
10523
+ max-width: 100%;
10524
+ overflow: hidden;
10525
+ text-overflow: ellipsis;
10526
+ white-space: nowrap;
10252
10527
  }
10253
10528
 
10254
10529
  .${cls} .hdr-meta-row {
10255
10530
  display: flex;
10256
10531
  align-items: baseline;
10257
- gap: 0.35rem;
10258
10532
  font-size: 1rem;
10259
10533
  line-height: 1.6;
10260
10534
  }
10261
10535
 
10262
10536
  .${cls} .hdr-meta-label {
10537
+ width: 50%;
10538
+ flex: 0 0 50%;
10263
10539
  color: rgba(0, 0, 0, 0.5);
10264
10540
  font-weight: 400;
10541
+ white-space: nowrap;
10542
+ overflow: hidden;
10543
+ text-overflow: ellipsis;
10544
+ padding-right: 4px;
10265
10545
  }
10266
10546
 
10267
10547
  .${cls} .hdr-meta-value {
10548
+ width: 50%;
10549
+ flex: 0 0 50%;
10268
10550
  color: var(--owt-color-text, #111827);
10269
10551
  font-weight: 500;
10552
+ white-space: nowrap;
10553
+ overflow: hidden;
10554
+ text-overflow: ellipsis;
10270
10555
  }
10271
10556
 
10272
10557
  .${cls} .hdr-select {
@@ -10330,7 +10615,7 @@ const HeaderSectionWidget = ({ config }) => {
10330
10615
  .parentElement?.querySelector('.hdr-avatar-placeholder');
10331
10616
  if (placeholder)
10332
10617
  placeholder.style.display = 'flex';
10333
- } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: getLabel('select') }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [jsxRuntimeExports.jsx("input", { type: "text", className: `hdr-input ${(!isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing) ? 'hdr-input--error' : ''}`, value: statusReason, placeholder: getLabel('enterReason'), required: isStatusChanged, "aria-required": isStatusChanged, "aria-invalid": !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing, onBlur: () => {
10618
+ } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: displayImageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) }), !isReadonly && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsxs("div", { className: "hdr-avatar-overlay", children: [jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action", onClick: () => fileInputRef.current?.click(), children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntimeExports.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntimeExports.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }), "Upload"] }), jsxRuntimeExports.jsxs("button", { type: "button", className: "hdr-avatar-action hdr-avatar-action--delete", onClick: handleImageDelete, children: [jsxRuntimeExports.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntimeExports.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }), "Delete"] })] }), jsxRuntimeExports.jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: handleImageUpload })] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", title: `${getLabel('functionalId')} :`, children: [getLabel('functionalId'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: functionalId || '-', children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx("span", { className: "hdr-field-label", title: getLabel('status'), children: getLabel('status') }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, title: statusLabel, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: "-", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: getLabel('select') }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", title: `${getLabel('statusReason')} :`, children: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", title: statusReason || '-', children: statusReason || '-' })) : (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [jsxRuntimeExports.jsx("input", { type: "text", className: `hdr-input ${(!isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing) ? 'hdr-input--error' : ''}`, value: statusReason, placeholder: getLabel('enterReason'), required: isStatusChanged, "aria-required": isStatusChanged, "aria-invalid": !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing, onBlur: () => {
10334
10619
  if (isReasonMissing)
10335
10620
  setShowReasonRequired(true);
10336
10621
  }, onChange: (e) => {
@@ -10338,7 +10623,7 @@ const HeaderSectionWidget = ({ config }) => {
10338
10623
  if (showReasonRequired && String(e.target.value || '').trim().length > 0) {
10339
10624
  setShowReasonRequired(false);
10340
10625
  }
10341
- } }), !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing ? (jsxRuntimeExports.jsx("div", { className: "hdr-error-text", children: getLabel('enterReason') })) : null] }))] })] })] }), jsxRuntimeExports.jsx("div", { className: "hdr-right", children: jsxRuntimeExports.jsxs("div", { className: "hdr-right-top", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-col", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('createdAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdAt || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [getLabel('lastApprovedAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] }), score ? (jsxRuntimeExports.jsx("div", { className: "hdr-score-ring", style: { ['--pct']: score.percent }, "aria-label": `Completion score ${score.completionDisplay} of ${score.idealDisplay} (${score.percent}%)`, title: `${score.completionDisplay} / ${score.idealDisplay} (${score.percent}%)`, children: jsxRuntimeExports.jsx("div", { className: "hdr-score-value", children: String(score.completionDisplay) }) })) : null] }) })] })] }));
10626
+ } }), !isReadonly && (showReasonRequired || isReasonMissing) && isReasonMissing ? (jsxRuntimeExports.jsx("div", { className: "hdr-error-text", children: getLabel('enterReason') })) : null] }))] })] })] }), jsxRuntimeExports.jsx("div", { className: "hdr-right", children: jsxRuntimeExports.jsxs("div", { className: "hdr-right-top", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-col", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", title: `${getLabel('createdBy')} :`, children: [getLabel('createdBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", title: createdBy || '-', children: createdBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", title: `${getLabel('createdAt')} :`, children: [getLabel('createdAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", title: createdAt || '-', children: createdAt || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", title: `${getLabel('lastApprovedBy')} :`, children: [getLabel('lastApprovedBy'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", title: lastApprovedBy || '-', children: lastApprovedBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", title: `${getLabel('lastApprovedAt')} :`, children: [getLabel('lastApprovedAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", title: lastApprovedAt || '-', children: lastApprovedAt || '-' })] })] }), score ? (jsxRuntimeExports.jsx("div", { className: "hdr-score-ring", style: { ['--pct']: score.percent }, "aria-label": `Completion score ${score.completionDisplay} of ${score.idealDisplay} (${score.percent}%)`, title: `${score.completionDisplay} / ${score.idealDisplay} (${score.percent}%)`, children: jsxRuntimeExports.jsx("div", { className: "hdr-score-value", children: String(score.completionDisplay) }) })) : null] }) })] })] }));
10342
10627
  };
10343
10628
 
10344
10629
  function getValueByPathOrKey(obj, path) {
@@ -11539,6 +11824,7 @@ exports.getValueByPath = getValueByPath;
11539
11824
  exports.getWidgetValue = getWidgetValue;
11540
11825
  exports.initI18n = initI18n;
11541
11826
  exports.isAllowedKey = isAllowedKey;
11827
+ exports.normalizeNumericDefault = normalizeNumericDefault;
11542
11828
  exports.parseDataPath = parseDataPath;
11543
11829
  exports.parseNumber = parseNumber;
11544
11830
  exports.registerDefaultWidgets = registerDefaultWidgets;