@openg2p/registry-widgets 0.1.7 → 0.1.9
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/SectionRenderer.d.ts.map +1 -1
- package/dist/components/SectionsContainer.d.ts.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.esm.js +607 -269
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +607 -268
- package/dist/index.js.map +1 -1
- package/dist/registry/defaultWidgets.d.ts.map +1 -1
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/widgets/HeaderSectionWidget.d.ts +7 -0
- package/dist/widgets/HeaderSectionWidget.d.ts.map +1 -0
- package/dist/widgets/TableWidget.d.ts.map +1 -1
- package/dist/widgets/index.d.ts +1 -0
- package/dist/widgets/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -670,23 +670,235 @@ const getFormattedNumberLength = (value, format) => {
|
|
|
670
670
|
return formatted.length;
|
|
671
671
|
};
|
|
672
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Date input utilities for parsing, formatting, and validation
|
|
675
|
+
*/
|
|
676
|
+
/**
|
|
677
|
+
* Parse date string in various formats to Date object
|
|
678
|
+
*/
|
|
679
|
+
const parseDate = (dateString) => {
|
|
680
|
+
if (!dateString)
|
|
681
|
+
return null;
|
|
682
|
+
// Try ISO format first (YYYY-MM-DD)
|
|
683
|
+
const isoMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
684
|
+
if (isoMatch) {
|
|
685
|
+
const date = new Date(parseInt(isoMatch[1]), parseInt(isoMatch[2]) - 1, parseInt(isoMatch[3]));
|
|
686
|
+
if (!isNaN(date.getTime())) {
|
|
687
|
+
return date;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// Try common formats
|
|
691
|
+
const date = new Date(dateString);
|
|
692
|
+
if (!isNaN(date.getTime())) {
|
|
693
|
+
return date;
|
|
694
|
+
}
|
|
695
|
+
return null;
|
|
696
|
+
};
|
|
697
|
+
/**
|
|
698
|
+
* Format date to ISO string (YYYY-MM-DD) for storage
|
|
699
|
+
*/
|
|
700
|
+
const formatDateToISO = (date) => {
|
|
701
|
+
if (!date)
|
|
702
|
+
return '';
|
|
703
|
+
let dateObj;
|
|
704
|
+
if (date instanceof Date) {
|
|
705
|
+
dateObj = date;
|
|
706
|
+
}
|
|
707
|
+
else if (typeof date === 'string') {
|
|
708
|
+
const parsed = parseDate(date);
|
|
709
|
+
if (!parsed)
|
|
710
|
+
return '';
|
|
711
|
+
dateObj = parsed;
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
return '';
|
|
715
|
+
}
|
|
716
|
+
if (isNaN(dateObj.getTime()))
|
|
717
|
+
return '';
|
|
718
|
+
const year = dateObj.getFullYear();
|
|
719
|
+
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
720
|
+
const day = String(dateObj.getDate()).padStart(2, '0');
|
|
721
|
+
return `${year}-${month}-${day}`;
|
|
722
|
+
};
|
|
723
|
+
/**
|
|
724
|
+
* Format date to custom format string
|
|
725
|
+
*/
|
|
726
|
+
const formatDateToString = (date, format) => {
|
|
727
|
+
if (!date || !format)
|
|
728
|
+
return '';
|
|
729
|
+
let dateObj;
|
|
730
|
+
if (date instanceof Date) {
|
|
731
|
+
dateObj = date;
|
|
732
|
+
}
|
|
733
|
+
else if (typeof date === 'string') {
|
|
734
|
+
const parsed = parseDate(date);
|
|
735
|
+
if (!parsed)
|
|
736
|
+
return '';
|
|
737
|
+
dateObj = parsed;
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
return '';
|
|
741
|
+
}
|
|
742
|
+
if (isNaN(dateObj.getTime()))
|
|
743
|
+
return '';
|
|
744
|
+
const day = String(dateObj.getDate()).padStart(2, '0');
|
|
745
|
+
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
746
|
+
const year = dateObj.getFullYear();
|
|
747
|
+
const yearShort = year.toString().slice(-2);
|
|
748
|
+
return format
|
|
749
|
+
.replace(/DD/g, day)
|
|
750
|
+
.replace(/MM/g, month)
|
|
751
|
+
.replace(/YYYY/g, year.toString())
|
|
752
|
+
.replace(/YY/g, yearShort);
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* Parse custom format string to Date object
|
|
756
|
+
*/
|
|
757
|
+
const parseDateFromFormat = (dateString, format) => {
|
|
758
|
+
if (!dateString || !format)
|
|
759
|
+
return null;
|
|
760
|
+
// Extract day, month, year positions from format
|
|
761
|
+
const dayIndex = format.indexOf('DD');
|
|
762
|
+
const monthIndex = format.indexOf('MM');
|
|
763
|
+
const yearIndex = format.indexOf('YYYY');
|
|
764
|
+
const yearShortIndex = format.indexOf('YY');
|
|
765
|
+
if (dayIndex === -1 || monthIndex === -1 || (yearIndex === -1 && yearShortIndex === -1)) {
|
|
766
|
+
// Fallback to standard Date parsing
|
|
767
|
+
return parseDate(dateString);
|
|
768
|
+
}
|
|
769
|
+
try {
|
|
770
|
+
let day = '';
|
|
771
|
+
let month = '';
|
|
772
|
+
let year = '';
|
|
773
|
+
// Extract day (2 digits)
|
|
774
|
+
if (dayIndex !== -1) {
|
|
775
|
+
day = dateString.substring(dayIndex, dayIndex + 2);
|
|
776
|
+
}
|
|
777
|
+
// Extract month (2 digits)
|
|
778
|
+
if (monthIndex !== -1) {
|
|
779
|
+
month = dateString.substring(monthIndex, monthIndex + 2);
|
|
780
|
+
}
|
|
781
|
+
// Extract year (4 digits or 2 digits)
|
|
782
|
+
if (yearIndex !== -1) {
|
|
783
|
+
year = dateString.substring(yearIndex, yearIndex + 4);
|
|
784
|
+
}
|
|
785
|
+
else if (yearShortIndex !== -1) {
|
|
786
|
+
const yearShort = dateString.substring(yearShortIndex, yearShortIndex + 2);
|
|
787
|
+
const currentYear = new Date().getFullYear();
|
|
788
|
+
const currentCentury = Math.floor(currentYear / 100) * 100;
|
|
789
|
+
const parsedYear = parseInt(yearShort);
|
|
790
|
+
// Assume 20xx for years 00-50, 19xx for years 51-99
|
|
791
|
+
year = parsedYear <= 50
|
|
792
|
+
? (currentCentury + parsedYear).toString()
|
|
793
|
+
: (currentCentury - 100 + parsedYear).toString();
|
|
794
|
+
}
|
|
795
|
+
if (day && month && year) {
|
|
796
|
+
const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
797
|
+
if (!isNaN(date.getTime())) {
|
|
798
|
+
return date;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
// Fallback to standard parsing
|
|
804
|
+
}
|
|
805
|
+
return parseDate(dateString);
|
|
806
|
+
};
|
|
807
|
+
/**
|
|
808
|
+
* Get min date based on constraint type
|
|
809
|
+
*/
|
|
810
|
+
const getMinDate = (constraint, minDate) => {
|
|
811
|
+
if (minDate) {
|
|
812
|
+
// If it's 'today', return today's date
|
|
813
|
+
if (minDate === 'today') {
|
|
814
|
+
return formatDateToISO(new Date());
|
|
815
|
+
}
|
|
816
|
+
return minDate;
|
|
817
|
+
}
|
|
818
|
+
if (constraint === 'past-only') {
|
|
819
|
+
// No minimum for past-only (can select any past date)
|
|
820
|
+
return undefined;
|
|
821
|
+
}
|
|
822
|
+
if (constraint === 'future-only') {
|
|
823
|
+
// Minimum is today for future-only
|
|
824
|
+
return formatDateToISO(new Date());
|
|
825
|
+
}
|
|
826
|
+
return undefined;
|
|
827
|
+
};
|
|
828
|
+
/**
|
|
829
|
+
* Get max date based on constraint type
|
|
830
|
+
*/
|
|
831
|
+
const getMaxDate = (constraint, maxDate) => {
|
|
832
|
+
if (maxDate) {
|
|
833
|
+
// If it's 'today', return today's date
|
|
834
|
+
if (maxDate === 'today') {
|
|
835
|
+
return formatDateToISO(new Date());
|
|
836
|
+
}
|
|
837
|
+
return maxDate;
|
|
838
|
+
}
|
|
839
|
+
if (constraint === 'past-only') {
|
|
840
|
+
// Maximum is today for past-only
|
|
841
|
+
return formatDateToISO(new Date());
|
|
842
|
+
}
|
|
843
|
+
if (constraint === 'future-only') {
|
|
844
|
+
// No maximum for future-only (can select any future date)
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
return undefined;
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* Validate date constraints
|
|
851
|
+
*/
|
|
852
|
+
const validateDateConstraints = (date, minDate, maxDate, constraint) => {
|
|
853
|
+
if (!date)
|
|
854
|
+
return null;
|
|
855
|
+
const dateObj = date instanceof Date ? date : parseDate(date);
|
|
856
|
+
if (!dateObj)
|
|
857
|
+
return null;
|
|
858
|
+
const dateISO = formatDateToISO(dateObj);
|
|
859
|
+
const todayISO = formatDateToISO(new Date());
|
|
860
|
+
// Check constraint type
|
|
861
|
+
if (constraint === 'past-only') {
|
|
862
|
+
if (dateISO > todayISO) {
|
|
863
|
+
return 'Date must be in the past';
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else if (constraint === 'future-only') {
|
|
867
|
+
if (dateISO < todayISO) {
|
|
868
|
+
return 'Date must be in the future';
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
// Check minDate
|
|
872
|
+
const effectiveMinDate = getMinDate(constraint, minDate);
|
|
873
|
+
if (effectiveMinDate && dateISO < effectiveMinDate) {
|
|
874
|
+
return `Date must be on or after ${effectiveMinDate}`;
|
|
875
|
+
}
|
|
876
|
+
// Check maxDate
|
|
877
|
+
const effectiveMaxDate = getMaxDate(constraint, maxDate);
|
|
878
|
+
if (effectiveMaxDate && dateISO > effectiveMaxDate) {
|
|
879
|
+
return `Date must be on or before ${effectiveMaxDate}`;
|
|
880
|
+
}
|
|
881
|
+
return null;
|
|
882
|
+
};
|
|
883
|
+
|
|
673
884
|
/**
|
|
674
885
|
* Format date value
|
|
675
886
|
*/
|
|
676
887
|
const formatDate = (value, format) => {
|
|
677
|
-
if (!value
|
|
678
|
-
return
|
|
888
|
+
if (!value) {
|
|
889
|
+
return '';
|
|
679
890
|
}
|
|
891
|
+
const dateFormat = format?.dateFormat || 'DD-MM-YYYY';
|
|
680
892
|
try {
|
|
681
|
-
const date = new Date(value);
|
|
682
|
-
if (isNaN(date.getTime())) {
|
|
893
|
+
const date = typeof value === 'string' ? parseDate(value) : new Date(value);
|
|
894
|
+
if (!date || isNaN(date.getTime())) {
|
|
683
895
|
return value?.toString() || '';
|
|
684
896
|
}
|
|
685
897
|
// Simple date formatting (can be enhanced with date-fns or similar)
|
|
686
898
|
const day = date.getDate().toString().padStart(2, '0');
|
|
687
899
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
688
900
|
const year = date.getFullYear();
|
|
689
|
-
return
|
|
901
|
+
return dateFormat
|
|
690
902
|
.replace('DD', day)
|
|
691
903
|
.replace('MM', month)
|
|
692
904
|
.replace('YYYY', year.toString())
|
|
@@ -3622,8 +3834,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
3622
3834
|
return [];
|
|
3623
3835
|
const snapshot = {};
|
|
3624
3836
|
let hasTable = false;
|
|
3625
|
-
if (!sectionRegisterId)
|
|
3626
|
-
return [];
|
|
3627
3837
|
const resolvePath = (path) => (pathPrefix ? `${pathPrefix}.${path}` : path);
|
|
3628
3838
|
widgets.forEach(widget => {
|
|
3629
3839
|
const widgetPath = widget['widget-data-path'];
|
|
@@ -3653,9 +3863,11 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
3653
3863
|
: fullPath;
|
|
3654
3864
|
cleanedSnapshot[fieldPath] = value;
|
|
3655
3865
|
});
|
|
3656
|
-
const sectionData =
|
|
3657
|
-
?
|
|
3658
|
-
|
|
3866
|
+
const sectionData = sectionRegisterId
|
|
3867
|
+
? (pathPrefix
|
|
3868
|
+
? getValueByPath(sourceData, resolvePath(sectionRegisterId))
|
|
3869
|
+
: sourceData[sectionRegisterId])
|
|
3870
|
+
: {};
|
|
3659
3871
|
return [
|
|
3660
3872
|
{
|
|
3661
3873
|
...sectionData,
|
|
@@ -3807,7 +4019,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
3807
4019
|
if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
|
|
3808
4020
|
try {
|
|
3809
4021
|
const sectionchanges = {
|
|
3810
|
-
section_id: dbSectionId,
|
|
4022
|
+
section_id: dbSectionId ?? originalSection['section-id'],
|
|
3811
4023
|
section_register_id: sectionRegisterId,
|
|
3812
4024
|
records: [...newSchemaData],
|
|
3813
4025
|
files: [...sectionFiles]
|
|
@@ -3822,49 +4034,52 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
3822
4034
|
};
|
|
3823
4035
|
// IntakeForm: save section then collapse current and expand next (or stay on final section)
|
|
3824
4036
|
const handleIntakeFormSave = useCallback(async () => {
|
|
3825
|
-
if (
|
|
3826
|
-
return;
|
|
3827
|
-
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
3828
|
-
const currentState = store.getState().widget;
|
|
3829
|
-
const currentSchemaData = currentState.values || {};
|
|
3830
|
-
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
|
|
3831
|
-
if (!isSectionValid)
|
|
4037
|
+
if (sectionIndex === undefined)
|
|
3832
4038
|
return;
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
const
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
files: [...sectionFiles],
|
|
4039
|
+
// Only run save/validation logic when in draft mode and handlers are available
|
|
4040
|
+
if (isDraft !== false && store && onSectionSave) {
|
|
4041
|
+
const sectionWidgets = collectWidgets(originalSection.panels);
|
|
4042
|
+
const currentState = store.getState().widget;
|
|
4043
|
+
const currentSchemaData = currentState.values || {};
|
|
4044
|
+
const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
|
|
4045
|
+
if (!isSectionValid)
|
|
4046
|
+
return;
|
|
4047
|
+
const oldSchemaData = schemaData || contextSchemaData;
|
|
4048
|
+
const newSchemaData = trackSectionChages(sectionWidgets, currentSchemaData, namespace);
|
|
4049
|
+
const sectionFiles = [];
|
|
4050
|
+
if (hasSupportingDocuments) {
|
|
4051
|
+
const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
|
|
4052
|
+
originalSupportingDocuments.forEach((doc) => {
|
|
4053
|
+
const originalDataPath = doc['document-data-path'];
|
|
4054
|
+
const storeDataPath = namespace && originalDataPath
|
|
4055
|
+
? `${namespace}.${originalDataPath}`
|
|
4056
|
+
: originalDataPath;
|
|
4057
|
+
sectionFiles.push(getValueByPath(currentSchemaData, storeDataPath));
|
|
3853
4058
|
});
|
|
3854
4059
|
}
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
4060
|
+
if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
|
|
4061
|
+
try {
|
|
4062
|
+
await onSectionSave({
|
|
4063
|
+
section_id: dbSectionId ?? originalSection['section-id'],
|
|
4064
|
+
section_register_id: sectionRegisterId,
|
|
4065
|
+
records: [...newSchemaData],
|
|
4066
|
+
files: [...sectionFiles],
|
|
4067
|
+
});
|
|
4068
|
+
}
|
|
4069
|
+
catch (error) {
|
|
4070
|
+
console.error('Section Changes Save failed', error);
|
|
4071
|
+
return;
|
|
4072
|
+
}
|
|
3858
4073
|
}
|
|
4074
|
+
if (mode === 'IntakeForm') {
|
|
4075
|
+
baselineSnapshotRef.current = buildSectionSnapshot(currentSchemaData, namespace);
|
|
4076
|
+
setIntakeFormBaselineTrigger((prev) => prev + 1);
|
|
4077
|
+
}
|
|
4078
|
+
onSectionDirtyChange?.(sectionId, false);
|
|
3859
4079
|
}
|
|
3860
|
-
//
|
|
3861
|
-
if (mode === 'IntakeForm') {
|
|
3862
|
-
baselineSnapshotRef.current = buildSectionSnapshot(currentSchemaData, namespace);
|
|
3863
|
-
setIntakeFormBaselineTrigger((prev) => prev + 1);
|
|
3864
|
-
}
|
|
3865
|
-
onSectionDirtyChange?.(sectionId, false);
|
|
4080
|
+
// Always navigate to the next section
|
|
3866
4081
|
onSectionSaveSuccess?.(sectionIndex);
|
|
3867
|
-
}, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode]);
|
|
4082
|
+
}, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode, isDraft]);
|
|
3868
4083
|
// Handle cancel button click
|
|
3869
4084
|
const handleCancel = () => {
|
|
3870
4085
|
// Revert values in store to original schema data
|
|
@@ -4185,7 +4400,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4185
4400
|
gap: '12px',
|
|
4186
4401
|
marginBottom: '20px',
|
|
4187
4402
|
width: '100%',
|
|
4188
|
-
}, children: [jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => onPreviousSection?.(sectionIndex),
|
|
4403
|
+
}, children: [typeof sectionIndex === 'number' && sectionIndex > 0 && (jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => onPreviousSection?.(sectionIndex), className: "intake-form-prev-btn", style: {
|
|
4189
4404
|
fontFamily: 'Roboto, sans-serif',
|
|
4190
4405
|
fontSize: '14px',
|
|
4191
4406
|
fontWeight: 400,
|
|
@@ -4193,25 +4408,25 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
|
|
|
4193
4408
|
borderRadius: '10px',
|
|
4194
4409
|
border: '1px solid #FD8C3E',
|
|
4195
4410
|
background: '#F3F4F6',
|
|
4196
|
-
color:
|
|
4197
|
-
cursor:
|
|
4411
|
+
color: 'rgba(0, 0, 0, 0.5)',
|
|
4412
|
+
cursor: 'pointer',
|
|
4198
4413
|
display: 'inline-flex',
|
|
4199
4414
|
alignItems: 'center',
|
|
4200
4415
|
gap: '8px',
|
|
4201
|
-
}, children: [jsxRuntimeExports.jsx("img", { src: img$5, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px', opacity:
|
|
4416
|
+
}, children: [jsxRuntimeExports.jsx("img", { src: img$5, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px', opacity: 0.5 } }), translate('common.previous') || 'Prev'] })), jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleIntakeFormSave, className: "intake-form-save-btn", style: {
|
|
4202
4417
|
fontFamily: 'Roboto, sans-serif',
|
|
4203
4418
|
fontSize: '14px',
|
|
4204
4419
|
fontWeight: 400,
|
|
4205
4420
|
padding: '8px 24px',
|
|
4206
4421
|
borderRadius: '10px',
|
|
4207
4422
|
border: '1px solid #FD8C3E',
|
|
4208
|
-
background:
|
|
4209
|
-
color:
|
|
4210
|
-
cursor:
|
|
4423
|
+
background: '#F3F4F6',
|
|
4424
|
+
color: 'rgba(0, 0, 0, 0.5)',
|
|
4425
|
+
cursor: 'pointer',
|
|
4211
4426
|
display: 'inline-flex',
|
|
4212
4427
|
alignItems: 'center',
|
|
4213
4428
|
gap: '8px',
|
|
4214
|
-
}, children: [translate('common.
|
|
4429
|
+
}, children: [translate('common.next') || 'Next', jsxRuntimeExports.jsx("img", { src: img$4, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px' } })] })] })] }) }))] })) : (
|
|
4215
4430
|
/* RegistryView / CRView: standard layout */
|
|
4216
4431
|
jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsxs("div", { style: {
|
|
4217
4432
|
marginTop: '35px',
|
|
@@ -4468,7 +4683,9 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
|
|
|
4468
4683
|
if (index + 1 < safeSections.length) {
|
|
4469
4684
|
setExpandedSectionIndex(index + 1);
|
|
4470
4685
|
}
|
|
4471
|
-
|
|
4686
|
+
else {
|
|
4687
|
+
setExpandedSectionIndex(null);
|
|
4688
|
+
}
|
|
4472
4689
|
}, [safeSections.length]);
|
|
4473
4690
|
// IntakeForm mode: called when Previous clicked - collapse current, expand previous
|
|
4474
4691
|
const handlePreviousSection = useCallback((index) => {
|
|
@@ -6796,217 +7013,6 @@ const BooleanWidget = ({ config }) => {
|
|
|
6796
7013
|
: '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: falseLabel })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
|
|
6797
7014
|
};
|
|
6798
7015
|
|
|
6799
|
-
/**
|
|
6800
|
-
* Date input utilities for parsing, formatting, and validation
|
|
6801
|
-
*/
|
|
6802
|
-
/**
|
|
6803
|
-
* Parse date string in various formats to Date object
|
|
6804
|
-
*/
|
|
6805
|
-
const parseDate = (dateString) => {
|
|
6806
|
-
if (!dateString)
|
|
6807
|
-
return null;
|
|
6808
|
-
// Try ISO format first (YYYY-MM-DD)
|
|
6809
|
-
const isoMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
6810
|
-
if (isoMatch) {
|
|
6811
|
-
const date = new Date(parseInt(isoMatch[1]), parseInt(isoMatch[2]) - 1, parseInt(isoMatch[3]));
|
|
6812
|
-
if (!isNaN(date.getTime())) {
|
|
6813
|
-
return date;
|
|
6814
|
-
}
|
|
6815
|
-
}
|
|
6816
|
-
// Try common formats
|
|
6817
|
-
const date = new Date(dateString);
|
|
6818
|
-
if (!isNaN(date.getTime())) {
|
|
6819
|
-
return date;
|
|
6820
|
-
}
|
|
6821
|
-
return null;
|
|
6822
|
-
};
|
|
6823
|
-
/**
|
|
6824
|
-
* Format date to ISO string (YYYY-MM-DD) for storage
|
|
6825
|
-
*/
|
|
6826
|
-
const formatDateToISO = (date) => {
|
|
6827
|
-
if (!date)
|
|
6828
|
-
return '';
|
|
6829
|
-
let dateObj;
|
|
6830
|
-
if (date instanceof Date) {
|
|
6831
|
-
dateObj = date;
|
|
6832
|
-
}
|
|
6833
|
-
else if (typeof date === 'string') {
|
|
6834
|
-
const parsed = parseDate(date);
|
|
6835
|
-
if (!parsed)
|
|
6836
|
-
return '';
|
|
6837
|
-
dateObj = parsed;
|
|
6838
|
-
}
|
|
6839
|
-
else {
|
|
6840
|
-
return '';
|
|
6841
|
-
}
|
|
6842
|
-
if (isNaN(dateObj.getTime()))
|
|
6843
|
-
return '';
|
|
6844
|
-
const year = dateObj.getFullYear();
|
|
6845
|
-
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
6846
|
-
const day = String(dateObj.getDate()).padStart(2, '0');
|
|
6847
|
-
return `${year}-${month}-${day}`;
|
|
6848
|
-
};
|
|
6849
|
-
/**
|
|
6850
|
-
* Format date to custom format string
|
|
6851
|
-
*/
|
|
6852
|
-
const formatDateToString = (date, format) => {
|
|
6853
|
-
if (!date || !format)
|
|
6854
|
-
return '';
|
|
6855
|
-
let dateObj;
|
|
6856
|
-
if (date instanceof Date) {
|
|
6857
|
-
dateObj = date;
|
|
6858
|
-
}
|
|
6859
|
-
else if (typeof date === 'string') {
|
|
6860
|
-
const parsed = parseDate(date);
|
|
6861
|
-
if (!parsed)
|
|
6862
|
-
return '';
|
|
6863
|
-
dateObj = parsed;
|
|
6864
|
-
}
|
|
6865
|
-
else {
|
|
6866
|
-
return '';
|
|
6867
|
-
}
|
|
6868
|
-
if (isNaN(dateObj.getTime()))
|
|
6869
|
-
return '';
|
|
6870
|
-
const day = String(dateObj.getDate()).padStart(2, '0');
|
|
6871
|
-
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
|
6872
|
-
const year = dateObj.getFullYear();
|
|
6873
|
-
const yearShort = year.toString().slice(-2);
|
|
6874
|
-
return format
|
|
6875
|
-
.replace(/DD/g, day)
|
|
6876
|
-
.replace(/MM/g, month)
|
|
6877
|
-
.replace(/YYYY/g, year.toString())
|
|
6878
|
-
.replace(/YY/g, yearShort);
|
|
6879
|
-
};
|
|
6880
|
-
/**
|
|
6881
|
-
* Parse custom format string to Date object
|
|
6882
|
-
*/
|
|
6883
|
-
const parseDateFromFormat = (dateString, format) => {
|
|
6884
|
-
if (!dateString || !format)
|
|
6885
|
-
return null;
|
|
6886
|
-
// Extract day, month, year positions from format
|
|
6887
|
-
const dayIndex = format.indexOf('DD');
|
|
6888
|
-
const monthIndex = format.indexOf('MM');
|
|
6889
|
-
const yearIndex = format.indexOf('YYYY');
|
|
6890
|
-
const yearShortIndex = format.indexOf('YY');
|
|
6891
|
-
if (dayIndex === -1 || monthIndex === -1 || (yearIndex === -1 && yearShortIndex === -1)) {
|
|
6892
|
-
// Fallback to standard Date parsing
|
|
6893
|
-
return parseDate(dateString);
|
|
6894
|
-
}
|
|
6895
|
-
try {
|
|
6896
|
-
let day = '';
|
|
6897
|
-
let month = '';
|
|
6898
|
-
let year = '';
|
|
6899
|
-
// Extract day (2 digits)
|
|
6900
|
-
if (dayIndex !== -1) {
|
|
6901
|
-
day = dateString.substring(dayIndex, dayIndex + 2);
|
|
6902
|
-
}
|
|
6903
|
-
// Extract month (2 digits)
|
|
6904
|
-
if (monthIndex !== -1) {
|
|
6905
|
-
month = dateString.substring(monthIndex, monthIndex + 2);
|
|
6906
|
-
}
|
|
6907
|
-
// Extract year (4 digits or 2 digits)
|
|
6908
|
-
if (yearIndex !== -1) {
|
|
6909
|
-
year = dateString.substring(yearIndex, yearIndex + 4);
|
|
6910
|
-
}
|
|
6911
|
-
else if (yearShortIndex !== -1) {
|
|
6912
|
-
const yearShort = dateString.substring(yearShortIndex, yearShortIndex + 2);
|
|
6913
|
-
const currentYear = new Date().getFullYear();
|
|
6914
|
-
const currentCentury = Math.floor(currentYear / 100) * 100;
|
|
6915
|
-
const parsedYear = parseInt(yearShort);
|
|
6916
|
-
// Assume 20xx for years 00-50, 19xx for years 51-99
|
|
6917
|
-
year = parsedYear <= 50
|
|
6918
|
-
? (currentCentury + parsedYear).toString()
|
|
6919
|
-
: (currentCentury - 100 + parsedYear).toString();
|
|
6920
|
-
}
|
|
6921
|
-
if (day && month && year) {
|
|
6922
|
-
const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
|
6923
|
-
if (!isNaN(date.getTime())) {
|
|
6924
|
-
return date;
|
|
6925
|
-
}
|
|
6926
|
-
}
|
|
6927
|
-
}
|
|
6928
|
-
catch {
|
|
6929
|
-
// Fallback to standard parsing
|
|
6930
|
-
}
|
|
6931
|
-
return parseDate(dateString);
|
|
6932
|
-
};
|
|
6933
|
-
/**
|
|
6934
|
-
* Get min date based on constraint type
|
|
6935
|
-
*/
|
|
6936
|
-
const getMinDate = (constraint, minDate) => {
|
|
6937
|
-
if (minDate) {
|
|
6938
|
-
// If it's 'today', return today's date
|
|
6939
|
-
if (minDate === 'today') {
|
|
6940
|
-
return formatDateToISO(new Date());
|
|
6941
|
-
}
|
|
6942
|
-
return minDate;
|
|
6943
|
-
}
|
|
6944
|
-
if (constraint === 'past-only') {
|
|
6945
|
-
// No minimum for past-only (can select any past date)
|
|
6946
|
-
return undefined;
|
|
6947
|
-
}
|
|
6948
|
-
if (constraint === 'future-only') {
|
|
6949
|
-
// Minimum is today for future-only
|
|
6950
|
-
return formatDateToISO(new Date());
|
|
6951
|
-
}
|
|
6952
|
-
return undefined;
|
|
6953
|
-
};
|
|
6954
|
-
/**
|
|
6955
|
-
* Get max date based on constraint type
|
|
6956
|
-
*/
|
|
6957
|
-
const getMaxDate = (constraint, maxDate) => {
|
|
6958
|
-
if (maxDate) {
|
|
6959
|
-
// If it's 'today', return today's date
|
|
6960
|
-
if (maxDate === 'today') {
|
|
6961
|
-
return formatDateToISO(new Date());
|
|
6962
|
-
}
|
|
6963
|
-
return maxDate;
|
|
6964
|
-
}
|
|
6965
|
-
if (constraint === 'past-only') {
|
|
6966
|
-
// Maximum is today for past-only
|
|
6967
|
-
return formatDateToISO(new Date());
|
|
6968
|
-
}
|
|
6969
|
-
if (constraint === 'future-only') {
|
|
6970
|
-
// No maximum for future-only (can select any future date)
|
|
6971
|
-
return undefined;
|
|
6972
|
-
}
|
|
6973
|
-
return undefined;
|
|
6974
|
-
};
|
|
6975
|
-
/**
|
|
6976
|
-
* Validate date constraints
|
|
6977
|
-
*/
|
|
6978
|
-
const validateDateConstraints = (date, minDate, maxDate, constraint) => {
|
|
6979
|
-
if (!date)
|
|
6980
|
-
return null;
|
|
6981
|
-
const dateObj = date instanceof Date ? date : parseDate(date);
|
|
6982
|
-
if (!dateObj)
|
|
6983
|
-
return null;
|
|
6984
|
-
const dateISO = formatDateToISO(dateObj);
|
|
6985
|
-
const todayISO = formatDateToISO(new Date());
|
|
6986
|
-
// Check constraint type
|
|
6987
|
-
if (constraint === 'past-only') {
|
|
6988
|
-
if (dateISO > todayISO) {
|
|
6989
|
-
return 'Date must be in the past';
|
|
6990
|
-
}
|
|
6991
|
-
}
|
|
6992
|
-
else if (constraint === 'future-only') {
|
|
6993
|
-
if (dateISO < todayISO) {
|
|
6994
|
-
return 'Date must be in the future';
|
|
6995
|
-
}
|
|
6996
|
-
}
|
|
6997
|
-
// Check minDate
|
|
6998
|
-
const effectiveMinDate = getMinDate(constraint, minDate);
|
|
6999
|
-
if (effectiveMinDate && dateISO < effectiveMinDate) {
|
|
7000
|
-
return `Date must be on or after ${effectiveMinDate}`;
|
|
7001
|
-
}
|
|
7002
|
-
// Check maxDate
|
|
7003
|
-
const effectiveMaxDate = getMaxDate(constraint, maxDate);
|
|
7004
|
-
if (effectiveMaxDate && dateISO > effectiveMaxDate) {
|
|
7005
|
-
return `Date must be on or before ${effectiveMaxDate}`;
|
|
7006
|
-
}
|
|
7007
|
-
return null;
|
|
7008
|
-
};
|
|
7009
|
-
|
|
7010
7016
|
const DateInputWidget = ({ config }) => {
|
|
7011
7017
|
const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
|
|
7012
7018
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
@@ -8095,6 +8101,13 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
|
|
|
8095
8101
|
};
|
|
8096
8102
|
return (jsxRuntimeExports.jsx("input", { type: "number", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: validationConfig?.min, max: validationConfig?.max, step: formatConfig?.decimalPlaces ? Math.pow(0.1, formatConfig.decimalPlaces) : undefined, className: `w-full h-[28px] px-2 text-sm border focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 text-right ${isReadonly ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'} border-gray-300`, style: { borderRadius: '10px' } }));
|
|
8097
8103
|
};
|
|
8104
|
+
const TableCellDate = ({ config, value, onValueChange }) => {
|
|
8105
|
+
const isReadonly = config['widget-readonly'] || false;
|
|
8106
|
+
const placeholder = config['widget-data-placeholder'] || '';
|
|
8107
|
+
// input type="date" requires YYYY-MM-DD format
|
|
8108
|
+
const displayValue = value && typeof value === 'string' ? value.split('T')[0] : '';
|
|
8109
|
+
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 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 ${isReadonly ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'} border-gray-300`, style: { borderRadius: '10px' } }));
|
|
8110
|
+
};
|
|
8098
8111
|
const TableWidget = ({ config }) => {
|
|
8099
8112
|
const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
|
|
8100
8113
|
const { translate, translateConfig } = useWidgetTranslation();
|
|
@@ -8478,6 +8491,9 @@ const TableWidget = ({ config }) => {
|
|
|
8478
8491
|
else if (widgetType === 'number') {
|
|
8479
8492
|
return jsxRuntimeExports.jsx(TableCellNumber, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
|
|
8480
8493
|
}
|
|
8494
|
+
else if (widgetType === 'date') {
|
|
8495
|
+
return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
|
|
8496
|
+
}
|
|
8481
8497
|
// For other widget types, use WidgetRenderer but with compact styling
|
|
8482
8498
|
return (jsxRuntimeExports.jsx("div", { className: "table-cell-widget", style: { margin: 0, padding: 0 }, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
|
|
8483
8499
|
[cellWidgetId]: cellValue !== undefined ? cellValue : (column['widget-data-default'] ?? ''),
|
|
@@ -8871,6 +8887,326 @@ const TextAreaWidget = ({ config }) => {
|
|
|
8871
8887
|
}, children: charCounterText }))] }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: errorMessage }))] })] }) }));
|
|
8872
8888
|
};
|
|
8873
8889
|
|
|
8890
|
+
const DEFAULT_STATUS_COLORS = {
|
|
8891
|
+
active: '#16A34A',
|
|
8892
|
+
inactive: '#D97706',
|
|
8893
|
+
archived: '#6B7280',
|
|
8894
|
+
};
|
|
8895
|
+
// ── Hook: load data-source options for a single field ────────────
|
|
8896
|
+
// Options are loaded in both view and edit modes because view mode
|
|
8897
|
+
// needs them to resolve display labels (e.g. "active" → "Active").
|
|
8898
|
+
// For API data sources, loading is deferred to edit mode to avoid
|
|
8899
|
+
// unnecessary network calls when only labels are needed.
|
|
8900
|
+
function useFieldDataSource(fieldKey, fieldConfig, isReadonly) {
|
|
8901
|
+
const [options, setOptions] = useState([]);
|
|
8902
|
+
const { dataSourceRequestHandler, schemaData } = useWidgetContext();
|
|
8903
|
+
const values = useSelector((state) => state.widget.values);
|
|
8904
|
+
const dataSource = fieldConfig?.['data-source'];
|
|
8905
|
+
const dsType = dataSource?.type;
|
|
8906
|
+
const dsKey = dataSource
|
|
8907
|
+
? `${fieldKey}-${dsType}-${JSON.stringify(dataSource)}`
|
|
8908
|
+
: '';
|
|
8909
|
+
useEffect(() => {
|
|
8910
|
+
if (!dataSource) {
|
|
8911
|
+
setOptions([]);
|
|
8912
|
+
return;
|
|
8913
|
+
}
|
|
8914
|
+
// API sources: only fetch when editable to avoid unnecessary calls
|
|
8915
|
+
if (dataSource.type === 'api' && isReadonly) {
|
|
8916
|
+
return;
|
|
8917
|
+
}
|
|
8918
|
+
let cancelled = false;
|
|
8919
|
+
const load = async () => {
|
|
8920
|
+
try {
|
|
8921
|
+
let raw = [];
|
|
8922
|
+
if (dataSource.type === 'static') {
|
|
8923
|
+
raw = getStaticDataSource(dataSource);
|
|
8924
|
+
}
|
|
8925
|
+
else if (dataSource.type === 'api') {
|
|
8926
|
+
if (!dataSourceRequestHandler)
|
|
8927
|
+
return;
|
|
8928
|
+
raw = await getApiDataSource(dataSource, values, dataSourceRequestHandler);
|
|
8929
|
+
}
|
|
8930
|
+
else if (dataSource.type === 'schema') {
|
|
8931
|
+
raw = getSchemaDataSource(dataSource, schemaData || {});
|
|
8932
|
+
}
|
|
8933
|
+
const transformed = transformDataSourceOptions(raw, dataSource.valueKey, dataSource.labelKey);
|
|
8934
|
+
if (!cancelled)
|
|
8935
|
+
setOptions(transformed);
|
|
8936
|
+
}
|
|
8937
|
+
catch (err) {
|
|
8938
|
+
console.error(`[HeaderSectionWidget] Error loading data-source for field "${fieldKey}":`, err);
|
|
8939
|
+
if (!cancelled)
|
|
8940
|
+
setOptions([]);
|
|
8941
|
+
}
|
|
8942
|
+
};
|
|
8943
|
+
load();
|
|
8944
|
+
return () => { cancelled = true; };
|
|
8945
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
8946
|
+
}, [dsKey, isReadonly, dataSourceRequestHandler]);
|
|
8947
|
+
return options;
|
|
8948
|
+
}
|
|
8949
|
+
// ── Main component ───────────────────────────────────────────────
|
|
8950
|
+
const HeaderSectionWidget = ({ config }) => {
|
|
8951
|
+
const { config: widgetConfig, getFieldValue, } = useBaseWidget({ config });
|
|
8952
|
+
const dispatch = useDispatch();
|
|
8953
|
+
const { translateConfig } = useWidgetTranslation();
|
|
8954
|
+
const { schemaData } = useWidgetContext();
|
|
8955
|
+
const values = useSelector((state) => state.widget.values);
|
|
8956
|
+
const isReadonly = widgetConfig['widget-readonly'] !== false;
|
|
8957
|
+
const dataPath = widgetConfig['widget-data-path'];
|
|
8958
|
+
// ── Per-field config map ──────────────────────────────────────
|
|
8959
|
+
const fieldConfigMap = useMemo(() => {
|
|
8960
|
+
return widgetConfig['widget-field-config'] || {};
|
|
8961
|
+
}, [widgetConfig['widget-field-config']]);
|
|
8962
|
+
// ── Load data source options for the status field ─────────────
|
|
8963
|
+
const statusOptions = useFieldDataSource('status', fieldConfigMap['status'], isReadonly);
|
|
8964
|
+
// ── Resolve field values ──────────────────────────────────────
|
|
8965
|
+
const paths = useMemo(() => {
|
|
8966
|
+
if (!dataPath || typeof dataPath !== 'object')
|
|
8967
|
+
return {};
|
|
8968
|
+
return dataPath;
|
|
8969
|
+
}, [dataPath]);
|
|
8970
|
+
const findValue = useCallback((fieldKey) => {
|
|
8971
|
+
const path = paths[fieldKey];
|
|
8972
|
+
if (!path)
|
|
8973
|
+
return undefined;
|
|
8974
|
+
const searchIn = (source) => {
|
|
8975
|
+
if (!source)
|
|
8976
|
+
return undefined;
|
|
8977
|
+
let v = getValueByPath(source, path);
|
|
8978
|
+
if (v !== undefined)
|
|
8979
|
+
return v;
|
|
8980
|
+
for (const obj of Object.values(source)) {
|
|
8981
|
+
if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
|
|
8982
|
+
v = getValueByPath(obj, path);
|
|
8983
|
+
if (v !== undefined)
|
|
8984
|
+
return v;
|
|
8985
|
+
}
|
|
8986
|
+
}
|
|
8987
|
+
return undefined;
|
|
8988
|
+
};
|
|
8989
|
+
let result = searchIn(values);
|
|
8990
|
+
if (result === undefined)
|
|
8991
|
+
result = searchIn(schemaData);
|
|
8992
|
+
return result;
|
|
8993
|
+
}, [paths, values, schemaData]);
|
|
8994
|
+
const imageUrl = findValue('image') || null;
|
|
8995
|
+
const displayName = findValue('name') || '';
|
|
8996
|
+
const functionalId = findValue('functionalId') || '';
|
|
8997
|
+
const statusValue = findValue('status') || '';
|
|
8998
|
+
const statusReason = findValue('statusReason') || '';
|
|
8999
|
+
const createdBy = findValue('createdBy') || '';
|
|
9000
|
+
const createdAt = findValue('createdAt') || '';
|
|
9001
|
+
const lastApprovedBy = findValue('lastApprovedBy') || '';
|
|
9002
|
+
const lastApprovedAt = findValue('lastApprovedAt') || '';
|
|
9003
|
+
// ── Format options ────────────────────────────────────────────
|
|
9004
|
+
const format = (widgetConfig['widget-data-format'] || {});
|
|
9005
|
+
const imageSize = format.imageSize || 90;
|
|
9006
|
+
const nameColor = format.nameColor || '#ED7C22';
|
|
9007
|
+
const statusColors = {
|
|
9008
|
+
...DEFAULT_STATUS_COLORS,
|
|
9009
|
+
...(format.statusColors || {}),
|
|
9010
|
+
};
|
|
9011
|
+
// ── Value change helpers ──────────────────────────────────────
|
|
9012
|
+
const updateFieldValue = useCallback((fieldKey, newValue) => {
|
|
9013
|
+
const path = paths[fieldKey];
|
|
9014
|
+
if (!path)
|
|
9015
|
+
return;
|
|
9016
|
+
const updated = setValueByPath({ ...values }, path, newValue);
|
|
9017
|
+
dispatch(setValues(updated));
|
|
9018
|
+
}, [paths, values, dispatch]);
|
|
9019
|
+
// ── Status helpers ────────────────────────────────────────────
|
|
9020
|
+
const statusLabel = useMemo(() => {
|
|
9021
|
+
if (!statusValue)
|
|
9022
|
+
return '';
|
|
9023
|
+
const opt = statusOptions.find((o) => String(o.value).toLowerCase() === String(statusValue).toLowerCase());
|
|
9024
|
+
return opt ? opt.label : String(statusValue);
|
|
9025
|
+
}, [statusValue, statusOptions]);
|
|
9026
|
+
const statusColor = statusColors[String(statusValue).toLowerCase()] || '#6B7280';
|
|
9027
|
+
// ── Scoped class for CSS isolation ────────────────────────────
|
|
9028
|
+
const cls = `header-section-widget-${widgetConfig['widget-id']}`;
|
|
9029
|
+
// ── Indicator dot component ───────────────────────────────────
|
|
9030
|
+
const Dot = ({ color }) => (jsxRuntimeExports.jsx("span", { style: {
|
|
9031
|
+
display: 'inline-block',
|
|
9032
|
+
width: 8,
|
|
9033
|
+
height: 8,
|
|
9034
|
+
borderRadius: '50%',
|
|
9035
|
+
backgroundColor: color,
|
|
9036
|
+
flexShrink: 0,
|
|
9037
|
+
marginTop: 6,
|
|
9038
|
+
} }));
|
|
9039
|
+
// ── RENDER ────────────────────────────────────────────────────
|
|
9040
|
+
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
|
|
9041
|
+
.${cls} {
|
|
9042
|
+
display: flex;
|
|
9043
|
+
flex-direction: row;
|
|
9044
|
+
gap: 1.5rem;
|
|
9045
|
+
width: 100%;
|
|
9046
|
+
font-family: Roboto, sans-serif;
|
|
9047
|
+
padding: 35px 0 16px 0;
|
|
9048
|
+
}
|
|
9049
|
+
|
|
9050
|
+
.${cls} .hdr-left {
|
|
9051
|
+
display: flex;
|
|
9052
|
+
flex-direction: row;
|
|
9053
|
+
align-items: flex-start;
|
|
9054
|
+
gap: 1rem;
|
|
9055
|
+
flex: 1 1 55%;
|
|
9056
|
+
min-width: 0;
|
|
9057
|
+
}
|
|
9058
|
+
|
|
9059
|
+
.${cls} .hdr-right {
|
|
9060
|
+
display: flex;
|
|
9061
|
+
flex-direction: column;
|
|
9062
|
+
gap: 0.5rem;
|
|
9063
|
+
flex: 0 0 auto;
|
|
9064
|
+
min-width: 220px;
|
|
9065
|
+
}
|
|
9066
|
+
|
|
9067
|
+
.${cls} .hdr-avatar {
|
|
9068
|
+
width: ${imageSize}px;
|
|
9069
|
+
height: ${imageSize}px;
|
|
9070
|
+
border-radius: 8px;
|
|
9071
|
+
object-fit: cover;
|
|
9072
|
+
background-color: #e5e7eb;
|
|
9073
|
+
border: 2px solid #d1d5db;
|
|
9074
|
+
flex-shrink: 0;
|
|
9075
|
+
}
|
|
9076
|
+
|
|
9077
|
+
.${cls} .hdr-avatar-placeholder {
|
|
9078
|
+
width: ${imageSize}px;
|
|
9079
|
+
height: ${imageSize}px;
|
|
9080
|
+
border-radius: 8px;
|
|
9081
|
+
background-color: #e5e7eb;
|
|
9082
|
+
border: 2px solid #d1d5db;
|
|
9083
|
+
display: flex;
|
|
9084
|
+
align-items: center;
|
|
9085
|
+
justify-content: center;
|
|
9086
|
+
flex-shrink: 0;
|
|
9087
|
+
overflow: hidden;
|
|
9088
|
+
}
|
|
9089
|
+
|
|
9090
|
+
.${cls} .hdr-avatar-placeholder img {
|
|
9091
|
+
width: 100%;
|
|
9092
|
+
height: 100%;
|
|
9093
|
+
object-fit: cover;
|
|
9094
|
+
border-radius: 8px;
|
|
9095
|
+
}
|
|
9096
|
+
|
|
9097
|
+
.${cls} .hdr-info {
|
|
9098
|
+
display: flex;
|
|
9099
|
+
flex-direction: column;
|
|
9100
|
+
gap: 0.35rem;
|
|
9101
|
+
min-width: 0;
|
|
9102
|
+
flex: 1;
|
|
9103
|
+
}
|
|
9104
|
+
|
|
9105
|
+
.${cls} .hdr-name {
|
|
9106
|
+
font-size: 1.25rem;
|
|
9107
|
+
font-weight: 600;
|
|
9108
|
+
color: ${nameColor};
|
|
9109
|
+
line-height: 1.4;
|
|
9110
|
+
word-wrap: break-word;
|
|
9111
|
+
}
|
|
9112
|
+
|
|
9113
|
+
.${cls} .hdr-field-row {
|
|
9114
|
+
display: flex;
|
|
9115
|
+
align-items: flex-start;
|
|
9116
|
+
gap: 0.5rem;
|
|
9117
|
+
font-size: 0.875rem;
|
|
9118
|
+
line-height: 1.6;
|
|
9119
|
+
}
|
|
9120
|
+
|
|
9121
|
+
.${cls} .hdr-field-label {
|
|
9122
|
+
color: #6b7280;
|
|
9123
|
+
font-weight: 500;
|
|
9124
|
+
white-space: nowrap;
|
|
9125
|
+
}
|
|
9126
|
+
|
|
9127
|
+
.${cls} .hdr-field-value {
|
|
9128
|
+
color: #111827;
|
|
9129
|
+
font-weight: 600;
|
|
9130
|
+
}
|
|
9131
|
+
|
|
9132
|
+
.${cls} .hdr-status-badge {
|
|
9133
|
+
display: inline-block;
|
|
9134
|
+
padding: 2px 12px;
|
|
9135
|
+
border-radius: 4px;
|
|
9136
|
+
font-size: 0.75rem;
|
|
9137
|
+
font-weight: 600;
|
|
9138
|
+
color: #fff;
|
|
9139
|
+
}
|
|
9140
|
+
|
|
9141
|
+
.${cls} .hdr-meta-row {
|
|
9142
|
+
display: flex;
|
|
9143
|
+
align-items: baseline;
|
|
9144
|
+
gap: 0.35rem;
|
|
9145
|
+
font-size: 0.875rem;
|
|
9146
|
+
line-height: 1.6;
|
|
9147
|
+
}
|
|
9148
|
+
|
|
9149
|
+
.${cls} .hdr-meta-label {
|
|
9150
|
+
color: #6b7280;
|
|
9151
|
+
font-weight: 400;
|
|
9152
|
+
}
|
|
9153
|
+
|
|
9154
|
+
.${cls} .hdr-meta-value {
|
|
9155
|
+
color: #111827;
|
|
9156
|
+
font-weight: 600;
|
|
9157
|
+
}
|
|
9158
|
+
|
|
9159
|
+
.${cls} .hdr-select {
|
|
9160
|
+
height: 32px;
|
|
9161
|
+
padding: 0 8px;
|
|
9162
|
+
border: 1px solid #d1d5db;
|
|
9163
|
+
border-radius: 6px;
|
|
9164
|
+
font-size: 0.875rem;
|
|
9165
|
+
font-family: Roboto, sans-serif;
|
|
9166
|
+
background: #fff;
|
|
9167
|
+
min-width: 140px;
|
|
9168
|
+
color: #374151;
|
|
9169
|
+
}
|
|
9170
|
+
.${cls} .hdr-select:focus {
|
|
9171
|
+
outline: none;
|
|
9172
|
+
border-color: #ED7C22;
|
|
9173
|
+
box-shadow: 0 0 0 2px rgba(237, 124, 34, 0.15);
|
|
9174
|
+
}
|
|
9175
|
+
|
|
9176
|
+
.${cls} .hdr-input {
|
|
9177
|
+
height: 32px;
|
|
9178
|
+
padding: 0 8px;
|
|
9179
|
+
border: 1px solid #d1d5db;
|
|
9180
|
+
border-radius: 6px;
|
|
9181
|
+
font-size: 0.875rem;
|
|
9182
|
+
font-family: Roboto, sans-serif;
|
|
9183
|
+
background: #fff;
|
|
9184
|
+
min-width: 140px;
|
|
9185
|
+
color: #374151;
|
|
9186
|
+
}
|
|
9187
|
+
.${cls} .hdr-input:focus {
|
|
9188
|
+
outline: none;
|
|
9189
|
+
border-color: #ED7C22;
|
|
9190
|
+
box-shadow: 0 0 0 2px rgba(237, 124, 34, 0.15);
|
|
9191
|
+
}
|
|
9192
|
+
|
|
9193
|
+
@media (max-width: 768px) {
|
|
9194
|
+
.${cls} {
|
|
9195
|
+
flex-direction: column;
|
|
9196
|
+
}
|
|
9197
|
+
.${cls} .hdr-right {
|
|
9198
|
+
min-width: 0;
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
` }), jsxRuntimeExports.jsxs("div", { className: cls, children: [jsxRuntimeExports.jsxs("div", { className: "hdr-left", children: [jsxRuntimeExports.jsxs("div", { children: [imageUrl ? (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: displayName || 'Profile', className: "hdr-avatar", onError: (e) => {
|
|
9202
|
+
e.target.style.display = 'none';
|
|
9203
|
+
const placeholder = e.target
|
|
9204
|
+
.parentElement?.querySelector('.hdr-avatar-placeholder');
|
|
9205
|
+
if (placeholder)
|
|
9206
|
+
placeholder.style.display = 'flex';
|
|
9207
|
+
} })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) })] }), 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.jsx(Dot, { color: "#9CA3AF" }), jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [translateConfig('Functional Record ID') || 'Functional Record ID', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx(Dot, { color: isReadonly ? statusColor : '#F59E0B' }), jsxRuntimeExports.jsx("span", { className: "hdr-field-label", children: translateConfig('Record Status') || 'Record 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: translateConfig('Select') || 'Select' }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx(Dot, { color: isReadonly ? '#9CA3AF' : '#F59E0B' }), jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [translateConfig('Status Reason') || 'Status Reason', " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsx("input", { type: "text", className: "hdr-input", value: statusReason, placeholder: translateConfig('Enter Reason') || 'Enter Reason', onChange: (e) => updateFieldValue('statusReason', e.target.value) }))] })] })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-right", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [translateConfig('Created by') || 'Created by', " :"] }), 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: [translateConfig('Created at') || 'Created at', " :"] }), 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: [translateConfig('Last Approved by') || 'Last Approved by', " :"] }), 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: [translateConfig('Last Approved at') || 'Last Approved at', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] })] })] }));
|
|
9208
|
+
};
|
|
9209
|
+
|
|
8874
9210
|
/**
|
|
8875
9211
|
* Register all default/generic widgets
|
|
8876
9212
|
* This is called automatically when the package is imported
|
|
@@ -8910,6 +9246,8 @@ const registerDefaultWidgets = () => {
|
|
|
8910
9246
|
widgetRegistry.register({ widget: 'display', component: DisplayWidget });
|
|
8911
9247
|
// Profile widget for displaying user identity (image, name, ID)
|
|
8912
9248
|
widgetRegistry.register({ widget: 'profile', component: ProfileWidget });
|
|
9249
|
+
// Header section widget for full-width registry header with profile, status, and metadata
|
|
9250
|
+
widgetRegistry.register({ widget: 'header-section', component: HeaderSectionWidget });
|
|
8913
9251
|
};
|
|
8914
9252
|
// Auto-register on import
|
|
8915
9253
|
registerDefaultWidgets();
|
|
@@ -9266,5 +9604,5 @@ const translateUISchema = (schema, translate) => {
|
|
|
9266
9604
|
};
|
|
9267
9605
|
};
|
|
9268
9606
|
|
|
9269
|
-
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DisplayWidget, FileInputWidget, IterableAccordionWidget, JSONEditorPanel, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, createWidgetStore, createZodSchema, evaluateCondition, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, initI18n, isAllowedKey, parseDataPath, parseNumber, registerDefaultWidgets, removeMask, resetAll, resetWidget, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
9607
|
+
export { ArrayWidget, BooleanWidget, CheckboxWidget, CurrencyInputWidget, DateInputWidget, DateTimeInputWidget, DisplayWidget, FileInputWidget, HeaderSectionWidget, IterableAccordionWidget, JSONEditorPanel, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, SimpleTableWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, createWidgetStore, createZodSchema, evaluateCondition, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, geoHierarchyBuilder, getApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, initI18n, isAllowedKey, parseDataPath, parseNumber, registerDefaultWidgets, removeMask, resetAll, resetWidget, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, useBaseWidget, useGeoWidgetCascade, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTranslation, validateNumericValue, validateWidget, widgetRegistry };
|
|
9270
9608
|
//# sourceMappingURL=index.esm.js.map
|