@openg2p/registry-widgets 1.1.0 → 1.1.1

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
@@ -808,6 +808,40 @@ const parseDateFromFormat = (dateString, format) => {
808
808
  }
809
809
  return parseDate(dateString);
810
810
  };
811
+ /**
812
+ * Resolve a stored date value (ISO or parseable string) to YYYY-MM-DD for comparisons.
813
+ */
814
+ const resolveDateBoundFromFieldValue = (fieldValue) => {
815
+ if (fieldValue == null || fieldValue === '') {
816
+ return undefined;
817
+ }
818
+ const iso = formatDateToISO(fieldValue);
819
+ return iso || undefined;
820
+ };
821
+ /**
822
+ * Pick the stricter (later) minimum when combining static and field-based bounds.
823
+ */
824
+ const mergeMinDateBounds = (boundA, boundB) => {
825
+ if (!boundA) {
826
+ return boundB;
827
+ }
828
+ if (!boundB) {
829
+ return boundA;
830
+ }
831
+ return boundA > boundB ? boundA : boundB;
832
+ };
833
+ /**
834
+ * Pick the stricter (earlier) maximum when combining static and field-based bounds.
835
+ */
836
+ const mergeMaxDateBounds = (boundA, boundB) => {
837
+ if (!boundA) {
838
+ return boundB;
839
+ }
840
+ if (!boundB) {
841
+ return boundA;
842
+ }
843
+ return boundA < boundB ? boundA : boundB;
844
+ };
811
845
  /**
812
846
  * Get min date based on constraint type
813
847
  */
@@ -850,10 +884,7 @@ const getMaxDate = (constraint, maxDate) => {
850
884
  }
851
885
  return undefined;
852
886
  };
