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