@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/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 || !format?.dateFormat) {
678
- return value?.toString() || '';
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 format.dateFormat
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 = pathPrefix
3657
- ? getValueByPath(sourceData, resolvePath(sectionRegisterId))
3658
- : sourceData[sectionRegisterId];
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 (!store || !onSectionSave || sectionIndex === undefined)
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
- const oldSchemaData = schemaData || contextSchemaData;
3834
- const newSchemaData = trackSectionChages(sectionWidgets, currentSchemaData, namespace);
3835
- const sectionFiles = [];
3836
- if (hasSupportingDocuments) {
3837
- const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
3838
- originalSupportingDocuments.forEach((doc) => {
3839
- const originalDataPath = doc['document-data-path'];
3840
- const storeDataPath = namespace && originalDataPath
3841
- ? `${namespace}.${originalDataPath}`
3842
- : originalDataPath;
3843
- sectionFiles.push(getValueByPath(currentSchemaData, storeDataPath));
3844
- });
3845
- }
3846
- if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
3847
- try {
3848
- await onSectionSave({
3849
- section_id: dbSectionId,
3850
- section_register_id: sectionRegisterId,
3851
- records: [...newSchemaData],
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
- catch (error) {
3856
- console.error('Section Changes Save failed', error);
3857
- return;
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
- // IntakeForm only: update baseline so section is no longer dirty; trigger re-render so badge updates to "Saved"
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), disabled: sectionIndex === 0, className: "intake-form-prev-btn", style: {
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: sectionIndex === 0 ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.5)',
4197
- cursor: sectionIndex === 0 ? 'not-allowed' : 'pointer',
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: sectionIndex === 0 ? 0.5 : 0.5 } }), translate('common.previous') || 'Prev'] }), jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleIntakeFormSave, disabled: isDraft === false, className: "intake-form-save-btn", style: {
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: isDraft === false ? '#9CA3AF' : '#F3F4F6',
4209
- color: isDraft === false ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.5)',
4210
- cursor: isDraft === false ? 'not-allowed' : 'pointer',
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.save') || 'Save', jsxRuntimeExports.jsx("img", { src: img$4, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px' } })] })] })] }) }))] })) : (
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
- // Last section: stay expanded (no action)
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