853
- /**
854
- * Validate date constraints
855
- */
856
- const validateDateConstraints = (date, minDate, maxDate, constraint) => {
887
+ const validateDateConstraints = (date, minDate, maxDate, constraint, messages) => {
857
888
  if (!date)
858
889
  return null;
859
890
  const dateObj = date instanceof Date ? date : parseDate(date);
@@ -872,15 +903,12 @@ const validateDateConstraints = (date, minDate, maxDate, constraint) => {
872
903
  return 'Date must be in the future';
873
904
  }
874
905
  }
875
- // Check minDate
876
- const effectiveMinDate = getMinDate(constraint, minDate);
877
- if (effectiveMinDate && dateISO < effectiveMinDate) {
878
- return `Date must be on or after ${effectiveMinDate}`;
906
+ // minDate / maxDate are effective bounds (static + field-based), resolved by the caller
907
+ if (minDate && dateISO < minDate) {
908
+ return messages?.minDateMessage ?? `Date must be on or after ${minDate}`;
879
909
  }
880
- // Check maxDate
881
- const effectiveMaxDate = getMaxDate(constraint, maxDate);
882
- if (effectiveMaxDate && dateISO > effectiveMaxDate) {
883
- return `Date must be on or before ${effectiveMaxDate}`;
910
+ if (maxDate && dateISO > maxDate) {
911
+ return messages?.maxDateMessage ?? `Date must be on or before ${maxDate}`;
884
912
  }
885
913
  return null;
886
914
  };
@@ -3794,6 +3822,116 @@ const namespaceSectionConfig = (section, namespace) => {
3794
3822
  return namespaced;
3795
3823
  };
3796
3824
 
3825
+ /** Table-style widgets that bind to an array path in the store / schema. */
3826
+ function isTableLikeWidget(widget) {
3827
+ const w = widget.widget;
3828
+ /** widget-type union in types omits legacy values like simple-table still used at runtime */
3829
+ const t = widget['widget-type'];
3830
+ return (w === 'table' ||
3831
+ w === 'dialog-table' ||
3832
+ w === 'simple-table' ||
3833
+ t === 'table' ||
3834
+ t === 'simple-table');
3835
+ }
3836
+ /**
3837
+ * Resolve `records` for section save payloads.
3838
+ * - Back-compat: path ending in `.records` (e.g. `regId.records`)
3839
+ * - Else: first array snapshot at a string `widget-data-path` on a table-like widget
3840
+ * (e.g. `household.members` for dialog-table)
3841
+ */
3842
+ function extractTableRecordsFromSnapshot(snapshot, sectionWidgets) {
3843
+ const convention = Object.entries(snapshot).find(([key, value]) => key.endsWith('.records') && Array.isArray(value));
3844
+ if (convention) {
3845
+ return convention[1];
3846
+ }
3847
+ const tablePaths = [];
3848
+ sectionWidgets.forEach((widget) => {
3849
+ if (!isTableLikeWidget(widget))
3850
+ return;
3851
+ const p = widget['widget-data-path'];
3852
+ if (typeof p === 'string' && p.length > 0) {
3853
+ tablePaths.push(p);
3854
+ }
3855
+ else if (p && typeof p === 'object') {
3856
+ Object.values(p).forEach((sub) => {
3857
+ if (typeof sub === 'string' && sub.length > 0)
3858
+ tablePaths.push(sub);
3859
+ });
3860
+ }
3861
+ });
3862
+ for (const path of tablePaths) {
3863
+ const val = snapshot[path];
3864
+ if (Array.isArray(val)) {
3865
+ return val;
3866
+ }
3867
+ }
3868
+ return [];
3869
+ }
3870
+
3871
+ const isColumnRequired = (column, skipRequired) => {
3872
+ if (skipRequired)
3873
+ return false;
3874
+ const validation = column['widget-data-validation'];
3875
+ return !!(column['widget-required'] || validation?.required);
3876
+ };
3877
+ const validateTableLikeWidget = (widget, currentSchemaData, dispatch, skipRequired) => {
3878
+ const widgetId = widget['widget-id'];
3879
+ if (!widgetId)
3880
+ return true;
3881
+ const columns = (widget['widget-data-columns'] || []);
3882
+ const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3883
+ const rows = Array.isArray(value)
3884
+ ? value
3885
+ : [];
3886
+ const activeRows = rows.filter((row) => row?.edit_action !== 'DELETE');
3887
+ const rowErrors = [];
3888
+ let isValid = true;
3889
+ if (!skipRequired && widget['widget-required'] && activeRows.length === 0) {
3890
+ dispatch(setTouched({ widgetId, touched: true }));
3891
+ dispatch(setError({ widgetId, errors: ['At least one record is required'] }));
3892
+ return false;
3893
+ }
3894
+ const hasRequiredColumns = columns.some((col) => isColumnRequired(col, skipRequired));
3895
+ if (!skipRequired && hasRequiredColumns && activeRows.length === 0) {
3896
+ dispatch(setTouched({ widgetId, touched: true }));
3897
+ dispatch(setError({
3898
+ widgetId,
3899
+ errors: ['Add at least one record and fill all required fields'],
3900
+ }));
3901
+ return false;
3902
+ }
3903
+ activeRows.forEach((row, rowIndex) => {
3904
+ columns.forEach((col) => {
3905
+ if (col['widget-readonly'])
3906
+ return;
3907
+ const key = col['column-key'];
3908
+ if (!key)
3909
+ return;
3910
+ const required = isColumnRequired(col, skipRequired);
3911
+ const cellValue = row[key];
3912
+ const errors = validateWidget(cellValue, col['widget-data-validation'], required, skipRequired);
3913
+ if (errors.length > 0) {
3914
+ isValid = false;
3915
+ const label = col['widget-label'] || key;
3916
+ rowErrors.push(`Row ${rowIndex + 1}, ${label}: ${errors[0]}`);
3917
+ }
3918
+ });
3919
+ });
3920
+ if (!isValid) {
3921
+ dispatch(setTouched({ widgetId, touched: true }));
3922
+ dispatch(setError({
3923
+ widgetId,
3924
+ errors: rowErrors.length > 0
3925
+ ? rowErrors.slice(0, 5)
3926
+ : ['Please fix required fields in the table'],
3927
+ }));
3928
+ }
3929
+ else {
3930
+ dispatch(setTouched({ widgetId, touched: false }));
3931
+ dispatch(setError({ widgetId, errors: [] }));
3932
+ }
3933
+ return isValid;
3934
+ };
3797
3935
  const collectWidgets = (panels) => {
3798
3936
  let widgets = [];
3799
3937
  panels.forEach((panel) => {
@@ -3822,6 +3960,13 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3822
3960
  if (!isVisible)
3823
3961
  continue;
3824
3962
  const widgetId = widget['widget-id'];
3963
+ if (isTableLikeWidget(widget)) {
3964
+ const tableValid = validateTableLikeWidget(widget, currentSchemaData, dispatch, skipRequired);
3965
+ if (!tableValid) {
3966
+ isValid = false;
3967
+ }
3968
+ continue;
3969
+ }
3825
3970
  const value = getWidgetValue(currentSchemaData, widget['widget-data-path'], widgetId);
3826
3971
  const errors = validateWidget(value, widget['widget-data-validation'], widget['widget-required'], skipRequired);
3827
3972
  if (errors.length > 0) {
@@ -3856,52 +4001,6 @@ const sectionValidate = (section, currentSchemaData, dispatch, skipRequired = fa
3856
4001
  return isValid;
3857
4002
  };
3858
4003
 
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
4004
  /** Root class on readonly label/value rows; SectionRenderer scopes overflow/ellipsis rules here. */
3906
4005
  const READONLY_VALUE_ROW_ROOT_CLASSES = [
3907
4006
  'TextDisplayWidget',
@@ -7511,6 +7610,8 @@ const BooleanWidget = ({ config }) => {
7511
7610
  return labels[representation];
7512
7611
  }, [representation, formatConfig, translateConfig]);
7513
7612
  const { trueLabel, falseLabel } = getLabels();
7613
+ const unsetLabel = React.useMemo(() => translateConfig(formatConfig?.booleanUnsetLabel || 'Not set'), [formatConfig?.booleanUnsetLabel, translateConfig]);
7614
+ const radioGroupName = `${widgetConfig['widget-id'] ?? 'boolean'}__${React.useId().replace(/:/g, '')}`;
7514
7615
  // Determine current value (handle null/undefined)
7515
7616
  const currentValue = React.useMemo(() => {
7516
7617
  if (value === null || value === undefined) {
@@ -7542,7 +7643,7 @@ const BooleanWidget = ({ config }) => {
7542
7643
  const label = translateConfig(widgetConfig['widget-label']);
7543
7644
  let displayValue = '';
7544
7645
  if (currentValue === null) {
7545
- displayValue = '-';
7646
+ displayValue = '';
7546
7647
  }
7547
7648
  else if (currentValue === true) {
7548
7649
  displayValue = trueLabel;
@@ -7554,18 +7655,20 @@ const BooleanWidget = ({ config }) => {
7554
7655
  }
7555
7656
  // Render based on control type
7556
7657
  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] }))] })] }) }));
