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