7658
+ 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
7659
  }
7559
7660
  if (controlType === 'radio') {
7560
7661
  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] }))] })] }) }));
7662
+ ? 'flex flex-row flex-wrap items-baseline gap-x-4 gap-y-2'
7663
+ : 'flex flex-col items-start gap-2';
7664
+ const radioDisabled = !isEnabled || widgetConfig['widget-readonly'];
7665
+ const optionDisabledClass = radioDisabled ? 'opacity-50 cursor-not-allowed' : '';
7666
+ 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
7667
  }
7565
7668
  // 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
7669
+ 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
7670
  ? '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
7671
+ : '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
7672
  ? 'bg-blue-600 text-white border-blue-600'
7570
7673
  : '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
7674
  ? 'bg-blue-600 text-white border-blue-600'
@@ -7573,42 +7676,76 @@ const BooleanWidget = ({ config }) => {
7573
7676
  };
7574
7677
 
7575
7678
  const DateInputWidget = ({ config }) => {
7576
- const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7577
- const { translate, translateConfig } = useWidgetTranslation();
7679
+ const { value, error, touched, isEnabled, onChange, onBlur, setError, config: widgetConfig, } = useBaseWidget({ config });
7680
+ const formValues = reactRedux.useSelector((state) => state.widget.values);
7681
+ const { translateConfig } = useWidgetTranslation();
7578
7682
  const formatConfig = widgetConfig['widget-data-format'];
7579
7683
  const optionsConfig = widgetConfig['widget-data-options'];
7580
7684
  const dateFormat = formatConfig?.dateFormat || 'YYYY-MM-DD';
7581
- const inputMethod = formatConfig?.inputMethod || 'picker'; // Default to picker for better UX
7685
+ const inputMethod = formatConfig?.inputMethod || 'picker';
7582
7686
  const dateConstraint = formatConfig?.dateConstraint || 'any';
7583
7687
  const minDate = optionsConfig?.minDate;
7584
7688
  const maxDate = optionsConfig?.maxDate;
7689
+ const minDateField = optionsConfig?.minDateField;
7690
+ const maxDateField = optionsConfig?.maxDateField;
7691
+ const minDateMessage = optionsConfig?.minDateMessage
7692
+ ? translateConfig(optionsConfig.minDateMessage)
7693
+ : undefined;
7694
+ const maxDateMessage = optionsConfig?.maxDateMessage
7695
+ ? translateConfig(optionsConfig.maxDateMessage)
7696
+ : undefined;
7585
7697
  const defaultToToday = widgetConfig['widget-data-default'] === 'today';
7586
- // Track manual input value (for manual/hybrid modes)
7587
7698
  const [manualInputValue, setManualInputValue] = React.useState('');
7588
7699
  const [isFocused, setIsFocused] = React.useState(false);
7589
- // Initialize default value to today if configured
7700
+ const fieldMinDate = React.useMemo(() => {
7701
+ if (!minDateField) {
7702
+ return undefined;
7703
+ }
7704
+ return resolveDateBoundFromFieldValue(getValueByPath(formValues, minDateField));
7705
+ }, [formValues, minDateField]);
7706
+ const fieldMaxDate = React.useMemo(() => {
7707
+ if (!maxDateField) {
7708
+ return undefined;
7709
+ }
7710
+ return resolveDateBoundFromFieldValue(getValueByPath(formValues, maxDateField));
7711
+ }, [formValues, maxDateField]);
7712
+ const effectiveMinDate = React.useMemo(() => {
7713
+ const staticMin = getMinDate(dateConstraint, minDate);
7714
+ return mergeMinDateBounds(staticMin, fieldMinDate);
7715
+ }, [dateConstraint, minDate, fieldMinDate]);
7716
+ const effectiveMaxDate = React.useMemo(() => {
7717
+ const staticMax = getMaxDate(dateConstraint, maxDate);
7718
+ return mergeMaxDateBounds(staticMax, fieldMaxDate);
7719
+ }, [dateConstraint, maxDate, fieldMaxDate]);
7720
+ const constraintMessages = React.useMemo(() => ({ minDateMessage, maxDateMessage }), [minDateMessage, maxDateMessage]);
7721
+ const runDateConstraintValidation = React.useCallback((dateValue) => {
7722
+ if (!dateValue) {
7723
+ return null;
7724
+ }
7725
+ return validateDateConstraints(dateValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
7726
+ }, [effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages]);
7590
7727
  React.useEffect(() => {
7591
7728
  if (defaultToToday && (value === null || value === undefined || value === '')) {
7592
7729
  const todayISO = formatDateToISO(new Date());
7593
7730
  onChange(todayISO);
7594
7731
  }
7595
7732
  }, [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
7733
+ // Re-validate when a relative bound field changes (e.g. start date set after end date)
7734
+ React.useEffect(() => {
7735
+ if (!value) {
7736
+ return;
7737
+ }
7738
+ const constraintError = runDateConstraintValidation(value);
7739
+ if (constraintError) {
7740
+ setError([constraintError]);
7741
+ }
7742
+ }, [fieldMinDate, fieldMaxDate, value, runDateConstraintValidation, setError]);
7604
7743
  const getDisplayValue = React.useCallback(() => {
7605
- // For picker mode, always use YYYY-MM-DD
7606
7744
  if (inputMethod === 'picker') {
7607
7745
  if (!value)
7608
7746
  return '';
7609
7747
  return formatDateToISO(value);
7610
7748
  }
7611
- // For manual/hybrid modes, use custom format
7612
7749
  if (isFocused && manualInputValue) {
7613
7750
  return manualInputValue;
7614
7751
  }
@@ -7619,7 +7756,6 @@ const DateInputWidget = ({ config }) => {
7619
7756
  }
7620
7757
  return formatDateToString(value, dateFormat);
7621
7758
  }, [value, inputMethod, dateFormat, isFocused, manualInputValue]);
7622
- // Initialize manual input value
7623
7759
  React.useEffect(() => {
7624
7760
  if (!isFocused && value) {
7625
7761
  if (dateFormat === 'YYYY-MM-DD') {
@@ -7630,66 +7766,81 @@ const DateInputWidget = ({ config }) => {
7630
7766
  }
7631
7767
  }
7632
7768
  }, [value, dateFormat, isFocused]);
7633
- // Handle input change
7769
+ const applyConstraintError = React.useCallback((dateValue) => {
7770
+ const constraintError = runDateConstraintValidation(dateValue);
7771
+ setError(constraintError ? [constraintError] : []);
7772
+ }, [runDateConstraintValidation, setError]);
7634
7773
  const handleChange = React.useCallback((e) => {
7635
7774
  const inputValue = e.target.value;
7636
7775
  if (inputMethod === 'picker') {
7637
- // Picker mode: input is always YYYY-MM-DD
7638
7776
  if (inputValue) {
7639
7777
  const date = parseDate(inputValue);
7640
7778
  if (date) {
7641
- onChange(formatDateToISO(date));
7779
+ const iso = formatDateToISO(date);
7780
+ onChange(iso);
7781
+ applyConstraintError(iso);
7642
7782
  }
7643
7783
  else {
7644
7784
  onChange('');
7785
+ setError([]);
7645
7786
  }
7646
7787
  }
7647
7788
  else {
7648
7789
  onChange('');
7790
+ setError([]);
7649
7791
  }
7650
7792
  }
7651
7793
  else {
7652
- // Manual/hybrid mode: parse custom format
7653
7794
  setManualInputValue(inputValue);
7654
7795
  if (inputValue) {
7655
7796
  const date = parseDateFromFormat(inputValue, dateFormat);
7656
7797
  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
- }
7798
+ const iso = formatDateToISO(date);
7799
+ onChange(iso);
7800
+ applyConstraintError(iso);
7666
7801
  }
7667
7802
  }
7668
7803
  else {
7669
7804
  onChange('');
7805
+ setError([]);
7670
7806
  }
7671
7807
  }
7672
- }, [inputMethod, dateFormat, onChange, minDate, maxDate, dateConstraint]);
7673
- // Handle blur - validate and format
7808
+ }, [inputMethod, dateFormat, onChange, applyConstraintError, setError]);
7674
7809
  const handleBlur = React.useCallback(() => {
7675
7810
  setIsFocused(false);
7676
7811
  if (inputMethod !== 'picker' && manualInputValue) {
7677
7812
  const date = parseDateFromFormat(manualInputValue, dateFormat);
7678
7813
  if (date) {
7679
- // Format the value according to the format
7680
7814
  const formatted = formatDateToString(date, dateFormat);
7681
7815
  setManualInputValue(formatted);
7682
- onChange(formatDateToISO(date));
7816
+ const iso = formatDateToISO(date);
7817
+ onChange(iso);
7818
+ applyConstraintError(iso);
7683
7819
  }
7684
7820
  else {
7685
- // Invalid date, clear it
7686
7821
  setManualInputValue('');
7687
7822
  onChange('');
7823
+ setError([]);
7688
7824
  }
7689
7825
  }
7690
7826
  onBlur();
7691
- }, [inputMethod, manualInputValue, dateFormat, onChange, onBlur]);
7692
- // Handle focus
7827
+ if (value) {
7828
+ const constraintError = runDateConstraintValidation(value);
7829
+ if (constraintError) {
7830
+ setError([constraintError]);
7831
+ }
7832
+ }
7833
+ }, [
7834
+ inputMethod,
7835
+ manualInputValue,
7836
+ dateFormat,
7837
+ onChange,
7838
+ onBlur,
7839
+ applyConstraintError,
7840
+ value,
7841
+ runDateConstraintValidation,
7842
+ setError,
7843
+ ]);
7693
7844
  const handleFocus = React.useCallback(() => {
7694
7845
  setIsFocused(true);
7695
7846
  if (value) {
@@ -7701,15 +7852,15 @@ const DateInputWidget = ({ config }) => {
7701
7852
  }
7702
7853
  }
7703
7854
  }, [value, dateFormat]);
7704
- // Determine placeholder
7705
7855
  const placeholder = React.useMemo(() => {
7706
- const hasValue = getDisplayValue() && getDisplayValue().trim().length > 0;
7856
+ const display = getDisplayValue();
7857
+ const hasValue = display && display.trim().length > 0;
7707
7858
  const placeholderText = translateConfig(widgetConfig['widget-data-placeholder']);
7708
- return hasValue ? undefined : (placeholderText || dateFormat);
7859
+ return hasValue ? undefined : placeholderText || dateFormat;
7709
7860
  }, [getDisplayValue, widgetConfig, translateConfig, dateFormat]);
7710
- // Determine input type
7711
7861
  const inputType = inputMethod === 'picker' ? 'date' : 'text';
7712
- // For readonly mode, render as display text
7862
+ const showRequiredError = widgetConfig['widget-required'] && (!value || value === '');
7863
+ const showValidationError = touched && error.length > 0;
7713
7864
  if (widgetConfig['widget-readonly']) {
7714
7865
  const label = translateConfig(widgetConfig['widget-label']);
7715
7866
  let displayValue = '';
@@ -7726,9 +7877,9 @@ const DateInputWidget = ({ config }) => {
7726
7877
  }
7727
7878
  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
7879
  }
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 === ''))
7880
+ 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
7881
  ? '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] }))] })] }) }));
7882
+ : '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
7883
  };
7733
7884
 
7734
7885
  /**
@@ -8276,7 +8427,7 @@ const CheckboxWidget = ({ config }) => {
8276
8427
  const displayValue = isChecked ? 'Yes' : 'No';
8277
8428
  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
8429
  }
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] }))] })] }) }));
8430
+ 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
8431
  }
8281
8432
  // Multiple checkboxes (with data source) - for array values
8282
8433
  // Process and sort options if needed
@@ -8319,7 +8470,7 @@ const CheckboxWidget = ({ config }) => {
8319
8470
  switch (layout) {
8320
8471
  case 'horizontal':
8321
8472
  return {
8322
- className: 'flex flex-row flex-wrap gap-4',
8473
+ className: 'flex flex-row flex-wrap items-baseline gap-4',
8323
8474
  style: undefined,
8324
8475
  };
8325
8476
  case 'grid':
@@ -8332,7 +8483,7 @@ const CheckboxWidget = ({ config }) => {
8332
8483
  case 'vertical':
8333
8484
  default:
8334
8485
  return {
8335
- className: 'flex flex-col space-y-2',
8486
+ className: 'flex flex-col gap-2',
8336
8487
  style: undefined,
8337
8488
  };
8338
8489
  }
@@ -8346,7 +8497,7 @@ const CheckboxWidget = ({ config }) => {
8346
8497
  : '-';
8347
8498
  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
8499
  }
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] }))] })] }) }));
8500
+ 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
8501
  };
8351
8502
 
8352
8503
  const SimpleTableWidget = ({ config }) => {
@@ -8672,16 +8823,70 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
8672
8823
  backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8673
8824
  } }));
8674
8825
  };
8675
- const TableCellDate = ({ config, value, onValueChange }) => {
8826
+ const TableCellDate = ({ config, value, rowValues, onValueChange }) => {
8827
+ const { translateConfig } = useWidgetTranslation();
8676
8828
  const isReadonly = config['widget-readonly'] || false;
8677
8829
  const placeholder = config['widget-data-placeholder'] || '';
8830
+ const optionsConfig = config['widget-data-options'];
8831
+ const formatConfig = config['widget-data-format'];
8832
+ const dateConstraint = formatConfig?.dateConstraint || 'any';
8833
+ const minDate = optionsConfig?.minDate;
8834
+ const maxDate = optionsConfig?.maxDate;
8835
+ const minDateField = optionsConfig?.minDateField;
8836
+ const maxDateField = optionsConfig?.maxDateField;
8837
+ const minDateMessage = optionsConfig?.minDateMessage
8838
+ ? translateConfig(optionsConfig.minDateMessage)
8839
+ : undefined;
8840
+ const maxDateMessage = optionsConfig?.maxDateMessage
8841
+ ? translateConfig(optionsConfig.maxDateMessage)
8842
+ : undefined;
8843
+ const [constraintError, setConstraintError] = React.useState(null);
8844
+ const resolveSiblingDate = (fieldRef) => {
8845
+ if (!fieldRef || !rowValues) {
8846
+ return undefined;
8847
+ }
8848
+ const raw = getValueByPath(rowValues, fieldRef) ?? rowValues[fieldRef];
8849
+ return resolveDateBoundFromFieldValue(raw);
8850
+ };
8851
+ const fieldMinDate = React.useMemo(() => resolveSiblingDate(minDateField), [minDateField, rowValues]);
8852
+ const fieldMaxDate = React.useMemo(() => resolveSiblingDate(maxDateField), [maxDateField, rowValues]);
8853
+ const effectiveMinDate = React.useMemo(() => {
8854
+ const staticMin = getMinDate(dateConstraint, minDate);
8855
+ return mergeMinDateBounds(staticMin, fieldMinDate);
8856
+ }, [dateConstraint, minDate, fieldMinDate]);
8857
+ const effectiveMaxDate = React.useMemo(() => {
8858
+ const staticMax = getMaxDate(dateConstraint, maxDate);
8859
+ return mergeMaxDateBounds(staticMax, fieldMaxDate);
8860
+ }, [dateConstraint, maxDate, fieldMaxDate]);
8861
+ const constraintMessages = React.useMemo(() => ({ minDateMessage, maxDateMessage }), [minDateMessage, maxDateMessage]);
8678
8862
  // input type="date" requires YYYY-MM-DD format
8679
8863
  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
- } }));
8864
+ React.useEffect(() => {
8865
+ if (!displayValue) {
8866
+ setConstraintError(null);
8867
+ return;
8868
+ }
8869
+ const error = validateDateConstraints(displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
8870
+ setConstraintError(error);
8871
+ }, [displayValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages]);
8872
+ const handleChange = (e) => {
8873
+ const nextValue = e.target.value;
8874
+ onValueChange(nextValue);
8875
+ if (!nextValue) {
8876
+ setConstraintError(null);
8877
+ return;
8878
+ }
8879
+ const error = validateDateConstraints(nextValue, effectiveMinDate, effectiveMaxDate, dateConstraint, constraintMessages);
8880
+ setConstraintError(error);
8881
+ };
8882
+ const hasError = Boolean(constraintError);
8883
+ 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: {
8884
+ borderRadius: '10px',
8885
+ borderColor: hasError
8886
+ ? 'var(--owt-color-error, #B91C1C)'
8887
+ : 'var(--owt-widget-input-border, #C4C4C4)',
8888
+ backgroundColor: isReadonly ? 'var(--owt-color-bg-alt, #F6F6F6)' : 'var(--owt-color-bg, #FFFFFF)',
8889
+ } }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-xs mt-0.5 leading-tight", children: constraintError }))] }));
8685
8890
  };
8686
8891
  const TableWidget = ({ config }) => {
8687
8892
  const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
@@ -9040,11 +9245,21 @@ const TableWidget = ({ config }) => {
9040
9245
  });
9041
9246
  }
9042
9247
  }, [isAdding, newRowData, columns, widgetConfig, rows.length, dispatch]);
9248
+ const getRowValuesForEdit = React.useCallback((rowIndex) => {
9249
+ if (editingState && editingState.rowIndex === rowIndex) {
9250
+ return editingState.currentValue ?? {};
9251
+ }
9252
+ if (isAdding && rowIndex === rows.length && newRowData) {
9253
+ return newRowData;
9254
+ }
9255
+ return rows[rowIndex] ?? {};
9256
+ }, [editingState, isAdding, rows, newRowData]);
9043
9257
  // Lightweight cell renderer for table cells (no labels, compact)
9044
9258
  const renderTableCell = React.useCallback((rowIndex, column, cellValue, isReadonly) => {
9045
9259
  const columnKey = column['column-key'];
9046
9260
  const widgetType = column.widget || 'text';
9047
9261
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
9262
+ const rowValues = getRowValuesForEdit(rowIndex);
9048
9263
  // Use lightweight cell config (no label, minimal styling)
9049
9264
  const cellConfig = {
9050
9265
  ...column,
@@ -9067,7 +9282,7 @@ const TableWidget = ({ config }) => {
9067
9282
  return jsxRuntimeExports.jsx(TableCellNumber, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9068
9283
  }
9069
9284
  else if (widgetType === 'date') {
9070
- return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9285
+ return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, rowValues: rowValues, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
9071
9286
  }
9072
9287
  // For other widget types, use WidgetRenderer but with compact styling
9073
9288
  return (jsxRuntimeExports.jsx("div", { className: "table-cell-widget", style: { margin: 0, padding: 0 }, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
@@ -9075,7 +9290,7 @@ const TableWidget = ({ config }) => {
9075
9290
  }, onValueChange: (widgetId, newValue) => {
9076
9291
  updateCellValue(columnKey, newValue, rowIndex);
9077
9292
  } }) }));
9078
- }, [widgetConfig, updateCellValue]);
9293
+ }, [widgetConfig, updateCellValue, getRowValuesForEdit]);
9079
9294
  // Render cell content (widget in edit mode, formatted value in view mode)
9080
9295
  const renderCell = React.useCallback((rowIndex, column, row) => {
9081
9296
  const columnKey = column['column-key'];
@@ -9385,6 +9600,27 @@ const DialogTableWidget = ({ config }) => {
9385
9600
  }, [formData, columns, storeValues, dialogFieldWidgetId]);
9386
9601
  const saveDialog = React.useCallback(() => {
9387
9602
  const payload = collectMergedRowPayload();
9603
+ let hasErrors = false;
9604
+ columns.forEach((col) => {
9605
+ const key = col['column-key'];
9606
+ const cellWidgetId = dialogFieldWidgetId(key);
9607
+ const isColReadonly = isReadonly || col['widget-readonly'] === true;
9608
+ if (isColReadonly)
9609
+ return;
9610
+ const cellValue = payload[key];
9611
+ const validationErrors = validateWidget(cellValue, col['widget-data-validation'], col['widget-required']);
9612
+ if (validationErrors && validationErrors.length > 0) {
9613
+ hasErrors = true;
9614
+ dispatch(setError({ widgetId: cellWidgetId, errors: validationErrors }));
9615
+ dispatch(setTouched({ widgetId: cellWidgetId, touched: true }));
9616
+ }
9617
+ else {
9618
+ dispatch(setError({ widgetId: cellWidgetId, errors: [] }));
9619
+ }
9620
+ });
9621
+ if (hasErrors) {
9622
+ return;
9623
+ }
9388
9624
  if (dialogMode === 'add') {
9389
9625
  const savedRow = { ...payload, edit_action: 'ADD' };
9390
9626
  onChange([...rows, savedRow]);
@@ -9400,7 +9636,7 @@ const DialogTableWidget = ({ config }) => {
9400
9636
  onChange(newRows);
9401
9637
  closeDialog();
9402
9638
  }
9403
- }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex]);
9639
+ }, [collectMergedRowPayload, dialogMode, onChange, rows, closeDialog, activeRowIndex, columns, dialogFieldWidgetId, isReadonly, dispatch]);
9404
9640
  const deleteRow = React.useCallback((rowIndex) => {
9405
9641
  const newRows = rows.filter((_, i) => i !== rowIndex);
9406
9642
  onChange(newRows);
@@ -10226,20 +10462,29 @@ const HeaderSectionWidget = ({ config }) => {
10226
10462
  .${cls} .hdr-field-row {
10227
10463
  display: flex;
10228
10464
  align-items: flex-start;
10229
- gap: 0.5rem;
10230
10465
  font-size: 1rem;
10231
10466
  line-height: 1.6;
10232
10467
  }
10233
10468
 
10234
10469
  .${cls} .hdr-field-label {
10470
+ width: 50%;
10471
+ flex: 0 0 50%;
10235
10472
  color: rgba(0, 0, 0, 0.5);
10236
10473
  font-weight: 400;
10237
10474
  white-space: nowrap;
10475
+ overflow: hidden;
10476
+ text-overflow: ellipsis;
10477
+ padding-right: 4px;
10238
10478
  }
10239
10479
 
10240
10480
  .${cls} .hdr-field-value {
10481
+ width: 50%;
10482
+ flex: 0 0 50%;
10241
10483
  color: var(--owt-color-text, #111827);
10242
10484
  font-weight: 500;
10485
+ white-space: nowrap;
10486
+ overflow: hidden;
10487
+ text-overflow: ellipsis;
10243
10488
  }
10244
10489
 
10245
10490
  .${cls} .hdr-status-badge {
@@ -10249,24 +10494,38 @@ const HeaderSectionWidget = ({ config }) => {
10249
10494
  font-size: 0.75rem;
10250
10495
  font-weight: 600;
10251
10496
  color: #fff;
10497
+ max-width: 100%;
10498
+ overflow: hidden;
10499
+ text-overflow: ellipsis;
10500
+ white-space: nowrap;
10252
10501
  }
10253
10502
 
10254
10503
  .${cls} .hdr-meta-row {
10255
10504
  display: flex;
10256
10505
  align-items: baseline;
10257
- gap: 0.35rem;
10258
10506
  font-size: 1rem;
10259
10507
  line-height: 1.6;
10260
10508
  }
10261
10509
 
10262
10510
  .${cls} .hdr-meta-label {
10511
+ width: 50%;
10512
+ flex: 0 0 50%;
10263
10513
  color: rgba(0, 0, 0, 0.5);
10264
10514
  font-weight: 400;
10515
+ white-space: nowrap;
10516
+ overflow: hidden;
10517
+ text-overflow: ellipsis;
10518
+ padding-right: 4px;
10265
10519
  }
10266
10520
 
10267
10521
  .${cls} .hdr-meta-value {
10522
+ width: 50%;
10523
+ flex: 0 0 50%;
10268
10524
  color: var(--owt-color-text, #111827);
10269
10525
  font-weight: 500;
10526
+ white-space: nowrap;
10527
+ overflow: hidden;
10528
+ text-overflow: ellipsis;
10270
10529
  }
10271
10530
 
10272
10531
  .${cls} .hdr-select {
@@ -10330,7 +10589,7 @@ const HeaderSectionWidget = ({ config }) => {
10330
10589
  .parentElement?.querySelector('.hdr-avatar-placeholder');
10331
10590
  if (placeholder)
10332
10591
  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: () => {
10592
+ } })) : 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
10593
  if (isReasonMissing)
10335
10594
  setShowReasonRequired(true);
10336
10595
  }, onChange: (e) => {
@@ -10338,7 +10597,7 @@ const HeaderSectionWidget = ({ config }) => {
10338
10597
  if (showReasonRequired && String(e.target.value || '').trim().length > 0) {
10339
10598
  setShowReasonRequired(false);
10340
10599
  }
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] }) })] })] }));
10600
+ } }), !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
10601
  };
10343
10602
 
10344
10603
  function getValueByPathOrKey(obj, path) {