From 136430defd9a555426256a2b715794ce797cdadd Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 5 Dec 2024 00:14:05 +0200
Subject: [PATCH 01/42] feat: enable non gregorian calendars in views and lists

---
 package.json                                  |  3 +-
 src/components/AppLoader/init.js              |  4 +--
 .../configs/dateField/getDateFieldConfig.js   |  3 +-
 .../getDateFieldConfigForCustomForm.js        |  3 +-
 .../EnrollmentDataEntry.component.js          |  7 +++--
 .../DateAndTime/D2Date/D2Date.component.js    |  4 +--
 .../capture-core/converters/clientToForm.js   | 11 ++++----
 .../capture-core/converters/clientToList.js   | 10 ++++---
 .../capture-core/converters/clientToView.js   | 12 +++++---
 .../capture-core/converters/formToClient.js   | 19 +++++++++----
 .../metaData/SystemSettings/SystemSettings.js |  1 +
 .../systemSettings/cacheSystemSetttings.js    |  6 +++-
 .../date/convertIsoToLocalCalendar.js         | 28 +++++++++++++++++++
 .../date/convertLocalToIsoCalendar.js         | 26 +++++++++++++++++
 .../date/dateObjectToDateFormatString.js      | 10 +++----
 .../utils/converters/date/index.js            |  3 ++
 .../utils/converters/date/padWithZeros.js     | 12 ++++++++
 .../DateField/Date.component.js               |  9 ++----
 yarn.lock                                     |  2 +-
 19 files changed, 129 insertions(+), 44 deletions(-)
 create mode 100644 src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
 create mode 100644 src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
 create mode 100644 src/core_modules/capture-core/utils/converters/date/padWithZeros.js

diff --git a/package.json b/package.json
index e4caffa666..1f22447823 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
         "packages/rules-engine"
     ],
     "dependencies": {
-        "@dhis2/rules-engine-javascript": "101.20.3",
+        "@dhis2-ui/calendar": "^10.0.3",
         "@dhis2/app-runtime": "^3.9.3",
         "@dhis2/d2-i18n": "^1.1.0",
         "@dhis2/d2-icons": "^1.0.1",
@@ -19,7 +19,6 @@
         "@dhis2/d2-ui-rich-text": "^7.4.0",
         "@dhis2/d2-ui-sharing-dialog": "^7.3.3",
         "@dhis2/ui": "^9.10.1",
-        "@dhis2-ui/calendar": "^10.0.3",
         "@joakim_sm/react-infinite-calendar": "^2.4.2",
         "@material-ui/core": "3.9.4",
         "@material-ui/icons": "3",
diff --git a/src/components/AppLoader/init.js b/src/components/AppLoader/init.js
index f7106fabc4..de3a3153fc 100644
--- a/src/components/AppLoader/init.js
+++ b/src/components/AppLoader/init.js
@@ -131,7 +131,7 @@ async function initializeMetaDataAsync(dbLocale: string, onQueryApi: Function, m
 
 async function initializeSystemSettingsAsync(
     uiLocale: string,
-    systemSettings: { dateFormat: string, serverTimeZoneId: string },
+    systemSettings: { dateFormat: string, serverTimeZoneId: string, calendar: string, },
 ) {
     const systemSettingsCacheData = await cacheSystemSettings(uiLocale, systemSettings);
     await buildSystemSettingsAsync(systemSettingsCacheData);
@@ -158,7 +158,7 @@ export async function initializeAsync(
     const systemSettings = await onQueryApi({
         resource: 'system/info',
         params: {
-            fields: 'dateFormat,serverTimeZoneId',
+            fields: 'dateFormat,serverTimeZoneId,calendar',
         },
     });
 
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
index 7c6f480ae7..7415bd7d5f 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
@@ -2,6 +2,7 @@
 import moment from 'moment';
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { DateFieldForForm } from '../../Components';
+import { convertDateObjectToDateFormatString } from '../../../../../../capture-core/utils/converters/date';
 import type { DateDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -15,7 +16,7 @@ export const getDateFieldConfig = (metaData: DateDataElement, options: Object, q
         maxWidth: options.formHorizontal ? 150 : 350,
         calendarWidth: options.formHorizontal ? 250 : 350,
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
-        calendarMaxMoment: !metaData.allowFutureDate ? moment() : undefined,
+        calendarMaxMoment: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
index b1b93fe119..8cb139fb73 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
@@ -2,6 +2,7 @@
 import moment from 'moment';
 import { createFieldConfig, createProps } from '../base/configBaseCustomForm';
 import { DateFieldForCustomForm } from '../../Components';
+import { convertDateObjectToDateFormatString } from '../../../../../../capture-core/utils/converters/date';
 import type { DateDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -10,7 +11,7 @@ export const getDateFieldConfigForCustomForm = (metaData: DateDataElement, optio
         width: 350,
         maxWidth: 350,
         calendarWidth: 350,
-        calendarMaxMoment: !metaData.allowFutureDate ? moment() : undefined,
+        calendarMaxMoment: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
     }, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
index 1444a98aea..76ed900ccc 100644
--- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
+++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
@@ -42,6 +42,7 @@ import {
     withAOCFieldBuilder,
     withDataEntryFields,
 } from '../../DataEntryDhis2Helpers';
+import { convertDateObjectToDateFormatString } from '../../../../capture-core/utils/converters/date';
 
 const overrideMessagePropNames = {
     errorMessage: 'validationError',
@@ -111,7 +112,7 @@ const getEnrollmentDateSettings = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
-            calendarMaxMoment: !props.enrollmentMetadata.allowFutureEnrollmentDate ? moment() : undefined,
+            calendarMaxMoment: !props.enrollmentMetadata.allowFutureEnrollmentDate ? convertDateObjectToDateFormatString(moment()) : undefined,
         }),
         getPropName: () => 'enrolledAt',
         getValidatorContainers: getEnrollmentDateValidatorContainer,
@@ -159,7 +160,9 @@ const getIncidentDateSettings = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
-            calendarMaxMoment: !props.enrollmentMetadata.allowFutureIncidentDate ? moment() : undefined,
+            calendarMaxMoment: !props.enrollmentMetadata.allowFutureIncidentDate ?
+                convertDateObjectToDateFormatString(moment()) :
+                undefined,
         }),
         getPropName: () => 'occurredAt',
         getPassOnFieldData: () => true,
diff --git a/src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2Date.component.js b/src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2Date.component.js
index eacca0a466..2adb875ce7 100644
--- a/src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2Date.component.js
+++ b/src/core_modules/capture-core/components/FormFields/DateAndTime/D2Date/D2Date.component.js
@@ -7,7 +7,6 @@ import { type DateValue } from '../../../FiltersForTypes/Date/types/date.types';
 type Props = {
     label?: ?string,
     value: ?string,
-    calendar?: string,
     calendarWidth?: ?number,
     inputWidth?: ?number,
     onBlur: (value: DateValue) => void,
@@ -50,7 +49,6 @@ export class D2Date extends React.Component<Props, State> {
 
     render() {
         const {
-            calendar,
             calendarWidth,
             inputWidth,
             classes,
@@ -62,7 +60,7 @@ export class D2Date extends React.Component<Props, State> {
             ...passOnProps
         } = this.props;
 
-        const calendarType = calendar || 'gregory';
+        const calendarType = systemSettingsStore.get().calendar || 'gregory';
         const format = systemSettingsStore.get().dateFormat;
 
         return (
diff --git a/src/core_modules/capture-core/converters/clientToForm.js b/src/core_modules/capture-core/converters/clientToForm.js
index f8e227b6d9..606b7e3f04 100644
--- a/src/core_modules/capture-core/converters/clientToForm.js
+++ b/src/core_modules/capture-core/converters/clientToForm.js
@@ -1,6 +1,6 @@
 // @flow
 import moment from 'moment';
-import { convertMomentToDateFormatString } from '../utils/converters/date';
+import { convertMomentToDateFormatString, convertIsoToLocalCalendar } from '../utils/converters/date';
 import { dataElementTypes } from '../metaData';
 
 import { stringifyNumber } from './common/stringifyNumber';
@@ -23,16 +23,17 @@ type RangeValue = {
 }
 
 function convertDateForEdit(rawValue: string): string {
-    const momentInstance = moment(rawValue);
-    return convertMomentToDateFormatString(momentInstance);
+    const momentDate = moment(rawValue);
+    const dateString = momentDate.format('YYYY-MM-DD');
+    return convertIsoToLocalCalendar(dateString);
 }
 
 function convertDateTimeForEdit(rawValue: string): DateTimeFormValue {
     const dateTime = moment(rawValue);
-    const dateString = convertMomentToDateFormatString(dateTime);
+    const dateString = dateTime.format('YYYY-MM-DD');
     const timeString = dateTime.format('HH:mm');
     return {
-        date: dateString,
+        date: convertIsoToLocalCalendar(dateString),
         time: timeString,
     };
 }
diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js
index e72b837179..6ee3cb78a7 100644
--- a/src/core_modules/capture-core/converters/clientToList.js
+++ b/src/core_modules/capture-core/converters/clientToList.js
@@ -5,21 +5,23 @@ import i18n from '@dhis2/d2-i18n';
 import { Tag } from '@dhis2/ui';
 import { PreviewImage } from 'capture-ui';
 import { dataElementTypes, type DataElement } from '../metaData';
-import { convertMomentToDateFormatString } from '../utils/converters/date';
+import { convertIsoToLocalCalendar } from '../utils/converters/date';
 import { stringifyNumber } from './common/stringifyNumber';
 import { MinimalCoordinates } from '../components/MinimalCoordinates';
 import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';
 
 function convertDateForListDisplay(rawValue: string): string {
     const momentDate = moment(rawValue);
-    return convertMomentToDateFormatString(momentDate);
+    const dateString = momentDate.format('YYYY-MM-DD');
+    return convertIsoToLocalCalendar(dateString);
 }
 
 function convertDateTimeForListDisplay(rawValue: string): string {
     const momentDate = moment(rawValue);
-    const dateString = convertMomentToDateFormatString(momentDate);
+    const dateString = momentDate.format('YYYY-MM-DD');
     const timeString = momentDate.format('HH:mm');
-    return `${dateString} ${timeString}`;
+    const localDate = convertIsoToLocalCalendar(dateString);
+    return `${localDate} ${timeString}`;
 }
 
 function convertTimeForListDisplay(rawValue: string): string {
diff --git a/src/core_modules/capture-core/converters/clientToView.js b/src/core_modules/capture-core/converters/clientToView.js
index fb7728f723..6f4e2b7e14 100644
--- a/src/core_modules/capture-core/converters/clientToView.js
+++ b/src/core_modules/capture-core/converters/clientToView.js
@@ -4,7 +4,7 @@ import moment from 'moment';
 import i18n from '@dhis2/d2-i18n';
 import { PreviewImage } from 'capture-ui';
 import { dataElementTypes, type DataElement } from '../metaData';
-import { convertMomentToDateFormatString } from '../utils/converters/date';
+import { convertIsoToLocalCalendar } from '../utils/converters/date';
 import { stringifyNumber } from './common/stringifyNumber';
 import { MinimalCoordinates } from '../components/MinimalCoordinates';
 import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';
@@ -12,14 +12,18 @@ import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';
 
 function convertDateForView(rawValue: string): string {
     const momentDate = moment(rawValue);
-    return convertMomentToDateFormatString(momentDate);
+    const dateString = momentDate.format('YYYY-MM-DD');
+    return convertIsoToLocalCalendar(dateString);
 }
 function convertDateTimeForView(rawValue: string): string {
     const momentDate = moment(rawValue);
-    const dateString = convertMomentToDateFormatString(momentDate);
+    const dateString = momentDate.format('YYYY-MM-DD');
     const timeString = momentDate.format('HH:mm');
-    return `${dateString} ${timeString}`;
+
+    const localDate = convertIsoToLocalCalendar(dateString);
+    return `${localDate} ${timeString}`;
 }
+
 function convertTimeForView(rawValue: string): string {
     const momentDate = moment(rawValue, 'HH:mm', true);
     return momentDate.format('HH:mm');
diff --git a/src/core_modules/capture-core/converters/formToClient.js b/src/core_modules/capture-core/converters/formToClient.js
index 61c286d4d2..61b37923fd 100644
--- a/src/core_modules/capture-core/converters/formToClient.js
+++ b/src/core_modules/capture-core/converters/formToClient.js
@@ -1,8 +1,9 @@
 // @flow
+import moment from 'moment';
 import isString from 'd2-utilizr/lib/isString';
 import { parseNumber, parseTime } from 'capture-core-utils/parsers';
 import { dataElementTypes } from '../metaData';
-import { parseDate } from '../utils/converters/date';
+import { parseDate, convertLocalToIsoCalendar } from '../utils/converters/date';
 
 type DateTimeValue = {
     date: string,
@@ -25,9 +26,11 @@ function convertDateTime(formValue: DateTimeValue): ?string {
     const minutes = momentTime.minute();
 
     const parsedDate = editedDate ? parseDate(editedDate) : null;
-    if (!(parsedDate && parsedDate.isValid)) return null;
-    // $FlowFixMe[incompatible-type] automated comment
-    const momentDateTime: moment$Moment = parsedDate.momentDate;
+    if (!(parsedDate && parsedDate.isValid && parsedDate.momentDate)) return null;
+
+    const formattedDate = parsedDate.momentDate.format('YYYY-MM-DD');
+    const isoDate = convertLocalToIsoCalendar(formattedDate);
+    const momentDateTime = moment(isoDate);
     momentDateTime.hour(hours);
     momentDateTime.minute(minutes);
     return momentDateTime.toISOString();
@@ -35,8 +38,12 @@ function convertDateTime(formValue: DateTimeValue): ?string {
 
 function convertDate(dateValue: string) {
     const parsedDate = parseDate(dateValue);
-    // $FlowFixMe[incompatible-use] automated comment
-    return parsedDate.isValid ? parsedDate.momentDate.toISOString() : null;
+    if (!parsedDate.isValid || !parsedDate.momentDate) {
+        return null;
+    }
+    const formattedDate = parsedDate.momentDate.format('YYYY-MM-DD');
+
+    return convertLocalToIsoCalendar(formattedDate);
 }
 
 function convertTime(timeValue: string) {
diff --git a/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js b/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
index 6231d3b637..ec8334b437 100644
--- a/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
+++ b/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
@@ -4,4 +4,5 @@ export class SystemSettings {
     dateFormat: string;
     dir: string;
     trackerAppRelativePath: string;
+    calendar: string;
 }
diff --git a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
index 7ff99e54aa..225c4261ab 100644
--- a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
+++ b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
@@ -10,7 +10,7 @@ function isLangRTL(code) {
 
 export async function cacheSystemSettings(
     uiLocale: string,
-    systemSettings: { dateFormat: string, serverTimeZoneId: string },
+    systemSettings: { dateFormat: string, serverTimeZoneId: string, calendar: string, },
 ) {
     const systemSettingsArray = [
         {
@@ -25,6 +25,10 @@ export async function cacheSystemSettings(
             id: 'serverTimeZoneId',
             value: systemSettings.serverTimeZoneId,
         },
+        {
+            id: 'calendar',
+            value: systemSettings.calendar,
+        },
     ];
 
     const storageController = getMainStorageController();
diff --git a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
new file mode 100644
index 0000000000..3f9f99e34b
--- /dev/null
+++ b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
@@ -0,0 +1,28 @@
+// @flow
+import {
+    convertFromIso8601,
+} from '@dhis2/multi-calendar-dates';
+import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
+import { padWithZeros } from './padWithZeros';
+
+/**
+ * Converts a date from ISO calendar to local calendar
+ * @export
+ * @param {string} isoDate - date in ISO format
+ * @returns {string}
+ */
+
+export function convertIsoToLocalCalendar(isoDate: string): string {
+    if (!isoDate) {
+        return '';
+    }
+    const calendar = systemSettingsStore.get().calendar;
+    const dateFormat = systemSettingsStore.get().dateFormat;
+
+    const { year, eraYear, month, day } = convertFromIso8601(isoDate, calendar);
+    const localYear = calendar === 'ethiopian' ? eraYear : year;
+
+    return dateFormat === 'DD-MM-YYYY'
+        ? `${padWithZeros(day, 2)}-${padWithZeros(month, 2)}-${padWithZeros(localYear, 4)}`
+        : `${padWithZeros(localYear, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}`;
+}
diff --git a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
new file mode 100644
index 0000000000..99b1215919
--- /dev/null
+++ b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
@@ -0,0 +1,26 @@
+// @flow
+import moment from 'moment';
+import {
+    convertToIso8601,
+} from '@dhis2/multi-calendar-dates';
+import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
+import { padWithZeros } from './padWithZeros';
+
+/**
+ * Converts a date from local calendar to ISO calendar
+ * @export
+ * @param {string} localDate - date in local calendar format
+ * @returns {string}
+ */
+export function convertLocalToIsoCalendar(localDate: string): string {
+    if (!localDate) {
+        return '';
+    }
+    const calendar = systemSettingsStore.get().calendar;
+
+    const { year, month, day } = convertToIso8601(localDate, calendar);
+    const dateString = `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}`;
+    const parsedMoment = moment(dateString);
+
+    return parsedMoment.isValid() ? parsedMoment.toISOString() : '';
+}
diff --git a/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js b/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
index de59f62d24..bbe748a579 100644
--- a/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
+++ b/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
@@ -1,6 +1,6 @@
 // @flow
 import moment from 'moment';
-import { systemSettingsStore } from '../../../metaDataMemoryStores';
+import { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
 
 /**
  * Converts a date instance to a string based on the system date format
@@ -8,8 +8,8 @@ import { systemSettingsStore } from '../../../metaDataMemoryStores';
  * @param {Date} dateValue: the date instance
  * @returns {string}
  */
-export function convertDateObjectToDateFormatString(dateValue: Date) {
-    const dateFormat = systemSettingsStore.get().dateFormat;
-    const formattedDateString = moment(dateValue).format(dateFormat);
-    return formattedDateString;
+export function convertDateObjectToDateFormatString(dateValue: Date |  moment$Moment) {
+    const momentDate = moment(dateValue);
+    const dateString = momentDate.format('YYYY-MM-DD');
+    return convertIsoToLocalCalendar(dateString);
 }
diff --git a/src/core_modules/capture-core/utils/converters/date/index.js b/src/core_modules/capture-core/utils/converters/date/index.js
index f7e46c2971..511298b89d 100644
--- a/src/core_modules/capture-core/utils/converters/date/index.js
+++ b/src/core_modules/capture-core/utils/converters/date/index.js
@@ -3,3 +3,6 @@ export { parseDate } from './parser';
 export { convertDateObjectToDateFormatString } from './dateObjectToDateFormatString';
 export { convertMomentToDateFormatString } from './momentToDateFormatString';
 export { convertStringToDateFormat } from './stringToMomentDateFormat';
+export { padWithZeros } from './padWithZeros';
+export { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
+export { convertLocalToIsoCalendar } from './convertLocalToIsoCalendar';
diff --git a/src/core_modules/capture-core/utils/converters/date/padWithZeros.js b/src/core_modules/capture-core/utils/converters/date/padWithZeros.js
new file mode 100644
index 0000000000..5edc85befd
--- /dev/null
+++ b/src/core_modules/capture-core/utils/converters/date/padWithZeros.js
@@ -0,0 +1,12 @@
+// @flow
+
+/**
+ * Pads a string or number with zeros at the start to reach a minimum length
+ * @export
+ * @param {string|number} value - the value to pad
+ * @param {number} length - length required
+ * @returns {string}
+ */
+export function padWithZeros(value: string | number, length: number): string {
+    return String(value).padStart(length, '0');
+}
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
index b48ee77ae5..95bf289714 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
@@ -18,7 +18,6 @@ type Props = {
     onBlur: (value: Object, options: ValidationOptions) => void,
     onFocus?: ?() => void,
     onDateSelectedFromCalendar?: () => void,
-    calendar?: string,
     placeholder?: string,
     label?: string,
     calendarMaxMoment?: any,
@@ -36,9 +35,6 @@ type State = {
     calendarError: ?Validation,
 };
 
-const formatDate = (date: any, dateFormat: string): ?string =>
-    (dateFormat === 'dd-MM-yyyy' ? date?.format('DD-MM-YYYY') : date?.format('YYYY-MM-DD'));
-
 export class DateField extends React.Component<Props, State> {
     handleDateSelected: (value: {calendarDateString: string}) => void;
 
@@ -65,7 +61,6 @@ export class DateField extends React.Component<Props, State> {
             maxWidth,
             calendarWidth,
             inputWidth,
-            calendar,
             calendarMaxMoment,
             value,
             innerMessage,
@@ -73,7 +68,7 @@ export class DateField extends React.Component<Props, State> {
 
         const calculatedInputWidth = inputWidth || width;
         const calculatedCalendarWidth = calendarWidth || width;
-        const calendarType = calendar || 'gregory';
+        const calendarType = systemSettingsStore.get().calendar || 'gregory';
         const format = systemSettingsStore.get().dateFormat;
         const errorProps = innerMessage && innerMessage.messageType === 'error'
             ? { error: !!innerMessage.message?.dateInnerErrorMessage,
@@ -99,7 +94,7 @@ export class DateField extends React.Component<Props, State> {
                     onFocus={this.props.onFocus}
                     disabled={this.props.disabled}
                     {...errorProps}
-                    maxDate={calendarMaxMoment && formatDate(calendarMaxMoment, format)}
+                    maxDate={calendarMaxMoment}
                 />
             </div>
         );
diff --git a/yarn.lock b/yarn.lock
index 4c2c7b3910..9a8bd281de 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2718,7 +2718,7 @@
     recompose "^0.26.0"
     rxjs "^5.5.7"
 
-"@dhis2/multi-calendar-dates@2.0.0":
+"@dhis2/multi-calendar-dates@2.0.0", "@dhis2/multi-calendar-dates@^2.0.0":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-2.0.0.tgz#febf04f873670960804d38c9ebaa1cadf8050db3"
   integrity sha512-pxu81kkkh70tB+CyAub41ulpNJPHyxDGwH2pdcc+NUqrKu4OTQr5ScdCBL2MndShrEKj9J6qj9zKVagvvymH5w==

From b8fd9411c421044c6738e0aa2bcef4895e88e9e8 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 5 Dec 2024 01:36:14 +0200
Subject: [PATCH 02/42] fix: working list filters to use gregorian

---
 .../Date/DateFilter.component.js              | 28 +++++++++++++++----
 .../date/convertIsoToLocalCalendar.js         |  2 +-
 .../date/convertLocalToIsoCalendar.js         |  2 +-
 .../date/dateObjectToDateFormatString.js      |  2 +-
 4 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
index b0fa8f967e..20fc8fe9fd 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
@@ -1,6 +1,7 @@
 // @flow
 import React, { Component } from 'react';
 import classNames from 'classnames';
+import moment from 'moment';
 import { withStyles } from '@material-ui/core/styles';
 import i18n from '@dhis2/d2-i18n';
 import { isValidZeroOrPositiveInteger } from 'capture-core-utils/validators/form';
@@ -16,7 +17,8 @@ import './calendarFilterStyles.css';
 import { mainOptionKeys, mainOptionTranslatedTexts } from './options';
 import { getDateFilterData } from './dateFilterDataGetter';
 import { RangeFilter } from './RangeFilter.component';
-import { parseDate } from '../../../utils/converters/date';
+import { parseDate, convertLocalToIsoCalendar, convertIsoToLocalCalendar } from '../../../utils/converters/date';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 
 const getStyles = (theme: Theme) => ({
     fromToContainer: {
@@ -265,12 +267,28 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
         return !values || DateFilter.isFilterValid(values.main, values.from, values.to, values.start, values.end);
     }
 
-    getUpdatedValue(valuePart: { [key: string]: string }) {
-        // $FlowFixMe[cannot-spread-indexer] automated comment
+    // eslint-disable-next-line complexity
+    getUpdatedValue(valuePart: Object) {
         const valueObject = {
             ...this.props.value,
             ...valuePart,
         };
+        const dateFormat = systemSettingsStore.get().dateFormat;
+
+        if (valuePart.from && valueObject?.from?.value) {
+            valueObject.from = {
+                ...valueObject.from,
+                value: moment(convertLocalToIsoCalendar(valueObject.from.value)).format(dateFormat),
+            };
+        }
+
+        if (valuePart.to && valueObject?.to?.value) {
+            valueObject.to = {
+                ...valueObject.to,
+                value: moment(convertLocalToIsoCalendar(valueObject.to.value)).format(dateFormat),
+            };
+        }
+
         const isRelativeRangeValue = () => valueObject?.start || valuePart?.start || valuePart?.end;
         const isAbsoluteRangevalue = () => valueObject?.from || valuePart?.from || valuePart?.to;
 
@@ -366,7 +384,7 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <FromDateFilter
-                            value={fromValue?.value}
+                            value={convertIsoToLocalCalendar(fromValue?.value)}
                             onBlur={this.handleFieldBlur}
                             onEnterKey={this.handleEnterKeyInFrom}
                             onDateSelectedFromCalendar={this.handleDateSelectedFromCalendarInFrom}
@@ -379,7 +397,7 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <ToDateFilter
-                            value={toValue?.value}
+                            value={convertIsoToLocalCalendar(toValue?.value)}
                             onBlur={this.handleFieldBlur}
                             textFieldRef={this.setToD2DateTextFieldInstance}
                             onFocusUpdateButton={onFocusUpdateButton}
diff --git a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
index 3f9f99e34b..019872c197 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
@@ -12,7 +12,7 @@ import { padWithZeros } from './padWithZeros';
  * @returns {string}
  */
 
-export function convertIsoToLocalCalendar(isoDate: string): string {
+export function convertIsoToLocalCalendar(isoDate: ?string): string {
     if (!isoDate) {
         return '';
     }
diff --git a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
index 99b1215919..b3f323d178 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
@@ -12,7 +12,7 @@ import { padWithZeros } from './padWithZeros';
  * @param {string} localDate - date in local calendar format
  * @returns {string}
  */
-export function convertLocalToIsoCalendar(localDate: string): string {
+export function convertLocalToIsoCalendar(localDate: ?string): string {
     if (!localDate) {
         return '';
     }
diff --git a/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js b/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
index bbe748a579..1f91c521cc 100644
--- a/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
+++ b/src/core_modules/capture-core/utils/converters/date/dateObjectToDateFormatString.js
@@ -8,7 +8,7 @@ import { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
  * @param {Date} dateValue: the date instance
  * @returns {string}
  */
-export function convertDateObjectToDateFormatString(dateValue: Date |  moment$Moment) {
+export function convertDateObjectToDateFormatString(dateValue: Date | moment$Moment) {
     const momentDate = moment(dateValue);
     const dateString = momentDate.format('YYYY-MM-DD');
     return convertIsoToLocalCalendar(dateString);

From e37855100e3b5c9b16792eeb70fec761900ecc8d Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Tue, 10 Dec 2024 14:18:33 +0200
Subject: [PATCH 03/42] fix: working list filter runtime error when dd-mm-yyyy
 format is passed

---
 .../converters/date/convertIsoToLocalCalendar.js      | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
index 019872c197..442ad4a60c 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
@@ -1,4 +1,5 @@
 // @flow
+import moment from 'moment';
 import {
     convertFromIso8601,
 } from '@dhis2/multi-calendar-dates';
@@ -16,10 +17,18 @@ export function convertIsoToLocalCalendar(isoDate: ?string): string {
     if (!isoDate) {
         return '';
     }
+
+    const momentDate = moment(isoDate);
+    if (!momentDate.isValid()) {
+        return '';
+    }
+
+    const formattedIsoDate = momentDate.format('YYYY-MM-DD');
+
     const calendar = systemSettingsStore.get().calendar;
     const dateFormat = systemSettingsStore.get().dateFormat;
 
-    const { year, eraYear, month, day } = convertFromIso8601(isoDate, calendar);
+    const { year, eraYear, month, day } = convertFromIso8601(formattedIsoDate, calendar);
     const localYear = calendar === 'ethiopian' ? eraYear : year;
 
     return dateFormat === 'DD-MM-YYYY'

From 8e44c3cad99d9ddc07f7f7a6dc7efc747b716ebf Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 11 Dec 2024 11:27:52 +0200
Subject: [PATCH 04/42] fix: missing rules engine in dependencies

---
 package.json | 1 +
 yarn.lock    | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 1f22447823..1178b6e2e1 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
         "packages/rules-engine"
     ],
     "dependencies": {
+        "@dhis2/rules-engine-javascript": "101.19.1",
         "@dhis2-ui/calendar": "^10.0.3",
         "@dhis2/app-runtime": "^3.9.3",
         "@dhis2/d2-i18n": "^1.1.0",
diff --git a/yarn.lock b/yarn.lock
index 9a8bd281de..4c2c7b3910 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2718,7 +2718,7 @@
     recompose "^0.26.0"
     rxjs "^5.5.7"
 
-"@dhis2/multi-calendar-dates@2.0.0", "@dhis2/multi-calendar-dates@^2.0.0":
+"@dhis2/multi-calendar-dates@2.0.0":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@dhis2/multi-calendar-dates/-/multi-calendar-dates-2.0.0.tgz#febf04f873670960804d38c9ebaa1cadf8050db3"
   integrity sha512-pxu81kkkh70tB+CyAub41ulpNJPHyxDGwH2pdcc+NUqrKu4OTQr5ScdCBL2MndShrEKj9J6qj9zKVagvvymH5w==

From 1f2518d9be14690500b64a8a5a3b0954fa62d130 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 11 Dec 2024 13:13:00 +0200
Subject: [PATCH 05/42] fix: working list filter not displaying local date

---
 .../buttonTextBuilder/converters/dateConverter.js           | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js b/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
index 4dce8ba7a9..adbc7c367d 100644
--- a/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
+++ b/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
@@ -6,6 +6,8 @@ import { convertMomentToDateFormatString } from '../../../../../../utils/convert
 import type { DateFilterData, AbsoluteDateFilterData } from '../../../../../FiltersForTypes';
 import { areRelativeRangeValuesSupported }
     from '../../../../../../utils/validation/validators/areRelativeRangeValuesSupported';
+import { convertClientToView } from '../../../../../../../capture-core/converters';
+import { dataElementTypes } from '../../../../../../metaData';
 
 const periods = {
     TODAY: 'TODAY',
@@ -37,8 +39,8 @@ const convertToViewValue = (filterValue: string) => pipe(
 
 function translateAbsoluteDate(filter: AbsoluteDateFilterData) {
     let appliedText = '';
-    const fromValue = filter.ge;
-    const toValue = filter.le;
+    const fromValue = convertClientToView(filter.ge, dataElementTypes.DATE);
+    const toValue = convertClientToView(filter.le, dataElementTypes.DATE);
 
     if (fromValue && toValue) {
         const momentFrom = moment(fromValue);

From 9a3d60960dc8c8228b6fd8cf1446bf0953e3fc60 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 11 Dec 2024 13:27:26 +0200
Subject: [PATCH 06/42] fix: reduce the complexity of getUpdatedValue

---
 .../FiltersForTypes/Date/DateFilter.component.js           | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
index 20fc8fe9fd..a38d9cd704 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
@@ -267,22 +267,23 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
         return !values || DateFilter.isFilterValid(values.main, values.from, values.to, values.start, values.end);
     }
 
-    // eslint-disable-next-line complexity
     getUpdatedValue(valuePart: Object) {
         const valueObject = {
             ...this.props.value,
             ...valuePart,
         };
         const dateFormat = systemSettingsStore.get().dateFormat;
+        const hasFromValue = () => valuePart.from && valueObject?.from?.value;
+        const hasToValue = () => valuePart.to && valueObject?.to?.value;
 
-        if (valuePart.from && valueObject?.from?.value) {
+        if (hasFromValue()) {
             valueObject.from = {
                 ...valueObject.from,
                 value: moment(convertLocalToIsoCalendar(valueObject.from.value)).format(dateFormat),
             };
         }
 
-        if (valuePart.to && valueObject?.to?.value) {
+        if (hasToValue()) {
             valueObject.to = {
                 ...valueObject.to,
                 value: moment(convertLocalToIsoCalendar(valueObject.to.value)).format(dateFormat),

From 3a3d0b2c320e2c6d48071b1ec3909450a6e9bc48 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 11 Dec 2024 23:37:38 +0200
Subject: [PATCH 07/42] chore: refactor working lists filter component

---
 .../Date/DateFilter.component.js              | 53 +++++++++++--------
 .../converters/dateConverter.js               | 24 +++------
 2 files changed, 39 insertions(+), 38 deletions(-)

diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
index a38d9cd704..80b002c049 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
@@ -273,22 +273,18 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
             ...valuePart,
         };
         const dateFormat = systemSettingsStore.get().dateFormat;
-        const hasFromValue = () => valuePart.from && valueObject?.from?.value;
-        const hasToValue = () => valuePart.to && valueObject?.to?.value;
 
-        if (hasFromValue()) {
-            valueObject.from = {
-                ...valueObject.from,
-                value: moment(convertLocalToIsoCalendar(valueObject.from.value)).format(dateFormat),
-            };
-        }
-
-        if (hasToValue()) {
-            valueObject.to = {
-                ...valueObject.to,
-                value: moment(convertLocalToIsoCalendar(valueObject.to.value)).format(dateFormat),
-            };
-        }
+        ['from', 'to'].forEach((key) => {
+            if (valuePart[key] && valueObject[key]?.value) {
+                const isValidDate = valuePart[key]?.isValid;
+                valueObject[key] = {
+                    ...valueObject[key],
+                    value: isValidDate ?
+                        moment(convertLocalToIsoCalendar(valueObject[key].value)).format(dateFormat) :
+                        valueObject[key].value,
+                };
+            }
+        });
 
         const isRelativeRangeValue = () => valueObject?.start || valuePart?.start || valuePart?.end;
         const isAbsoluteRangevalue = () => valueObject?.from || valuePart?.from || valuePart?.to;
@@ -362,10 +358,23 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
 
     render() {
         const { value, classes, onFocusUpdateButton } = this.props;
-        const fromValue = value?.from;
-        const toValue = value?.to;
         const { startValueError, endValueError, dateLogicError, bufferLogicError } =
-            this.getErrors();
+        this.getErrors();
+
+        const dateFormat = systemSettingsStore.get().dateFormat;
+
+        const formatDate = (dateValue) => {
+            if (!dateValue) return '';
+            const momentValue = moment(dateValue, dateFormat);
+            if (!momentValue.isValid()) return dateValue;
+            const isoDate = momentValue.format('YYYY-MM-DD');
+            return convertIsoToLocalCalendar(isoDate);
+        };
+
+        const from = value?.from;
+        const to = value?.to;
+        const fromDate = formatDate(from?.value);
+        const toDate = formatDate(to?.value);
 
         return (
             <div id="dateFilter">
@@ -385,11 +394,11 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <FromDateFilter
-                            value={convertIsoToLocalCalendar(fromValue?.value)}
+                            value={fromDate}
                             onBlur={this.handleFieldBlur}
                             onEnterKey={this.handleEnterKeyInFrom}
                             onDateSelectedFromCalendar={this.handleDateSelectedFromCalendarInFrom}
-                            error={fromValue?.error}
+                            error={from?.error}
                             errorClass={classes.error}
                         />
                     </div>
@@ -398,11 +407,11 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <ToDateFilter
-                            value={convertIsoToLocalCalendar(toValue?.value)}
+                            value={toDate}
                             onBlur={this.handleFieldBlur}
                             textFieldRef={this.setToD2DateTextFieldInstance}
                             onFocusUpdateButton={onFocusUpdateButton}
-                            error={toValue?.error}
+                            error={to?.error}
                             errorClass={classes.error}
                         />
                     </div>
diff --git a/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js b/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
index adbc7c367d..35c60d8d2e 100644
--- a/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
+++ b/src/core_modules/capture-core/components/ListView/Filters/FilterButton/buttonTextBuilder/converters/dateConverter.js
@@ -1,13 +1,10 @@
 // @flow
 import i18n from '@dhis2/d2-i18n';
-import { pipe } from 'capture-core-utils';
 import moment from 'moment';
-import { convertMomentToDateFormatString } from '../../../../../../utils/converters/date';
+import { convertIsoToLocalCalendar } from '../../../../../../utils/converters/date';
 import type { DateFilterData, AbsoluteDateFilterData } from '../../../../../FiltersForTypes';
 import { areRelativeRangeValuesSupported }
     from '../../../../../../utils/validation/validators/areRelativeRangeValuesSupported';
-import { convertClientToView } from '../../../../../../../capture-core/converters';
-import { dataElementTypes } from '../../../../../../metaData';
 
 const periods = {
     TODAY: 'TODAY',
@@ -32,32 +29,27 @@ const translatedPeriods = {
     [periods.RELATIVE_RANGE]: i18n.t('Relative range'),
 };
 
-const convertToViewValue = (filterValue: string) => pipe(
-    value => moment(value),
-    momentDate => convertMomentToDateFormatString(momentDate),
-)(filterValue);
-
 function translateAbsoluteDate(filter: AbsoluteDateFilterData) {
     let appliedText = '';
-    const fromValue = convertClientToView(filter.ge, dataElementTypes.DATE);
-    const toValue = convertClientToView(filter.le, dataElementTypes.DATE);
+    const fromValue = filter.ge;
+    const toValue = filter.le;
 
     if (fromValue && toValue) {
         const momentFrom = moment(fromValue);
         const momentTo = moment(toValue);
         if (momentFrom.isSame(momentTo)) {
-            appliedText = convertMomentToDateFormatString(momentFrom);
+            appliedText = convertIsoToLocalCalendar(fromValue);
         } else {
-            const appliedTextFrom = convertMomentToDateFormatString(momentFrom);
-            const appliedTextTo = convertMomentToDateFormatString(momentTo);
+            const appliedTextFrom = convertIsoToLocalCalendar(fromValue);
+            const appliedTextTo = convertIsoToLocalCalendar(toValue);
             appliedText = i18n.t('{{fromDate}} to {{toDate}}', { fromDate: appliedTextFrom, toDate: appliedTextTo });
         }
     } else if (fromValue) {
-        const appliedTextFrom = convertToViewValue(fromValue);
+        const appliedTextFrom = convertIsoToLocalCalendar(fromValue);
         appliedText = i18n.t('after or equal to {{date}}', { date: appliedTextFrom });
     } else {
         // $FlowFixMe[incompatible-call] automated comment
-        const appliedTextTo = convertToViewValue(toValue);
+        const appliedTextTo = convertIsoToLocalCalendar(toValue);
         appliedText = i18n.t('before or equal to {{date}}', { date: appliedTextTo });
     }
     return appliedText;

From 53ef00edf71809d50e44332171ffdebeca1af7a9 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 11 Dec 2024 23:45:12 +0200
Subject: [PATCH 08/42] fix: convert age values to local date

---
 src/core_modules/capture-core/converters/clientToForm.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core_modules/capture-core/converters/clientToForm.js b/src/core_modules/capture-core/converters/clientToForm.js
index 606b7e3f04..c0b0d10948 100644
--- a/src/core_modules/capture-core/converters/clientToForm.js
+++ b/src/core_modules/capture-core/converters/clientToForm.js
@@ -1,6 +1,6 @@
 // @flow
 import moment from 'moment';
-import { convertMomentToDateFormatString, convertIsoToLocalCalendar } from '../utils/converters/date';
+import { convertIsoToLocalCalendar } from '../utils/converters/date';
 import { dataElementTypes } from '../metaData';
 
 import { stringifyNumber } from './common/stringifyNumber';
@@ -56,7 +56,7 @@ function convertAgeForEdit(rawValue: string): AgeFormValue {
     const days = now.diff(age, 'days');
 
     return {
-        date: convertMomentToDateFormatString(moment(rawValue)),
+        date: convertIsoToLocalCalendar(rawValue),
         years: years.toString(),
         months: months.toString(),
         days: days.toString(),

From 1d3faa6538664fd29c9143751d2e24cf1c7f6369 Mon Sep 17 00:00:00 2001
From: Alaa Yahia <6881345+alaa-yahia@users.noreply.github.com>
Date: Mon, 16 Dec 2024 10:44:21 +0200
Subject: [PATCH 09/42] feat: [DHIS2 15466] typing the date when editing
 enrollment and incident date (#3905)

feat: typing the date when editing enrollment and incident date
---
 .../WidgetEnrollment/Date/Date.component.js   | 45 ++++++++++++-------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
index 0fdba91aea..1b8dc31ee1 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
@@ -1,9 +1,8 @@
 // @flow
 import React, { useState, useCallback } from 'react';
-import moment from 'moment';
+import { DateField } from 'capture-core/components/FormFields/New';
 import {
     Button,
-    CalendarInput,
     IconCalendar16,
     IconEdit16,
     colors,
@@ -12,8 +11,11 @@ import {
 import i18n from '@dhis2/d2-i18n';
 import { withStyles } from '@material-ui/core';
 import { convertValue as convertValueClientToView } from '../../../converters/clientToView';
+import { convertValue as convertValueFormToClient } from '../../../converters/formToClient';
+import { convertValue as convertValueClientToServer } from '../../../converters/clientToServer';
 import { dataElementTypes } from '../../../metaData';
 
+
 type Props = {
     date: string,
     dateLabel: string,
@@ -24,7 +26,7 @@ type Props = {
     ...CssClasses,
 }
 
-const styles = {
+const styles = (theme: Theme) => ({
     editButton: {
         display: 'inline-flex',
         alignItems: 'center',
@@ -62,7 +64,11 @@ const styles = {
         fontSize: '12px',
         color: colors.grey700,
     },
-};
+    error: {
+        ...theme.typography.caption,
+        color: theme.palette.error.main,
+    },
+});
 
 const DateComponentPlain = ({
     date,
@@ -75,21 +81,23 @@ const DateComponentPlain = ({
 }: Props) => {
     const [editMode, setEditMode] = useState(false);
     const [selectedDate, setSelectedDate] = useState();
-    const dateChangeHandler = useCallback(({ calendarDateString }) => {
-        setSelectedDate(calendarDateString);
+    const [validation, setValidation] = useState();
+
+    const dateChangeHandler = useCallback((dateString, internalComponentError) => {
+        setSelectedDate(dateString);
+        setValidation(internalComponentError);
     }, [setSelectedDate]);
     const displayDate = String(convertValueClientToView(date, dataElementTypes.DATE));
 
     const onOpenEdit = () => {
-        // CalendarInput component only supports the YYYY-MM-DD format
-        setSelectedDate(moment(date).format('YYYY-MM-DD'));
+        setSelectedDate(String(convertValueClientToView(date, dataElementTypes.DATE)));
         setEditMode(true);
     };
     const saveHandler = () => {
-        // CalendarInput component only supports the YYYY-MM-DD format
         if (selectedDate) {
-            const newDate = moment.utc(selectedDate, 'YYYY-MM-DD').format('YYYY-MM-DDTHH:mm:ss.SSS');
-            if (newDate !== date) {
+            const newClientDate = convertValueFormToClient(selectedDate, dataElementTypes.DATE);
+            const newDate = convertValueClientToServer(newClientDate, dataElementTypes.DATE);
+            if (typeof newDate === 'string' && newDate !== date) {
                 onSave(newDate);
             }
         }
@@ -99,21 +107,24 @@ const DateComponentPlain = ({
     return editMode ? (
         <div data-test="widget-enrollment-date">
             <div className={classes.inputField}>
-                <CalendarInput
-                    calendar="gregory"
-                    dense
-                    className={classes.calendar}
+                <DateField
+                    width={200}
+                    value={selectedDate}
+                    onBlur={dateChangeHandler}
                     label={dateLabel}
-                    date={selectedDate}
+                    dense
                     locale={locale}
-                    onDateSelect={dateChangeHandler}
                 />
+                <div className={classes.error}>
+                    {validation && validation.error ? i18n.t('Please provide a valid date') : ''}
+                </div>
             </div>
             <div className={classes.buttonStrip}>
                 <Button
                     primary
                     small
                     onClick={saveHandler}
+                    disabled={!!validation?.error}
                 >
                     {i18n.t('Save')}
                 </Button>

From 7968ea734503ae0e14d3c13a61baead5e67161f7 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 16 Dec 2024 11:45:42 +0200
Subject: [PATCH 10/42] fix: age values not filled correctly

---
 .../capture-ui/AgeField/AgeField.component.js            | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index a125ea8900..78abcf4a21 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -1,6 +1,8 @@
 // @flow
 import React, { Component } from 'react';
 import { isValidPositiveInteger } from 'capture-core-utils/validators/form';
+import { convertDateObjectToDateFormatString } from 'capture-core/utils/converters/date';
+import { systemSettingsStore } from 'capture-core/metaDataMemoryStores';
 import i18n from '@dhis2/d2-i18n';
 import classNames from 'classnames';
 import { IconButton } from 'capture-ui';
@@ -72,7 +74,8 @@ function getCalculatedValues(
             days: '',
         };
     }
-    const now = moment();
+    const dateFormat = systemSettingsStore.get().dateFormat;
+    const now = moment(convertDateObjectToDateFormatString(moment()), dateFormat);
     const age = moment(parseData.momentDate);
 
     const years = now.diff(age, 'years');
@@ -133,8 +136,8 @@ class D2AgeFieldPlain extends Component<Props> {
             this.props.onBlur({ ...values, date: '' });
             return;
         }
-
-        const momentDate = moment(undefined, undefined, true);
+        const dateFormat = systemSettingsStore.get().dateFormat;
+        const momentDate = moment(convertDateObjectToDateFormatString(moment(undefined, undefined, true)), dateFormat);
         momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.years), 'years');
         momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.months), 'months');
         momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.days), 'days');

From e8d226f9b13a93abee91e2c0f30a09bb81e88c9c Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Sun, 22 Dec 2024 23:51:24 +0200
Subject: [PATCH 11/42] fix: keep local calendar date in state in working list
 filters

---
 .../Date/DateFilter.component.js              | 82 +++++++------------
 .../Date/DateFilterManager.component.js       |  7 +-
 .../Date/dateFilterDataGetter.js              |  6 +-
 .../date/stringToMomentDateFormat.js          |  4 +-
 4 files changed, 36 insertions(+), 63 deletions(-)

diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
index 80b002c049..1e03d9167e 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
@@ -1,9 +1,9 @@
 // @flow
 import React, { Component } from 'react';
 import classNames from 'classnames';
-import moment from 'moment';
 import { withStyles } from '@material-ui/core/styles';
 import i18n from '@dhis2/d2-i18n';
+import { Temporal } from '@js-temporal/polyfill';
 import { isValidZeroOrPositiveInteger } from 'capture-core-utils/validators/form';
 import { SelectBoxes, orientations } from '../../FormFields/Options/SelectBoxes';
 import { OptionSet } from '../../../metaData/OptionSet/OptionSet';
@@ -17,8 +17,7 @@ import './calendarFilterStyles.css';
 import { mainOptionKeys, mainOptionTranslatedTexts } from './options';
 import { getDateFilterData } from './dateFilterDataGetter';
 import { RangeFilter } from './RangeFilter.component';
-import { parseDate, convertLocalToIsoCalendar, convertIsoToLocalCalendar } from '../../../utils/converters/date';
-import { systemSettingsStore } from '../../../metaDataMemoryStores';
+import { convertStringToDateFormat } from '../../../utils/converters/date';
 
 const getStyles = (theme: Theme) => ({
     fromToContainer: {
@@ -119,24 +118,27 @@ const getRelativeRangeErrors = (startValue, endValue, submitAttempted) => {
     return errors;
 };
 
+// eslint-disable-next-line complexity
 const isAbsoluteRangeFilterValid = (from, to) => {
-    if (!from?.value && !to?.value) {
-        return false;
-    }
     const fromValue = from?.value;
     const toValue = to?.value;
-    const parseResultFrom = fromValue ? parseDate(fromValue) : { isValid: true, moment: null };
-    const parseResultTo = toValue ? parseDate(toValue) : { isValid: true, moment: null };
 
-    if (!(parseResultFrom.isValid && parseResultTo.isValid)) {
+    if (!fromValue && !toValue) {
+        return false;
+    }
+
+    const isFromValueValid = from ? from.isValid : true;
+    const isToValueValid = to ? to.isValid : true;
+
+    if (!isFromValueValid || !isToValueValid) {
         return false;
     }
-    const isValidMomentDate = () =>
-        parseResultFrom.momentDate &&
-        parseResultTo.momentDate &&
-        parseResultFrom.momentDate.isAfter(parseResultTo.momentDate);
 
-    return !isValidMomentDate();
+    if ((!fromValue && toValue) || (fromValue && !toValue)) {
+        return true;
+    }
+
+    return !DateFilter.isFromAfterTo(fromValue, toValue);
 };
 
 const isRelativeRangeFilterValid = (startValue, endValue) => {
@@ -188,11 +190,9 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
     }
 
     static isFromAfterTo(valueFrom: string, valueTo: string) {
-        const momentFrom = parseDate(valueFrom).momentDate;
-        const momentTo = parseDate(valueTo).momentDate;
-        // $FlowFixMe[incompatible-use] automated comment
-        // $FlowFixMe[incompatible-call] automated comment
-        return momentFrom.isAfter(momentTo);
+        const formattedFrom = convertStringToDateFormat(valueFrom, 'YYYY-MM-DD');
+        const fromattedTo = convertStringToDateFormat(valueTo, 'YYYY-MM-DD');
+        return Temporal.PlainDate.compare(formattedFrom, fromattedTo) > 0;
     }
 
     toD2DateTextFieldInstance: any;
@@ -267,25 +267,12 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
         return !values || DateFilter.isFilterValid(values.main, values.from, values.to, values.start, values.end);
     }
 
-    getUpdatedValue(valuePart: Object) {
+    getUpdatedValue(valuePart: { [key: string]: string }) {
+        // $FlowFixMe[cannot-spread-indexer] automated comment
         const valueObject = {
             ...this.props.value,
             ...valuePart,
         };
-        const dateFormat = systemSettingsStore.get().dateFormat;
-
-        ['from', 'to'].forEach((key) => {
-            if (valuePart[key] && valueObject[key]?.value) {
-                const isValidDate = valuePart[key]?.isValid;
-                valueObject[key] = {
-                    ...valueObject[key],
-                    value: isValidDate ?
-                        moment(convertLocalToIsoCalendar(valueObject[key].value)).format(dateFormat) :
-                        valueObject[key].value,
-                };
-            }
-        });
-
         const isRelativeRangeValue = () => valueObject?.start || valuePart?.start || valuePart?.end;
         const isAbsoluteRangevalue = () => valueObject?.from || valuePart?.from || valuePart?.to;
 
@@ -358,23 +345,10 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
 
     render() {
         const { value, classes, onFocusUpdateButton } = this.props;
+        const fromValue = value?.from;
+        const toValue = value?.to;
         const { startValueError, endValueError, dateLogicError, bufferLogicError } =
-        this.getErrors();
-
-        const dateFormat = systemSettingsStore.get().dateFormat;
-
-        const formatDate = (dateValue) => {
-            if (!dateValue) return '';
-            const momentValue = moment(dateValue, dateFormat);
-            if (!momentValue.isValid()) return dateValue;
-            const isoDate = momentValue.format('YYYY-MM-DD');
-            return convertIsoToLocalCalendar(isoDate);
-        };
-
-        const from = value?.from;
-        const to = value?.to;
-        const fromDate = formatDate(from?.value);
-        const toDate = formatDate(to?.value);
+            this.getErrors();
 
         return (
             <div id="dateFilter">
@@ -394,11 +368,11 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <FromDateFilter
-                            value={fromDate}
+                            value={fromValue?.value}
                             onBlur={this.handleFieldBlur}
                             onEnterKey={this.handleEnterKeyInFrom}
                             onDateSelectedFromCalendar={this.handleDateSelectedFromCalendarInFrom}
-                            error={from?.error}
+                            error={fromValue?.error}
                             errorClass={classes.error}
                         />
                     </div>
@@ -407,11 +381,11 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
                         {/* $FlowSuppress: Flow not working 100% with HOCs */}
                         {/* $FlowFixMe[prop-missing] automated comment */}
                         <ToDateFilter
-                            value={toDate}
+                            value={toValue?.value}
                             onBlur={this.handleFieldBlur}
                             textFieldRef={this.setToD2DateTextFieldInstance}
                             onFocusUpdateButton={onFocusUpdateButton}
-                            error={to?.error}
+                            error={toValue?.error}
                             errorClass={classes.error}
                         />
                     </div>
diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilterManager.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilterManager.component.js
index 928d9fd754..c0464eb8a6 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilterManager.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilterManager.component.js
@@ -1,8 +1,7 @@
 // @flow
 import * as React from 'react';
-import moment from 'moment';
 import log from 'loglevel';
-import { convertMomentToDateFormatString } from '../../../utils/converters/date';
+import { convertIsoToLocalCalendar } from '../../../utils/converters/date';
 import { DateFilter } from './DateFilter.component';
 import { mainOptionKeys } from './options';
 import { dateFilterTypes } from './constants';
@@ -22,8 +21,8 @@ type State = {
 
 export class DateFilterManager extends React.Component<Props, State> {
     static convertDateForEdit(rawValue: string) {
-        const momentInstance = moment(rawValue);
-        return convertMomentToDateFormatString(momentInstance);
+        const localDate = convertIsoToLocalCalendar(rawValue);
+        return localDate;
     }
     static calculateAbsoluteRangeValueState(filter: DateFilterData) {
         return {
diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/dateFilterDataGetter.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/dateFilterDataGetter.js
index f7758cd1b1..6be16601ed 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/dateFilterDataGetter.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/dateFilterDataGetter.js
@@ -2,7 +2,7 @@
 import { parseNumber } from 'capture-core-utils/parsers';
 import { mainOptionKeys } from './options';
 import { dateFilterTypes } from './constants';
-import { parseDate } from '../../../utils/converters/date';
+import { convertLocalToIsoCalendar } from '../../../utils/converters/date';
 import { type AbsoluteDateFilterData, type RelativeDateFilterData, type DateValue } from './types';
 
 type Value = {
@@ -20,13 +20,13 @@ function convertAbsoluteDate(fromValue: ?string, toValue: ?string) {
 
     if (fromValue) {
         // $FlowFixMe[incompatible-type] automated comment
-        const fromClientValue: string = parseDate(fromValue).momentDate;
+        const fromClientValue: string = convertLocalToIsoCalendar(fromValue);
         rangeData.ge = fromClientValue;
     }
 
     if (toValue) {
         // $FlowFixMe[incompatible-type] automated comment
-        const toClientValue: string = parseDate(toValue).momentDate;
+        const toClientValue: string = convertLocalToIsoCalendar(toValue);
         rangeData.le = toClientValue;
     }
 
diff --git a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
index eed1df7957..1e718a5119 100644
--- a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
+++ b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
@@ -8,9 +8,9 @@ import { systemSettingsStore } from '../../../metaDataMemoryStores';
  * @param {*} string - the string instance
  * @returns {string}
  */
-export function convertStringToDateFormat(date: string) {
+export function convertStringToDateFormat(date: string, format) {
     if (!date || !date.length) { return ''; }
-    const dateFormat = systemSettingsStore.get().dateFormat;
+    const dateFormat = format || systemSettingsStore.get().dateFormat;
     const formattedDateString = moment(date, dateFormat).format(dateFormat);
     return formattedDateString;
 }

From 8ce68dd3714d1201b1bacc94055c03e58539de3c Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Sun, 22 Dec 2024 23:55:07 +0200
Subject: [PATCH 12/42] fix: rename calendarMaxMoment to calendarMax

---
 .../D2Form/field/configs/dateField/getDateFieldConfig.js  | 2 +-
 .../configs/dateField/getDateFieldConfigForCustomForm.js  | 2 +-
 .../Enrollment/EnrollmentDataEntry.component.js           | 4 ++--
 .../DateAndTimeFields/DateField/Date.component.js         | 8 ++++----
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
index 7415bd7d5f..355346d87d 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
@@ -16,7 +16,7 @@ export const getDateFieldConfig = (metaData: DateDataElement, options: Object, q
         maxWidth: options.formHorizontal ? 150 : 350,
         calendarWidth: options.formHorizontal ? 250 : 350,
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
-        calendarMaxMoment: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+        calendarMax: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
index 8cb139fb73..efeea1412c 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
@@ -11,7 +11,7 @@ export const getDateFieldConfigForCustomForm = (metaData: DateDataElement, optio
         width: 350,
         maxWidth: 350,
         calendarWidth: 350,
-        calendarMaxMoment: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+        calendarMax: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
     }, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
index 76ed900ccc..5ca70266c2 100644
--- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
+++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
@@ -112,7 +112,7 @@ const getEnrollmentDateSettings = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
-            calendarMaxMoment: !props.enrollmentMetadata.allowFutureEnrollmentDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+            calendarMax: !props.enrollmentMetadata.allowFutureEnrollmentDate ? convertDateObjectToDateFormatString(moment()) : undefined,
         }),
         getPropName: () => 'enrolledAt',
         getValidatorContainers: getEnrollmentDateValidatorContainer,
@@ -160,7 +160,7 @@ const getIncidentDateSettings = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
-            calendarMaxMoment: !props.enrollmentMetadata.allowFutureIncidentDate ?
+            calendarMax: !props.enrollmentMetadata.allowFutureIncidentDate ?
                 convertDateObjectToDateFormatString(moment()) :
                 undefined,
         }),
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
index 95bf289714..c8ba299197 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
@@ -20,7 +20,7 @@ type Props = {
     onDateSelectedFromCalendar?: () => void,
     placeholder?: string,
     label?: string,
-    calendarMaxMoment?: any,
+    calendarMax?: any,
     innerMessage?: any
 };
 
@@ -61,11 +61,11 @@ export class DateField extends React.Component<Props, State> {
             maxWidth,
             calendarWidth,
             inputWidth,
-            calendarMaxMoment,
+            calendarMax,
             value,
             innerMessage,
         } = this.props;
-
+console.log(calendarMax,"calendarMax")
         const calculatedInputWidth = inputWidth || width;
         const calculatedCalendarWidth = calendarWidth || width;
         const calendarType = systemSettingsStore.get().calendar || 'gregory';
@@ -94,7 +94,7 @@ export class DateField extends React.Component<Props, State> {
                     onFocus={this.props.onFocus}
                     disabled={this.props.disabled}
                     {...errorProps}
-                    maxDate={calendarMaxMoment}
+                    maxDate={calendarMax}
                 />
             </div>
         );

From b0dc55c8b770280b8ee0165ce64c06235db4fb59 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 23 Dec 2024 00:52:08 +0200
Subject: [PATCH 13/42] fix: remove formating lines

---
 .../capture-core/converters/clientToForm.js            |  7 ++-----
 .../capture-core/converters/clientToList.js            |  7 ++-----
 .../capture-core/converters/clientToView.js            |  7 ++-----
 .../capture-core/converters/formToClient.js            |  8 ++------
 .../utils/converters/date/convertLocalToIsoCalendar.js | 10 +++++++++-
 .../utils/converters/date/stringToMomentDateFormat.js  |  3 ++-
 .../DateAndTimeFields/DateField/Date.component.js      |  1 -
 7 files changed, 19 insertions(+), 24 deletions(-)

diff --git a/src/core_modules/capture-core/converters/clientToForm.js b/src/core_modules/capture-core/converters/clientToForm.js
index c0b0d10948..d8cec38fd3 100644
--- a/src/core_modules/capture-core/converters/clientToForm.js
+++ b/src/core_modules/capture-core/converters/clientToForm.js
@@ -23,17 +23,14 @@ type RangeValue = {
 }
 
 function convertDateForEdit(rawValue: string): string {
-    const momentDate = moment(rawValue);
-    const dateString = momentDate.format('YYYY-MM-DD');
-    return convertIsoToLocalCalendar(dateString);
+    return convertIsoToLocalCalendar(rawValue);
 }
 
 function convertDateTimeForEdit(rawValue: string): DateTimeFormValue {
     const dateTime = moment(rawValue);
-    const dateString = dateTime.format('YYYY-MM-DD');
     const timeString = dateTime.format('HH:mm');
     return {
-        date: convertIsoToLocalCalendar(dateString),
+        date: convertIsoToLocalCalendar(rawValue),
         time: timeString,
     };
 }
diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js
index 6ee3cb78a7..f026191a4e 100644
--- a/src/core_modules/capture-core/converters/clientToList.js
+++ b/src/core_modules/capture-core/converters/clientToList.js
@@ -11,16 +11,13 @@ import { MinimalCoordinates } from '../components/MinimalCoordinates';
 import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';
 
 function convertDateForListDisplay(rawValue: string): string {
-    const momentDate = moment(rawValue);
-    const dateString = momentDate.format('YYYY-MM-DD');
-    return convertIsoToLocalCalendar(dateString);
+    return convertIsoToLocalCalendar(rawValue);
 }
 
 function convertDateTimeForListDisplay(rawValue: string): string {
     const momentDate = moment(rawValue);
-    const dateString = momentDate.format('YYYY-MM-DD');
     const timeString = momentDate.format('HH:mm');
-    const localDate = convertIsoToLocalCalendar(dateString);
+    const localDate = convertIsoToLocalCalendar(rawValue);
     return `${localDate} ${timeString}`;
 }
 
diff --git a/src/core_modules/capture-core/converters/clientToView.js b/src/core_modules/capture-core/converters/clientToView.js
index 6f4e2b7e14..0eb7efc21e 100644
--- a/src/core_modules/capture-core/converters/clientToView.js
+++ b/src/core_modules/capture-core/converters/clientToView.js
@@ -11,16 +11,13 @@ import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';
 
 
 function convertDateForView(rawValue: string): string {
-    const momentDate = moment(rawValue);
-    const dateString = momentDate.format('YYYY-MM-DD');
-    return convertIsoToLocalCalendar(dateString);
+    return convertIsoToLocalCalendar(rawValue);
 }
 function convertDateTimeForView(rawValue: string): string {
     const momentDate = moment(rawValue);
-    const dateString = momentDate.format('YYYY-MM-DD');
     const timeString = momentDate.format('HH:mm');
 
-    const localDate = convertIsoToLocalCalendar(dateString);
+    const localDate = convertIsoToLocalCalendar(rawValue);
     return `${localDate} ${timeString}`;
 }
 
diff --git a/src/core_modules/capture-core/converters/formToClient.js b/src/core_modules/capture-core/converters/formToClient.js
index 61b37923fd..d0a4460e24 100644
--- a/src/core_modules/capture-core/converters/formToClient.js
+++ b/src/core_modules/capture-core/converters/formToClient.js
@@ -38,12 +38,8 @@ function convertDateTime(formValue: DateTimeValue): ?string {
 
 function convertDate(dateValue: string) {
     const parsedDate = parseDate(dateValue);
-    if (!parsedDate.isValid || !parsedDate.momentDate) {
-        return null;
-    }
-    const formattedDate = parsedDate.momentDate.format('YYYY-MM-DD');
-
-    return convertLocalToIsoCalendar(formattedDate);
+    // $FlowFixMe[incompatible-use] automated comment
+    return parsedDate.isValid ? convertLocalToIsoCalendar(parsedDate.momentDate.toISOString()) : null;
 }
 
 function convertTime(timeValue: string) {
diff --git a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
index b3f323d178..2c2ce3d192 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
@@ -16,9 +16,17 @@ export function convertLocalToIsoCalendar(localDate: ?string): string {
     if (!localDate) {
         return '';
     }
+
+    const momentDate = moment(localDate);
+    if (!momentDate.isValid()) {
+        return '';
+    }
+
+    const formattedIsoDate = momentDate.format('YYYY-MM-DD');
+
     const calendar = systemSettingsStore.get().calendar;
 
-    const { year, month, day } = convertToIso8601(localDate, calendar);
+    const { year, month, day } = convertToIso8601(formattedIsoDate, calendar);
     const dateString = `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}`;
     const parsedMoment = moment(dateString);
 
diff --git a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
index 1e718a5119..29c7b2c929 100644
--- a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
+++ b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
@@ -6,9 +6,10 @@ import { systemSettingsStore } from '../../../metaDataMemoryStores';
  * Converts a string date to a string date with default format based on the system date format
  * @export
  * @param {*} string - the string instance
+ * @param {string} [format] - optional date format. If not provided, the function uses system date format
  * @returns {string}
  */
-export function convertStringToDateFormat(date: string, format) {
+export function convertStringToDateFormat(date: string, format?: string) {
     if (!date || !date.length) { return ''; }
     const dateFormat = format || systemSettingsStore.get().dateFormat;
     const formattedDateString = moment(date, dateFormat).format(dateFormat);
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
index c8ba299197..1c4b2ee4b0 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
@@ -65,7 +65,6 @@ export class DateField extends React.Component<Props, State> {
             value,
             innerMessage,
         } = this.props;
-console.log(calendarMax,"calendarMax")
         const calculatedInputWidth = inputWidth || width;
         const calculatedCalendarWidth = calendarWidth || width;
         const calendarType = systemSettingsStore.get().calendar || 'gregory';

From 2cc70be916491eb0988cd81d2110ec77f299a939 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 23 Dec 2024 02:57:17 +0200
Subject: [PATCH 14/42] fix: day value not giving correct result in age field

---
 .../New/Fields/AgeField/AgeField.component.js |  5 --
 .../date/convertStringToTemporal.js           | 39 +++++++++
 .../date/convertTemporalToString.js           | 29 +++++++
 .../utils/converters/date/index.js            |  2 +
 .../capture-ui/AgeField/AgeField.component.js | 85 +++++++------------
 5 files changed, 102 insertions(+), 58 deletions(-)
 create mode 100644 src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
 create mode 100644 src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js

diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
index 2306b0854d..c160c34475 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
@@ -2,8 +2,6 @@
 import * as React from 'react';
 import { withStyles, withTheme } from '@material-ui/core/styles';
 import { AgeField as UIAgeField } from 'capture-ui';
-import moment from 'moment';
-import { parseDate, convertMomentToDateFormatString } from '../../../../../utils/converters/date';
 import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 
 const getStyles = (theme: Theme) => ({
@@ -50,9 +48,6 @@ const AgeFieldPlain = (props: Props) => {
     return (
         // $FlowFixMe[cannot-spread-inexact] automated comment
         <UIAgeField
-            onParseDate={parseDate}
-            onGetFormattedDateStringFromMoment={convertMomentToDateFormatString}
-            moment={moment}
             datePlaceholder={systemSettingsStore.get().dateFormat.toLowerCase()}
             {...passOnProps}
         />
diff --git a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
new file mode 100644
index 0000000000..5eee8cc7a1
--- /dev/null
+++ b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
@@ -0,0 +1,39 @@
+// @flow
+import { Temporal } from '@js-temporal/polyfill';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
+
+/**
+ * Converts a date string into a Temporal.PlainDate object using the system set calendar
+ * @export
+ * @param {*} string - dateString
+ * @returns {(Temporal.PlainDate | null)}
+ */
+
+export function convertStringToTemporal(dateString: string): Temporal.PlainDate | null {
+    if (!dateString) {
+        return null;
+    }
+    try {
+        const dateWithHyphen = dateString.replace(/[\/\.]/g, '-');
+
+        const calendar = systemSettingsStore.get().calendar;
+        const dateFormat = systemSettingsStore.get().dateFormat;
+
+        let year; let month; let day;
+
+        if (dateFormat === 'YYYY-MM-DD') {
+            [year, month, day] = dateWithHyphen.split('-').map(Number);
+        }
+        if (dateFormat === 'DD-MM-YYYY') {
+            [day, month, year] = dateWithHyphen.split('-').map(Number);
+        }
+        return Temporal.PlainDate.from({
+            year,
+            month,
+            day,
+            calendar,
+        });
+    } catch (error) {
+        return '';
+    }
+}
diff --git a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
new file mode 100644
index 0000000000..a295b7984d
--- /dev/null
+++ b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
@@ -0,0 +1,29 @@
+// @flow
+import { Temporal } from '@js-temporal/polyfill';
+import { padWithZeros } from './padWithZeros';
+import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
+
+/**
+ * Converts a Temporal.PlainDate to a formatted date string (YYYY-MM-DD || DD-MM-YYYY)
+ * @param {Temporal.PlainDate} temporalDate - The Temporal date to convert
+ * @returns {string} Formatted date string, or empty string if invalid
+ */
+
+export function convertTemporalToString(temporalDate: Temporal.PlainDate | null): string {
+    if (!temporalDate) {
+        return '';
+    }
+    const dateFormat = systemSettingsStore.get().dateFormat;
+
+    try {
+        const year = temporalDate.year;
+        const month = temporalDate.month;
+        const day = temporalDate.day;
+
+        return dateFormat === 'YYYY-MM-DD' ?
+            `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}` :
+            `${padWithZeros(day, 2)}-${padWithZeros(month, 2)}-${padWithZeros(year, 4)}`;
+    } catch (error) {
+        return '';
+    }
+}
diff --git a/src/core_modules/capture-core/utils/converters/date/index.js b/src/core_modules/capture-core/utils/converters/date/index.js
index 511298b89d..11f200fb34 100644
--- a/src/core_modules/capture-core/utils/converters/date/index.js
+++ b/src/core_modules/capture-core/utils/converters/date/index.js
@@ -6,3 +6,5 @@ export { convertStringToDateFormat } from './stringToMomentDateFormat';
 export { padWithZeros } from './padWithZeros';
 export { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
 export { convertLocalToIsoCalendar } from './convertLocalToIsoCalendar';
+export { convertStringToTemporal } from './convertStringToTemporal';
+export { convertTemporalToString } from './convertTemporalToString';
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index 78abcf4a21..b04b755613 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -1,7 +1,7 @@
 // @flow
 import React, { Component } from 'react';
+import { Temporal } from '@js-temporal/polyfill';
 import { isValidPositiveInteger } from 'capture-core-utils/validators/form';
-import { convertDateObjectToDateFormatString } from 'capture-core/utils/converters/date';
 import { systemSettingsStore } from 'capture-core/metaDataMemoryStores';
 import i18n from '@dhis2/d2-i18n';
 import classNames from 'classnames';
@@ -12,6 +12,7 @@ import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
 import { orientations } from '../constants/orientations.const';
 import { withInternalChangeHandler } from '../HOC/withInternalChangeHandler';
+import { convertStringToTemporal, convertTemporalToString } from '../../capture-core/utils/converters/date';
 
 type AgeValues = {
     date?: ?string,
@@ -47,9 +48,6 @@ type Props = {
     inputMessageClasses: ?InputMessageClasses,
     inFocus?: ?boolean,
     shrinkDisabled?: ?boolean,
-    onParseDate: DateParser,
-    onGetFormattedDateStringFromMoment: DateStringFromMomentFormatter,
-    moment: any,
     dateCalendarTheme: Object,
     dateCalendarWidth?: ?any,
     datePopupAnchorPosition?: ?string,
@@ -59,38 +57,26 @@ type Props = {
     datePlaceholder?: ?string,
     disabled?: ?boolean,
 };
-function getCalculatedValues(
-    dateValue: ?string,
-    onParseDate: DateParser,
-    onGetFormattedDateStringFromMoment: DateStringFromMomentFormatter,
-    moment: any,
-): AgeValues {
-    const parseData = dateValue && onParseDate(dateValue);
-    if (!parseData || !parseData.isValid) {
-        return {
-            date: dateValue,
-            years: '',
-            months: '',
-            days: '',
-        };
-    }
-    const dateFormat = systemSettingsStore.get().dateFormat;
-    const now = moment(convertDateObjectToDateFormatString(moment()), dateFormat);
-    const age = moment(parseData.momentDate);
 
-    const years = now.diff(age, 'years');
-    age.add(years, 'years');
+function getCalculatedValues(dateValue: ?string): AgeValues {
+    const calendar = systemSettingsStore.get().calendar;
+
+    const now = Temporal.Now.plainDateISO().withCalendar(calendar);
 
-    const months = now.diff(age, 'months');
-    age.add(months, 'months');
+    const age = convertStringToTemporal(dateValue);
 
-    const days = now.diff(age, 'days');
+    const diff = now.since(age, {
+        largestUnit: 'years',
+        smallestUnit: 'days',
+    });
+
+    const date = convertTemporalToString(age);
 
     return {
-        date: onGetFormattedDateStringFromMoment(parseData.momentDate),
-        years: years.toString(),
-        months: months.toString(),
-        days: days.toString(),
+        date,
+        years: diff.years.toString(),
+        months: diff.months.toString(),
+        days: diff.days.toString(),
     };
 }
 
@@ -124,7 +110,7 @@ class D2AgeFieldPlain extends Component<Props> {
     }
 
     handleNumberBlur = (values: AgeValues) => {
-        const { onParseDate, onGetFormattedDateStringFromMoment, onRemoveFocus, moment } = this.props;
+        const { onRemoveFocus } = this.props;
 
         onRemoveFocus && onRemoveFocus();
         if (D2AgeFieldPlain.isEmptyNumbers(values)) {
@@ -136,28 +122,25 @@ class D2AgeFieldPlain extends Component<Props> {
             this.props.onBlur({ ...values, date: '' });
             return;
         }
-        const dateFormat = systemSettingsStore.get().dateFormat;
-        const momentDate = moment(convertDateObjectToDateFormatString(moment(undefined, undefined, true)), dateFormat);
-        momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.years), 'years');
-        momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.months), 'months');
-        momentDate.subtract(D2AgeFieldPlain.getNumberOrZero(values.days), 'days');
-        const calculatedValues = getCalculatedValues(
-            onGetFormattedDateStringFromMoment(momentDate),
-            onParseDate,
-            onGetFormattedDateStringFromMoment,
-            moment,
-        );
+
+        const calendar = systemSettingsStore.get().calendar;
+
+        const now = Temporal.Now.plainDateISO().withCalendar(calendar);
+
+        const calculatedDate = now.subtract({
+            years: D2AgeFieldPlain.getNumberOrZero(values.years),
+            months: D2AgeFieldPlain.getNumberOrZero(values.months),
+            days: D2AgeFieldPlain.getNumberOrZero(values.days),
+        });
+
+        const calculatedValues = getCalculatedValues(convertTemporalToString(calculatedDate));
         this.props.onBlur(calculatedValues);
     }
 
     handleDateBlur = (date: ?string, options: ?ValidationOptions) => {
-        const { onParseDate, onGetFormattedDateStringFromMoment, onRemoveFocus, moment } = this.props;
+        const { onRemoveFocus } = this.props;
         onRemoveFocus && onRemoveFocus();
-        const calculatedValues = date ? getCalculatedValues(
-            date,
-            onParseDate,
-            onGetFormattedDateStringFromMoment,
-            moment) : null;
+        const calculatedValues = date ? getCalculatedValues(date) : null;
         this.props.onBlur(calculatedValues, options);
     }
 
@@ -184,8 +167,6 @@ class D2AgeFieldPlain extends Component<Props> {
             datePopupAnchorPosition,
             dateCalendarTheme,
             dateCalendarLocale,
-            moment,
-            onParseDate,
             ...passOnProps } = this.props;
         return (
             <div className={defaultClasses.ageNumberInputContainer}>
@@ -211,8 +192,6 @@ class D2AgeFieldPlain extends Component<Props> {
             shrinkDisabled,
             dateCalendarWidth,
             datePlaceholder,
-            moment,
-            onParseDate,
             ...passOnProps
         } = this.props;
 

From 383c66fc571b6b4220fc0abf43f748744b62191f Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 23 Dec 2024 03:13:11 +0200
Subject: [PATCH 15/42] fix: flow errors

---
 .../utils/converters/date/convertStringToTemporal.js   | 10 ++++++++--
 .../utils/converters/date/convertTemporalToString.js   |  9 +++++++--
 .../capture-ui/AgeField/AgeField.component.js          |  4 ----
 3 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
index 5eee8cc7a1..a50c92be11 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
@@ -9,7 +9,13 @@ import { systemSettingsStore } from '../../../metaDataMemoryStores';
  * @returns {(Temporal.PlainDate | null)}
  */
 
-export function convertStringToTemporal(dateString: string): Temporal.PlainDate | null {
+type PlainDate = {
+    year: number,
+    month: number,
+    day: number
+};
+
+export function convertStringToTemporal(dateString: ?string): PlainDate | null {
     if (!dateString) {
         return null;
     }
@@ -34,6 +40,6 @@ export function convertStringToTemporal(dateString: string): Temporal.PlainDate
             calendar,
         });
     } catch (error) {
-        return '';
+        return null;
     }
 }
diff --git a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
index a295b7984d..e413468c66 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
@@ -1,5 +1,4 @@
 // @flow
-import { Temporal } from '@js-temporal/polyfill';
 import { padWithZeros } from './padWithZeros';
 import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
 
@@ -9,7 +8,13 @@ import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStor
  * @returns {string} Formatted date string, or empty string if invalid
  */
 
-export function convertTemporalToString(temporalDate: Temporal.PlainDate | null): string {
+type PlainDate = {
+    year: number,
+    month: number,
+    day: number
+};
+
+export function convertTemporalToString(temporalDate: PlainDate | null): string {
     if (!temporalDate) {
         return '';
     }
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index b04b755613..fccf96d0e9 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -28,10 +28,6 @@ type InputMessageClasses = {
     validating?: ?string,
 }
 
-type DateParser = (value: string) => { isValid: boolean, momentDate: any };
-
-type DateStringFromMomentFormatter = (momentValue: Object) => string;
-
 type ValidationOptions = {
     error?: ?string,
     errorCode?: ?string,

From 013db2ccccfea8aae6363a471820c3bec70193d3 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 6 Jan 2025 12:37:16 +0200
Subject: [PATCH 16/42] fix: display local time in tooltips

---
 package.json                                  |  4 +-
 .../WidgetEnrollment.component.js             |  3 +-
 .../WidgetNote/NoteSection/NoteSection.js     | 41 +++++++++++--------
 .../StageOverview/StageOverview.component.js  |  5 ++-
 4 files changed, 31 insertions(+), 22 deletions(-)

diff --git a/package.json b/package.json
index 1178b6e2e1..e4caffa666 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,7 @@
         "packages/rules-engine"
     ],
     "dependencies": {
-        "@dhis2/rules-engine-javascript": "101.19.1",
-        "@dhis2-ui/calendar": "^10.0.3",
+        "@dhis2/rules-engine-javascript": "101.20.3",
         "@dhis2/app-runtime": "^3.9.3",
         "@dhis2/d2-i18n": "^1.1.0",
         "@dhis2/d2-icons": "^1.0.1",
@@ -20,6 +19,7 @@
         "@dhis2/d2-ui-rich-text": "^7.4.0",
         "@dhis2/d2-ui-sharing-dialog": "^7.3.3",
         "@dhis2/ui": "^9.10.1",
+        "@dhis2-ui/calendar": "^10.0.3",
         "@joakim_sm/react-infinite-calendar": "^2.4.2",
         "@material-ui/core": "3.9.4",
         "@material-ui/icons": "3",
diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
index 1cd597f02d..66b9321c2a 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
@@ -74,6 +74,7 @@ export const WidgetEnrollmentPlain = ({
 }: PlainProps) => {
     const [open, setOpenStatus] = useState(true);
     const { fromServerDate } = useTimeZoneConversion();
+    const localDateTime: string = (convertValue(enrollment?.updatedAt, dataElementTypes.DATETIME): any);
     const geometryType = getGeometryType(enrollment?.geometry?.type);
     const { displayName: orgUnitName, ancestors } = useOrgUnitNameWithAncestors(enrollment?.orgUnit);
     const { displayName: ownerOrgUnitName, ancestors: ownerAncestors } = useOrgUnitNameWithAncestors(ownerOrgUnit?.id);
@@ -157,7 +158,7 @@ export const WidgetEnrollmentPlain = ({
                                 <IconClock16 color={colors.grey600} />
                             </span>
                             {i18n.t('Last updated')}
-                            <Tooltip content={(fromServerDate(enrollment.updatedAt).toLocaleString())}>
+                            <Tooltip content={(fromServerDate(localDateTime).toLocaleString())}>
                                 {moment(fromServerDate(enrollment.updatedAt)).fromNow()}
                             </Tooltip>
                         </div>
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index f908d1d3ed..825d27d527 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -9,6 +9,8 @@ import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
+import { convertValue as convertValueClientToView } from '../../../converters/clientToView';
+import { dataElementTypes } from '../../../metaData';
 
 const FocusTextField = withFocusSaver()(TextField);
 
@@ -97,26 +99,29 @@ const NoteSectionPlain = ({
         setEditing(false);
     }, [handleAddNote, newNoteValue]);
 
-    const NoteItem = ({ value, storedAt, createdBy }) => (
-        <div data-test="note-item" className={cx(classes.item)}>
-            {/* TODO: add avatar */}
-            <div className={classes.rightColumn}>
-                <div className={classes.header}>
-                    {createdBy && <span className={cx(classes.headerText, classes.name)}>
-                        {createdBy.firstName} {' '} {createdBy.surname}
-                    </span>}
-                    <span className={cx(classes.headerText, classes.lastUpdated)}>
-                        <Tooltip content={fromServerDate(storedAt).toLocaleString()}>
-                            {moment(fromServerDate(storedAt)).fromNow()}
-                        </Tooltip>
-                    </span>
-                </div>
-                <div className={classes.body}>
-                    <Parser>{value}</Parser>
+    const NoteItem = ({ value, storedAt, createdBy }) => {
+        const localDateTime: string = (convertValueClientToView(storedAt, dataElementTypes.DATETIME): any);
+        return (
+            <div data-test="note-item" className={cx(classes.item)}>
+                {/* TODO: add avatar */}
+                <div className={classes.rightColumn}>
+                    <div className={classes.header}>
+                        {createdBy && <span className={cx(classes.headerText, classes.name)}>
+                            {createdBy.firstName} {' '} {createdBy.surname}
+                        </span>}
+                        <span className={cx(classes.headerText, classes.lastUpdated)}>
+                            <Tooltip content={fromServerDate(localDateTime).toLocaleString()}>
+                                {moment(fromServerDate(storedAt)).fromNow()}
+                            </Tooltip>
+                        </span>
+                    </div>
+                    <div className={classes.body}>
+                        <Parser>{value}</Parser>
+                    </div>
                 </div>
             </div>
-        </div>
-    );
+        );
+    };
 
 
     return (
diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
index 907e049840..5c72a76670 100644
--- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
+++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
@@ -12,6 +12,8 @@ import { statusTypes } from 'capture-core/events/statusTypes';
 import { NonBundledDhis2Icon } from '../../../../NonBundledDhis2Icon';
 import type { Props } from './stageOverview.types';
 import { isEventOverdue } from '../StageDetail/hooks/helpers';
+import { convertValue as convertValueClientToView } from '../../../../../converters/clientToView';
+import { dataElementTypes } from '../../../../../metaData';
 
 const styles = {
     container: {
@@ -71,11 +73,12 @@ const getLastUpdatedAt = (events, fromServerDate) => {
 
     if (lastEventUpdated) {
         const { updatedAt } = lastEventUpdated;
+        const localDateTime: string = (convertValueClientToView(updatedAt, dataElementTypes.DATETIME): any);
         return lastEventUpdated?.updatedAt && moment(updatedAt).isValid()
             ? (
                 <>
                     {i18n.t('Last updated')}&nbsp;
-                    <Tooltip content={fromServerDate(updatedAt).toLocaleString()}>
+                    <Tooltip content={fromServerDate(localDateTime).toLocaleString()}>
                         {moment(fromServerDate(updatedAt)).fromNow()}
                     </Tooltip>
                 </>

From 8dd111092a4f17190c4c1cd9de8b2d00cca89989 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 6 Jan 2025 15:22:02 +0200
Subject: [PATCH 17/42] fix: date is not valid error not displayed

---
 .../capture-ui/AgeField/AgeField.component.js         | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index fccf96d0e9..65085efe6b 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -7,6 +7,7 @@ import i18n from '@dhis2/d2-i18n';
 import classNames from 'classnames';
 import { IconButton } from 'capture-ui';
 import { IconCross24 } from '@dhis2/ui';
+import { parseDate } from 'capture-core/utils/converters/date';
 import { AgeNumberInput } from '../internal/AgeInput/AgeNumberInput.component';
 import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
@@ -55,6 +56,16 @@ type Props = {
 };
 
 function getCalculatedValues(dateValue: ?string): AgeValues {
+    const parseData = dateValue && parseDate(dateValue);
+    if (!parseData || !parseData.isValid) {
+        return {
+            date: dateValue,
+            years: '',
+            months: '',
+            days: '',
+        };
+    }
+
     const calendar = systemSettingsStore.get().calendar;
 
     const now = Temporal.Now.plainDateISO().withCalendar(calendar);

From 209860c630fce98c981aff7413513b7bda13676d Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 9 Jan 2025 13:42:31 +0200
Subject: [PATCH 18/42] fix: remove app specific objects

---
 .../capture-core-utils/date/index.js          |  3 +
 .../date/padWithZeros.js                      |  0
 .../date/stringToTemporal.js                  | 45 +++++++++++++++
 .../date/temporalToString.js                  | 33 +++++++++++
 .../configs/ageField/getAgeFieldConfig.js     |  3 +
 .../configs/dateField/getDateFieldConfig.js   |  3 +
 .../date/convertIsoToLocalCalendar.js         |  2 +-
 .../date/convertLocalToIsoCalendar.js         |  2 +-
 .../date/convertStringToTemporal.js           | 28 ++--------
 .../date/convertTemporalToString.js           | 15 +----
 .../utils/converters/date/index.js            |  1 -
 .../capture-ui/AgeField/AgeField.component.js | 55 ++++++++++---------
 .../DateField/Date.component.js               | 13 +++--
 13 files changed, 131 insertions(+), 72 deletions(-)
 rename src/core_modules/{capture-core/utils/converters => capture-core-utils}/date/padWithZeros.js (100%)
 create mode 100644 src/core_modules/capture-core-utils/date/stringToTemporal.js
 create mode 100644 src/core_modules/capture-core-utils/date/temporalToString.js

diff --git a/src/core_modules/capture-core-utils/date/index.js b/src/core_modules/capture-core-utils/date/index.js
index 8af83e8b8d..f29e3a8760 100644
--- a/src/core_modules/capture-core-utils/date/index.js
+++ b/src/core_modules/capture-core-utils/date/index.js
@@ -1,2 +1,5 @@
 // @flow
 export { getFormattedStringFromMomentUsingEuropeanGlyphs } from './date.utils';
+export { padWithZeros } from './padWithZeros';
+export { temporalToString } from './temporalToString';
+export { stringToTemporal } from './stringToTemporal';
diff --git a/src/core_modules/capture-core/utils/converters/date/padWithZeros.js b/src/core_modules/capture-core-utils/date/padWithZeros.js
similarity index 100%
rename from src/core_modules/capture-core/utils/converters/date/padWithZeros.js
rename to src/core_modules/capture-core-utils/date/padWithZeros.js
diff --git a/src/core_modules/capture-core-utils/date/stringToTemporal.js b/src/core_modules/capture-core-utils/date/stringToTemporal.js
new file mode 100644
index 0000000000..8063305d0f
--- /dev/null
+++ b/src/core_modules/capture-core-utils/date/stringToTemporal.js
@@ -0,0 +1,45 @@
+// @flow
+import { Temporal } from '@js-temporal/polyfill';
+
+/**
+ * Converts a date string into a Temporal.PlainDate object using the specified calendar
+ * @export
+ * @param {?string} dateString - The date string to convert
+ * @param {?string} calendarType - The calendar type to use
+ * @param {?string} dateFormat - The current system date format ('YYYY-MM-DD' or 'DD-MM-YYYY')
+ * @returns {(Temporal.PlainDate | null)}
+ */
+
+type PlainDate = {
+    year: number,
+    month: number,
+    day: number
+};
+
+export function stringToTemporal(dateString: ?string,
+    calendarType: ?string,
+    dateFormat: ?string): PlainDate | null {
+    if (!dateString) {
+        return null;
+    }
+    try {
+        const dateWithHyphen = dateString.replace(/[\/\.]/g, '-');
+
+        let year; let month; let day;
+
+        if (dateFormat === 'YYYY-MM-DD') {
+            [year, month, day] = dateWithHyphen.split('-').map(Number);
+        }
+        if (dateFormat === 'DD-MM-YYYY') {
+            [day, month, year] = dateWithHyphen.split('-').map(Number);
+        }
+        return Temporal.PlainDate.from({
+            year,
+            month,
+            day,
+            calendarType,
+        });
+    } catch (error) {
+        return null;
+    }
+}
diff --git a/src/core_modules/capture-core-utils/date/temporalToString.js b/src/core_modules/capture-core-utils/date/temporalToString.js
new file mode 100644
index 0000000000..72f650b149
--- /dev/null
+++ b/src/core_modules/capture-core-utils/date/temporalToString.js
@@ -0,0 +1,33 @@
+// @flow
+import { padWithZeros } from './padWithZeros';
+
+/**
+ * Converts a Temporal.PlainDate to a formatted date string (YYYY-MM-DD or DD-MM-YYYY)
+ * @param {Temporal.PlainDate | null} temporalDate - The Temporal date to convert
+ * @param {?string} dateFormat - The current system date format ('YYYY-MM-DD' or 'DD-MM-YYYY')
+ * @returns {string} Formatted date string, or empty string if invalid
+ */
+
+type PlainDate = {
+    year: number,
+    month: number,
+    day: number
+};
+
+export function temporalToString(temporalDate: PlainDate | null, dateFormat: ?string): string {
+    if (!temporalDate) {
+        return '';
+    }
+
+    try {
+        const year = temporalDate.year;
+        const month = temporalDate.month;
+        const day = temporalDate.day;
+
+        return dateFormat === 'YYYY-MM-DD' ?
+            `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}` :
+            `${padWithZeros(day, 2)}-${padWithZeros(month, 2)}-${padWithZeros(year, 4)}`;
+    } catch (error) {
+        return '';
+    }
+}
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/ageField/getAgeFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/ageField/getAgeFieldConfig.js
index 4c55b92527..e9ffbf41b6 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/ageField/getAgeFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/ageField/getAgeFieldConfig.js
@@ -2,6 +2,7 @@
 import { orientations } from '../../../../FormFields/New';
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { AgeFieldForForm } from '../../Components';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import { type DataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -15,6 +16,8 @@ export const getAgeFieldConfig = (metaData: DataElement, options: Object, queryS
         shrinkDisabled: options.formHorizontal,
         dateCalendarWidth: options.formHorizontal ? 250 : 350,
         datePopupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
index 355346d87d..4af9e783c5 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfig.js
@@ -3,6 +3,7 @@ import moment from 'moment';
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { DateFieldForForm } from '../../Components';
 import { convertDateObjectToDateFormatString } from '../../../../../../capture-core/utils/converters/date';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DateDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -17,6 +18,8 @@ export const getDateFieldConfig = (metaData: DateDataElement, options: Object, q
         calendarWidth: options.formHorizontal ? 250 : 350,
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
         calendarMax: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
index 442ad4a60c..fa09f77c53 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
@@ -4,7 +4,7 @@ import {
     convertFromIso8601,
 } from '@dhis2/multi-calendar-dates';
 import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
-import { padWithZeros } from './padWithZeros';
+import { padWithZeros } from '../../../../capture-core-utils/date';
 
 /**
  * Converts a date from ISO calendar to local calendar
diff --git a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
index 2c2ce3d192..2323f01370 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
@@ -4,7 +4,7 @@ import {
     convertToIso8601,
 } from '@dhis2/multi-calendar-dates';
 import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
-import { padWithZeros } from './padWithZeros';
+import { padWithZeros } from '../../../../capture-core-utils/date';
 
 /**
  * Converts a date from local calendar to ISO calendar
diff --git a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
index a50c92be11..57c0dec668 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
@@ -1,6 +1,6 @@
 // @flow
-import { Temporal } from '@js-temporal/polyfill';
 import { systemSettingsStore } from '../../../metaDataMemoryStores';
+import { stringToTemporal } from '../../../../capture-core-utils/date';
 
 /**
  * Converts a date string into a Temporal.PlainDate object using the system set calendar
@@ -19,27 +19,7 @@ export function convertStringToTemporal(dateString: ?string): PlainDate | null {
     if (!dateString) {
         return null;
     }
-    try {
-        const dateWithHyphen = dateString.replace(/[\/\.]/g, '-');
-
-        const calendar = systemSettingsStore.get().calendar;
-        const dateFormat = systemSettingsStore.get().dateFormat;
-
-        let year; let month; let day;
-
-        if (dateFormat === 'YYYY-MM-DD') {
-            [year, month, day] = dateWithHyphen.split('-').map(Number);
-        }
-        if (dateFormat === 'DD-MM-YYYY') {
-            [day, month, year] = dateWithHyphen.split('-').map(Number);
-        }
-        return Temporal.PlainDate.from({
-            year,
-            month,
-            day,
-            calendar,
-        });
-    } catch (error) {
-        return null;
-    }
+    const calendar = systemSettingsStore.get().calendar;
+    const dateFormat = systemSettingsStore.get().dateFormat;
+    return stringToTemporal(dateString, calendar, dateFormat);
 }
diff --git a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
index e413468c66..9ba772b23a 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
@@ -1,6 +1,6 @@
 // @flow
-import { padWithZeros } from './padWithZeros';
 import { systemSettingsStore } from '../../../../capture-core/metaDataMemoryStores';
+import { temporalToString } from '../../../../capture-core-utils/date';
 
 /**
  * Converts a Temporal.PlainDate to a formatted date string (YYYY-MM-DD || DD-MM-YYYY)
@@ -19,16 +19,5 @@ export function convertTemporalToString(temporalDate: PlainDate | null): string
         return '';
     }
     const dateFormat = systemSettingsStore.get().dateFormat;
-
-    try {
-        const year = temporalDate.year;
-        const month = temporalDate.month;
-        const day = temporalDate.day;
-
-        return dateFormat === 'YYYY-MM-DD' ?
-            `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}` :
-            `${padWithZeros(day, 2)}-${padWithZeros(month, 2)}-${padWithZeros(year, 4)}`;
-    } catch (error) {
-        return '';
-    }
+    return temporalToString(temporalDate, dateFormat);
 }
diff --git a/src/core_modules/capture-core/utils/converters/date/index.js b/src/core_modules/capture-core/utils/converters/date/index.js
index 11f200fb34..8f878a421d 100644
--- a/src/core_modules/capture-core/utils/converters/date/index.js
+++ b/src/core_modules/capture-core/utils/converters/date/index.js
@@ -3,7 +3,6 @@ export { parseDate } from './parser';
 export { convertDateObjectToDateFormatString } from './dateObjectToDateFormatString';
 export { convertMomentToDateFormatString } from './momentToDateFormatString';
 export { convertStringToDateFormat } from './stringToMomentDateFormat';
-export { padWithZeros } from './padWithZeros';
 export { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
 export { convertLocalToIsoCalendar } from './convertLocalToIsoCalendar';
 export { convertStringToTemporal } from './convertStringToTemporal';
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index 65085efe6b..f2b96b19a5 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -2,18 +2,16 @@
 import React, { Component } from 'react';
 import { Temporal } from '@js-temporal/polyfill';
 import { isValidPositiveInteger } from 'capture-core-utils/validators/form';
-import { systemSettingsStore } from 'capture-core/metaDataMemoryStores';
 import i18n from '@dhis2/d2-i18n';
 import classNames from 'classnames';
 import { IconButton } from 'capture-ui';
 import { IconCross24 } from '@dhis2/ui';
-import { parseDate } from 'capture-core/utils/converters/date';
 import { AgeNumberInput } from '../internal/AgeInput/AgeNumberInput.component';
 import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
 import { orientations } from '../constants/orientations.const';
 import { withInternalChangeHandler } from '../HOC/withInternalChangeHandler';
-import { convertStringToTemporal, convertTemporalToString } from '../../capture-core/utils/converters/date';
+import { stringToTemporal, temporalToString } from '../../capture-core-utils/date';
 
 type AgeValues = {
     date?: ?string,
@@ -53,31 +51,21 @@ type Props = {
     dateCalendarOnConvertValueOut: (value: string) => string,
     datePlaceholder?: ?string,
     disabled?: ?boolean,
+    dateFormat: ?string,
+    calendarType: ?string,
 };
 
-function getCalculatedValues(dateValue: ?string): AgeValues {
-    const parseData = dateValue && parseDate(dateValue);
-    if (!parseData || !parseData.isValid) {
-        return {
-            date: dateValue,
-            years: '',
-            months: '',
-            days: '',
-        };
-    }
-
-    const calendar = systemSettingsStore.get().calendar;
-
-    const now = Temporal.Now.plainDateISO().withCalendar(calendar);
+function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateFormat: ?string): AgeValues {
+    const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
 
-    const age = convertStringToTemporal(dateValue);
+    const age = stringToTemporal(dateValue, calendarType, dateFormat);
 
     const diff = now.since(age, {
         largestUnit: 'years',
         smallestUnit: 'days',
     });
 
-    const date = convertTemporalToString(age);
+    const date = temporalToString(age, dateFormat);
 
     return {
         date,
@@ -117,7 +105,7 @@ class D2AgeFieldPlain extends Component<Props> {
     }
 
     handleNumberBlur = (values: AgeValues) => {
-        const { onRemoveFocus } = this.props;
+        const { onRemoveFocus, calendarType = 'gregory', dateFormat = 'YYYY-MM-DD' } = this.props;
 
         onRemoveFocus && onRemoveFocus();
         if (D2AgeFieldPlain.isEmptyNumbers(values)) {
@@ -130,24 +118,37 @@ class D2AgeFieldPlain extends Component<Props> {
             return;
         }
 
-        const calendar = systemSettingsStore.get().calendar;
-
-        const now = Temporal.Now.plainDateISO().withCalendar(calendar);
+        const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
 
         const calculatedDate = now.subtract({
             years: D2AgeFieldPlain.getNumberOrZero(values.years),
             months: D2AgeFieldPlain.getNumberOrZero(values.months),
             days: D2AgeFieldPlain.getNumberOrZero(values.days),
         });
-
-        const calculatedValues = getCalculatedValues(convertTemporalToString(calculatedDate));
+        const dateString = temporalToString(calculatedDate, dateFormat);
+        const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
         this.props.onBlur(calculatedValues);
     }
 
     handleDateBlur = (date: ?string, options: ?ValidationOptions) => {
-        const { onRemoveFocus } = this.props;
+        const { onRemoveFocus, calendarType = 'gregory', dateFormat = 'YYYY-MM-DD' } = this.props;
         onRemoveFocus && onRemoveFocus();
-        const calculatedValues = date ? getCalculatedValues(date) : null;
+        const isDateValid = options && !options.error;
+        if (!date) {
+            this.props.onBlur(null, options);
+            return;
+        }
+        if (!isDateValid) {
+            const calculatedValues = {
+                date,
+                years: '',
+                months: '',
+                days: '',
+            };
+            this.props.onBlur(calculatedValues, options);
+            return;
+        }
+        const calculatedValues = getCalculatedValues(date, calendarType, dateFormat);
         this.props.onBlur(calculatedValues, options);
     }
 
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
index 1c4b2ee4b0..8fa5d01b6e 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
@@ -1,7 +1,6 @@
 // @flow
 import React from 'react';
 import { CalendarInput } from '@dhis2/ui';
-import { systemSettingsStore } from '../../../capture-core/metaDataMemoryStores';
 
 type ValidationOptions = {
     error?: ?string,
@@ -21,7 +20,9 @@ type Props = {
     placeholder?: string,
     label?: string,
     calendarMax?: any,
-    innerMessage?: any
+    innerMessage?: any,
+    dateFormat: ?string,
+    calendarType: ?string,
 };
 
 type Validation = {|
@@ -64,11 +65,13 @@ export class DateField extends React.Component<Props, State> {
             calendarMax,
             value,
             innerMessage,
+            calendarType,
+            dateFormat,
         } = this.props;
         const calculatedInputWidth = inputWidth || width;
         const calculatedCalendarWidth = calendarWidth || width;
-        const calendarType = systemSettingsStore.get().calendar || 'gregory';
-        const format = systemSettingsStore.get().dateFormat;
+        const calendar = calendarType || 'gregory';
+        const format = dateFormat || 'YYYY-MM-DD';
         const errorProps = innerMessage && innerMessage.messageType === 'error'
             ? { error: !!innerMessage.message?.dateInnerErrorMessage,
                 validationText: innerMessage.message?.dateInnerErrorMessage }
@@ -86,7 +89,7 @@ export class DateField extends React.Component<Props, State> {
                     placeholder={this.props.placeholder}
                     format={format}
                     onDateSelect={this.handleDateSelected}
-                    calendar={calendarType}
+                    calendar={calendar}
                     date={value}
                     width={String(calculatedCalendarWidth)}
                     inputWidth={String(calculatedInputWidth)}

From c8d1fbd06d059a4eac71255c0812b4988b85523c Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 13 Jan 2025 11:21:00 +0200
Subject: [PATCH 19/42] refactor: simplify converters logic

---
 .../capture-core-utils/date/stringToTemporal.js     |  4 ++--
 .../configs/dateTimeField/getDateTimeFieldConfig.js |  3 +++
 .../Enrollment/EnrollmentDataEntry.component.js     |  9 ++++++++-
 .../EditEventDataEntry.component.js                 |  3 +++
 .../capture-core/converters/formToClient.js         | 13 +++++--------
 .../converters/date/convertLocalToIsoCalendar.js    |  9 +--------
 .../converters/date/convertStringToTemporal.js      |  8 ++++----
 .../converters/date/convertTemporalToString.js      |  4 ++--
 8 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/src/core_modules/capture-core-utils/date/stringToTemporal.js b/src/core_modules/capture-core-utils/date/stringToTemporal.js
index 8063305d0f..3be34f0775 100644
--- a/src/core_modules/capture-core-utils/date/stringToTemporal.js
+++ b/src/core_modules/capture-core-utils/date/stringToTemporal.js
@@ -17,7 +17,7 @@ type PlainDate = {
 };
 
 export function stringToTemporal(dateString: ?string,
-    calendarType: ?string,
+    calendar: ?string,
     dateFormat: ?string): PlainDate | null {
     if (!dateString) {
         return null;
@@ -37,7 +37,7 @@ export function stringToTemporal(dateString: ?string,
             year,
             month,
             day,
-            calendarType,
+            calendar,
         });
     } catch (error) {
         return null;
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfig.js
index d7a24cb7f3..939efefb95 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfig.js
@@ -2,6 +2,7 @@
 import { orientations } from '../../../../FormFields/New';
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { DateTimeFieldForForm } from '../../Components';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DataElement as MetaDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -17,6 +18,8 @@ export const getDateTimeFieldConfig = (metaData: MetaDataElement, options: Objec
         shrinkDisabled: options.formHorizontal,
         calendarWidth: options.formHorizontal ? '250px' : '350px',
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
index 5ca70266c2..6f9a52fb42 100644
--- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
+++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js
@@ -43,6 +43,7 @@ import {
     withDataEntryFields,
 } from '../../DataEntryDhis2Helpers';
 import { convertDateObjectToDateFormatString } from '../../../../capture-core/utils/converters/date';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 
 const overrideMessagePropNames = {
     errorMessage: 'validationError',
@@ -112,7 +113,11 @@ const getEnrollmentDateSettings = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
-            calendarMax: !props.enrollmentMetadata.allowFutureEnrollmentDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+            calendarMax: !props.enrollmentMetadata.allowFutureEnrollmentDate ?
+                convertDateObjectToDateFormatString(moment()) :
+                undefined,
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => 'enrolledAt',
         getValidatorContainers: getEnrollmentDateValidatorContainer,
@@ -163,6 +168,8 @@ const getIncidentDateSettings = () => {
             calendarMax: !props.enrollmentMetadata.allowFutureIncidentDate ?
                 convertDateObjectToDateFormatString(moment()) :
                 undefined,
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => 'occurredAt',
         getPassOnFieldData: () => true,
diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js b/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
index e958a69644..96b1e4ff0a 100644
--- a/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
+++ b/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
@@ -47,6 +47,7 @@ import {
     withAOCFieldBuilder,
     withDataEntryFields,
 } from '../../DataEntryDhis2Helpers/';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 import type { UserFormField } from '../../FormFields/UserField';
 
 const tabMode = Object.freeze({
@@ -137,6 +138,8 @@ const buildReportDateSettingsFn = () => {
             calendarWidth: 350,
             label: props.formFoundation.getLabel('occurredAt'),
             required: true,
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => 'occurredAt',
         getValidatorContainers: () => getEventDateValidatorContainers(),
diff --git a/src/core_modules/capture-core/converters/formToClient.js b/src/core_modules/capture-core/converters/formToClient.js
index d0a4460e24..187549d140 100644
--- a/src/core_modules/capture-core/converters/formToClient.js
+++ b/src/core_modules/capture-core/converters/formToClient.js
@@ -3,7 +3,7 @@ import moment from 'moment';
 import isString from 'd2-utilizr/lib/isString';
 import { parseNumber, parseTime } from 'capture-core-utils/parsers';
 import { dataElementTypes } from '../metaData';
-import { parseDate, convertLocalToIsoCalendar } from '../utils/converters/date';
+import { convertLocalToIsoCalendar } from '../utils/converters/date';
 
 type DateTimeValue = {
     date: string,
@@ -25,21 +25,18 @@ function convertDateTime(formValue: DateTimeValue): ?string {
     const hours = momentTime.hour();
     const minutes = momentTime.minute();
 
-    const parsedDate = editedDate ? parseDate(editedDate) : null;
-    if (!(parsedDate && parsedDate.isValid && parsedDate.momentDate)) return null;
+    const isoDate = convertDate(editedDate);
+    if (!isoDate) return null;
 
-    const formattedDate = parsedDate.momentDate.format('YYYY-MM-DD');
-    const isoDate = convertLocalToIsoCalendar(formattedDate);
     const momentDateTime = moment(isoDate);
+    if (!momentDateTime.isValid()) return null;
     momentDateTime.hour(hours);
     momentDateTime.minute(minutes);
     return momentDateTime.toISOString();
 }
 
 function convertDate(dateValue: string) {
-    const parsedDate = parseDate(dateValue);
-    // $FlowFixMe[incompatible-use] automated comment
-    return parsedDate.isValid ? convertLocalToIsoCalendar(parsedDate.momentDate.toISOString()) : null;
+    return convertLocalToIsoCalendar(dateValue);
 }
 
 function convertTime(timeValue: string) {
diff --git a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
index 2323f01370..408116d011 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertLocalToIsoCalendar.js
@@ -17,16 +17,9 @@ export function convertLocalToIsoCalendar(localDate: ?string): string {
         return '';
     }
 
-    const momentDate = moment(localDate);
-    if (!momentDate.isValid()) {
-        return '';
-    }
-
-    const formattedIsoDate = momentDate.format('YYYY-MM-DD');
-
     const calendar = systemSettingsStore.get().calendar;
 
-    const { year, month, day } = convertToIso8601(formattedIsoDate, calendar);
+    const { year, month, day } = convertToIso8601(localDate, calendar);
     const dateString = `${padWithZeros(year, 4)}-${padWithZeros(month, 2)}-${padWithZeros(day, 2)}`;
     const parsedMoment = moment(dateString);
 
diff --git a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
index 57c0dec668..1c53443cc6 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertStringToTemporal.js
@@ -15,11 +15,11 @@ type PlainDate = {
     day: number
 };
 
-export function convertStringToTemporal(dateString: ?string): PlainDate | null {
+export function convertStringToTemporal(dateString: ?string, calendar: ?string, format: ?string): PlainDate | null {
     if (!dateString) {
         return null;
     }
-    const calendar = systemSettingsStore.get().calendar;
-    const dateFormat = systemSettingsStore.get().dateFormat;
-    return stringToTemporal(dateString, calendar, dateFormat);
+    const calendarType = calendar || systemSettingsStore.get().calendar;
+    const dateFormat = format || systemSettingsStore.get().dateFormat;
+    return stringToTemporal(dateString, calendarType, dateFormat);
 }
diff --git a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
index 9ba772b23a..0030ce0d68 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertTemporalToString.js
@@ -14,10 +14,10 @@ type PlainDate = {
     day: number
 };
 
-export function convertTemporalToString(temporalDate: PlainDate | null): string {
+export function convertTemporalToString(temporalDate: PlainDate | null, format: ?string): string {
     if (!temporalDate) {
         return '';
     }
-    const dateFormat = systemSettingsStore.get().dateFormat;
+    const dateFormat = format || systemSettingsStore.get().dateFormat;
     return temporalToString(temporalDate, dateFormat);
 }

From 98054358fe0936f29471ea47e7ce2aae444f246f Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 13 Jan 2025 11:52:52 +0200
Subject: [PATCH 20/42] fix: logic of dateRange & dateTimeRange validators

---
 .../capture-core-utils/parsers/date.parser.js | 88 -------------------
 .../capture-core-utils/parsers/index.js       |  1 -
 .../utils/converters/date/index.js            |  1 -
 .../utils/converters/date/parser.js           | 14 ---
 .../date/stringToMomentDateFormat.js          |  2 +-
 .../validators/form/getDateRangeValidator.js  |  8 +-
 .../form/getDateTimeRangeValidator.js         | 38 ++++----
 7 files changed, 25 insertions(+), 127 deletions(-)
 delete mode 100644 src/core_modules/capture-core-utils/parsers/date.parser.js
 delete mode 100644 src/core_modules/capture-core/utils/converters/date/parser.js

diff --git a/src/core_modules/capture-core-utils/parsers/date.parser.js b/src/core_modules/capture-core-utils/parsers/date.parser.js
deleted file mode 100644
index 98bbef55f0..0000000000
--- a/src/core_modules/capture-core-utils/parsers/date.parser.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// @flow
-import moment from 'moment';
-
-const getReturnObject = (momentDate: ?moment$Moment) => ({
-    momentDate,
-    isValid: momentDate ? momentDate.isValid() : false,
-});
-
-function manipulateFormatAndParseWithSeparator(dateString: string, inputFormat: string, separator: string) {
-    const dateSplitted = dateString.split(separator);
-    const formatSplitted = inputFormat.split(separator);
-    if (dateSplitted.length === formatSplitted.length) {
-        const newLocaleFormat = formatSplitted
-            .map((formatPart, index) => {
-                let newFormatPart = '';
-                const datePart = dateSplitted[index];
-                if (formatPart && datePart) {
-                    const partKey = formatPart.charAt(0);
-                    if (partKey === 'M') {
-                        if (datePart.length === 1) {
-                            newFormatPart = 'M';
-                        } else if (datePart.length === 2) {
-                            newFormatPart = 'MM';
-                        }
-                    } else if (partKey === 'D') {
-                        if (datePart.length === 1) {
-                            newFormatPart = 'D';
-                        } else if (datePart.length === 2) {
-                            newFormatPart = 'DD';
-                        }
-                    } else if (partKey === 'Y') {
-                        if (datePart.length === 2) {
-                            newFormatPart = 'YY';
-                        } else if (datePart.length === 4) {
-                            newFormatPart = 'YYYY';
-                        }
-                    }
-                }
-                return newFormatPart || formatPart;
-            })
-            .join(separator);
-        const momentDate = moment(dateString, newLocaleFormat, true);
-        return getReturnObject(momentDate);
-    }
-
-    return getReturnObject(null);
-}
-
-function parseWithSeparator(dateString: string, localeFormat: string, separatorPattern: RegExp) {
-    const specialCharactersInLocaleFormat = localeFormat.match(separatorPattern);
-    // $FlowFixMe[incompatible-type] automated comment
-    const separator: string = specialCharactersInLocaleFormat && specialCharactersInLocaleFormat[0];
-    const dateStringWithLocaleSeparator = dateString.replace(separatorPattern, separator);
-    const localeFormatSameSeparator = localeFormat.replace(separatorPattern, separator);
-
-    const momentDate = moment(dateStringWithLocaleSeparator, localeFormatSameSeparator, true);
-    if (momentDate.isValid()) {
-        return getReturnObject(momentDate);
-    }
-
-    const parseData = manipulateFormatAndParseWithSeparator(
-        dateStringWithLocaleSeparator,
-        localeFormatSameSeparator,
-        separator,
-    );
-    return parseData;
-}
-
-function parseWithoutSeparator(dateString: string, localeFormat: string, separatorPattern: RegExp) {
-    const dateStringWithoutSeparator = dateString.replace(separatorPattern, '');
-    const localeFormatWithoutSeparator = localeFormat.replace(separatorPattern, '');
-
-    const momentDate = moment(dateStringWithoutSeparator, localeFormatWithoutSeparator, true);
-    if (momentDate.isValid()) {
-        return getReturnObject(momentDate);
-    }
-
-    return getReturnObject(null);
-}
-
-export function parseDate(dateString: string, format: string) {
-    const separatorPattern = /[.,\-_/\\]/g;
-    if (separatorPattern.test(dateString) && separatorPattern.test(format)) {
-        return parseWithSeparator(dateString, format, separatorPattern);
-    }
-
-    return parseWithoutSeparator(dateString, format, separatorPattern);
-}
diff --git a/src/core_modules/capture-core-utils/parsers/index.js b/src/core_modules/capture-core-utils/parsers/index.js
index e28120d581..46502500df 100644
--- a/src/core_modules/capture-core-utils/parsers/index.js
+++ b/src/core_modules/capture-core-utils/parsers/index.js
@@ -1,4 +1,3 @@
 // @flow
-export { parseDate } from './date.parser';
 export { parseNumber } from './number.parser';
 export { parseTime } from './time.parser';
diff --git a/src/core_modules/capture-core/utils/converters/date/index.js b/src/core_modules/capture-core/utils/converters/date/index.js
index 8f878a421d..6c755de6c3 100644
--- a/src/core_modules/capture-core/utils/converters/date/index.js
+++ b/src/core_modules/capture-core/utils/converters/date/index.js
@@ -1,5 +1,4 @@
 // @flow
-export { parseDate } from './parser';
 export { convertDateObjectToDateFormatString } from './dateObjectToDateFormatString';
 export { convertMomentToDateFormatString } from './momentToDateFormatString';
 export { convertStringToDateFormat } from './stringToMomentDateFormat';
diff --git a/src/core_modules/capture-core/utils/converters/date/parser.js b/src/core_modules/capture-core/utils/converters/date/parser.js
deleted file mode 100644
index a487155ca0..0000000000
--- a/src/core_modules/capture-core/utils/converters/date/parser.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// @flow
-import { parseDate as parseDateCore } from 'capture-core-utils/parsers';
-import { systemSettingsStore } from '../../../metaDataMemoryStores';
-
-/**
- * Parse a string in date format
- * @export
- * @param {string} value - the string in date format
- * @returns {date}
- */
-export function parseDate(value: string) {
-    const format = systemSettingsStore.get().dateFormat;
-    return parseDateCore(value, format);
-}
diff --git a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
index 29c7b2c929..f75fb68255 100644
--- a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
+++ b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
@@ -9,7 +9,7 @@ import { systemSettingsStore } from '../../../metaDataMemoryStores';
  * @param {string} [format] - optional date format. If not provided, the function uses system date format
  * @returns {string}
  */
-export function convertStringToDateFormat(date: string, format?: string) {
+export function convertStringToDateFormat(date: ?string, format?: string) {
     if (!date || !date.length) { return ''; }
     const dateFormat = format || systemSettingsStore.get().dateFormat;
     const formattedDateString = moment(date, dateFormat).format(dateFormat);
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
index c76d738184..f54f241d7d 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
@@ -1,6 +1,7 @@
 // @flow
+import { Temporal } from '@js-temporal/polyfill';
 import { isValidDate } from './dateValidator';
-import { parseDate } from '../../../converters/date';
+import { convertStringToDateFormat } from '../../../converters/date';
 /**
  *
  * @export
@@ -30,6 +31,9 @@ export const getDateRangeValidator = (invalidDateMessage: string) =>
                 errorMessage: errorResult.reduce((map, error) => ({ ...map, ...error }), {}),
             };
         }
+        const { from, to } = value;
         // $FlowFixMe
-        return parseDate(value.from).momentDate <= parseDate(value.to).momentDate;
+        const formattedFrom = convertStringToDateFormat(from, 'YYYY-MM-DD');
+        const fromattedTo = convertStringToDateFormat(to, 'YYYY-MM-DD');
+        return Temporal.PlainDate.compare(formattedFrom, fromattedTo) <= 0;
     };
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
index 154749b666..f238469b0f 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
@@ -1,34 +1,32 @@
 // @flow
+import { Temporal } from '@js-temporal/polyfill';
 import { isValidDateTime } from './dateTimeValidator';
-import { parseDate } from '../../../converters/date';
 
 function isValidDateTimeWithEmptyCheck(value: ?Object) {
     return value && isValidDateTime(value);
 }
-const convertDateTimeToMoment = (value: Object) => {
-    const date = value.date;
-    const time = value.time;
+const convertDateTimeToTemporal = (value: Object) => {
+    const { date, time } = value;
+
+    const [year, month, day] = date.split('-').map(Number);
+
     let hour;
     let minutes;
     if (/[:.]/.test(time)) {
-        [hour, minutes] = time.split(/[:.]/);
+        [hour, minutes] = time.split(/[:.]/).map(Number);
     } else if (time.length === 3) {
-        hour = time.substring(0, 1);
-        minutes = time.substring(2, 3);
+        hour = Number(time.substring(0, 1));
+        minutes = Number(time.substring(2, 3));
     } else {
-        hour = time.substring(0, 2);
-        minutes = time.substring(3, 4);
+        hour = Number(time.substring(0, 2));
+        minutes = Number(time.substring(3, 4));
     }
-    const momentDateTime = parseDate(date).momentDate;
-    // $FlowFixMe[incompatible-use] automated comment
-    momentDateTime.hour(hour);
-    // $FlowFixMe[incompatible-use] automated comment
-    momentDateTime.minute(minutes);
-    return momentDateTime;
+
+    return new Temporal.PlainDateTime(year, month, day, hour, minutes);
 };
 
 export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
-    (value: { from?: ?Object, to?: ?Object}) => {
+    (value: { from?: ?Object, to?: ?Object }) => {
         const errorResult = [];
         if (!isValidDateTimeWithEmptyCheck(value.from)) {
             errorResult.push({ from: invalidDateTimeMessage });
@@ -46,8 +44,8 @@ export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
             };
         }
 
-        const fromDateTime = convertDateTimeToMoment(value.from);
-        const toDateTime = convertDateTimeToMoment(value.to);
-        // $FlowFixMe[invalid-compare] automated comment
-        return fromDateTime <= toDateTime;
+        const fromDateTime = convertDateTimeToTemporal(value.from);
+        const toDateTime = convertDateTimeToTemporal(value.to);
+
+        return Temporal.PlainDateTime.compare(fromDateTime, toDateTime) <= 0;
     };

From d67810bedf6a73c62ebe17735231f5be4c60366e Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 13 Jan 2025 12:54:15 +0200
Subject: [PATCH 21/42] chore: pass format and calendar as props

---
 .../dateField/getDateFieldConfigForCustomForm.js     |  3 +++
 .../dateRangeField/getDateRangeFieldConfig.js        |  3 +++
 .../getDateTimeFieldConfigForCustomForm.js           |  3 +++
 .../getDateTimeRangeFieldConfig.js                   |  3 +++
 .../EnrollmentWithFirstStageDataEntry.component.js   |  3 +++
 .../DataEntry/DataEntry.component.js                 |  3 +++
 .../DataEntry/DataEntry.component.js                 |  3 +++
 .../EditEventDataEntry.component.js                  |  2 ++
 .../ScheduleDate/ScheduleDate.component.js           |  3 +++
 .../FormComponents/DateFieldForRelatedStages.js      |  6 +++++-
 .../validators/form/getDateTimeRangeValidator.js     | 12 ++++++++++--
 11 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
index efeea1412c..9f5b057d0d 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateField/getDateFieldConfigForCustomForm.js
@@ -3,6 +3,7 @@ import moment from 'moment';
 import { createFieldConfig, createProps } from '../base/configBaseCustomForm';
 import { DateFieldForCustomForm } from '../../Components';
 import { convertDateObjectToDateFormatString } from '../../../../../../capture-core/utils/converters/date';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DateDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -12,6 +13,8 @@ export const getDateFieldConfigForCustomForm = (metaData: DateDataElement, optio
         maxWidth: 350,
         calendarWidth: 350,
         calendarMax: !metaData.allowFutureDate ? convertDateObjectToDateFormatString(moment()) : undefined,
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
index 283da64708..90fec23265 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
@@ -1,6 +1,7 @@
 // @flow
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { DateRangeFieldForForm } from '../../Components';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DataElement as MetaDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -14,6 +15,8 @@ export const getDateRangeFieldConfig = (metaData: MetaDataElement, options: Obje
         maxWidth: options.formHorizontal ? 150 : 350,
         calendarWidth: options.formHorizontal ? 250 : 350,
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfigForCustomForm.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfigForCustomForm.js
index 86024056fc..c061ea81e7 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfigForCustomForm.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeField/getDateTimeFieldConfigForCustomForm.js
@@ -2,6 +2,7 @@
 import { orientations } from '../../../../FormFields/New';
 import { createFieldConfig, createProps } from '../base/configBaseCustomForm';
 import { DateTimeFieldForCustomForm } from '../../Components';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DataElement as MetaDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -12,6 +13,8 @@ export const getDateTimeFieldConfigForCustomForm = (metaData: MetaDataElement, o
         calendarWidth: '350px',
         orientation: orientations.HORIZONTAL,
         shrinkDisabled: false,
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeRangeField/getDateTimeRangeFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeRangeField/getDateTimeRangeFieldConfig.js
index cc6e6c1a1a..b4919fd991 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeRangeField/getDateTimeRangeFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateTimeRangeField/getDateTimeRangeFieldConfig.js
@@ -2,6 +2,7 @@
 import { orientations } from '../../../../FormFields/New';
 import { createFieldConfig, createProps } from '../base/configBaseDefaultForm';
 import { DateTimeRangeFieldForForm } from '../../Components';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 import type { DataElement as MetaDataElement } from '../../../../../metaData';
 import type { QuerySingleResource } from '../../../../../utils/api/api.types';
 
@@ -17,6 +18,8 @@ export const getDateTimeRangeFieldConfig = (metaData: MetaDataElement, options:
         shrinkDisabled: options.formHorizontal,
         calendarWidth: options.formHorizontal ? 250 : 350,
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
+        calendarType: systemSettingsStore.get().calendar,
+        dateFormat: systemSettingsStore.get().dateFormat,
     }, options, metaData);
 
     return createFieldConfig({
diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js
index cff88329d0..e547c41eaf 100644
--- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js
+++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentWithFirstStageDataEntry/EnrollmentWithFirstStageDataEntry.component.js
@@ -25,6 +25,7 @@ import { withCleanUp } from './withCleanUp';
 import { getEventDateValidatorContainers } from './fieldValidators/eventDate.validatorContainersGetter';
 import { stageMainDataIds } from './getDataEntryPropsToInclude';
 import { withTransformPropName } from '../../../../HOC';
+import { systemSettingsStore } from '../../../../metaDataMemoryStores';
 
 const overrideMessagePropNames = {
     errorMessage: 'validationError',
@@ -219,6 +220,8 @@ const getReportDateSettingsFn = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => stageMainDataIds.OCCURRED_AT,
         getValidatorContainers: () => getEventDateValidatorContainers(),
diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js
index 3743fa5dc4..ac5fb4c65d 100644
--- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js
+++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js
@@ -52,6 +52,7 @@ import {
     attributeOptionsKey,
     getCategoryOptionsValidatorContainers, withAOCFieldBuilder, withDataEntryFields,
 } from '../../../../DataEntryDhis2Helpers';
+import { systemSettingsStore } from '../../../../../metaDataMemoryStores';
 
 const getStyles = theme => ({
     savingContextContainer: {
@@ -162,6 +163,8 @@ const buildReportDateSettingsFn = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => 'occurredAt',
         getValidatorContainers: () => getEventDateValidatorContainers(),
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js
index aa4520ca1c..3954899320 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js
@@ -42,6 +42,7 @@ import {
     attributeOptionsKey,
     getCategoryOptionsValidatorContainers,
 } from '../../DataEntryDhis2Helpers';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 
 const getStyles = theme => ({
     savingContextContainer: {
@@ -149,6 +150,8 @@ const buildReportDateSettingsFn = () => {
             required: true,
             calendarWidth: props.formHorizontal ? 250 : 350,
             popupAnchorPosition: getCalendarAnchorPosition(props.formHorizontal),
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getPropName: () => 'occurredAt',
         getValidatorContainers: () => getEventDateValidatorContainers(),
diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js b/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
index 96b1e4ff0a..5a112438b1 100644
--- a/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
+++ b/src/core_modules/capture-core/components/WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.component.js
@@ -187,6 +187,8 @@ const buildScheduleDateSettingsFn = () => {
             calendarWidth: 350,
             label: props.formFoundation.getLabel('scheduledAt'),
             disabled: true,
+            calendarType: systemSettingsStore.get().calendar,
+            dateFormat: systemSettingsStore.get().dateFormat,
         }),
         getIsHidden: (props: Object) => props.id !== dataEntryIds.ENROLLMENT_EVENT || props.hideDueDate,
         getPropName: () => 'scheduledAt',
diff --git a/src/core_modules/capture-core/components/WidgetEventSchedule/ScheduleDate/ScheduleDate.component.js b/src/core_modules/capture-core/components/WidgetEventSchedule/ScheduleDate/ScheduleDate.component.js
index ca3f806f2d..cd5d2ab5f1 100644
--- a/src/core_modules/capture-core/components/WidgetEventSchedule/ScheduleDate/ScheduleDate.component.js
+++ b/src/core_modules/capture-core/components/WidgetEventSchedule/ScheduleDate/ScheduleDate.component.js
@@ -4,6 +4,7 @@ import { spacersNum } from '@dhis2/ui';
 import withStyles from '@material-ui/core/styles/withStyles';
 import { DateField } from 'capture-core/components/FormFields/New';
 import { InfoBox } from '../InfoBox';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 import type { Props } from './scheduleDate.types';
 
 const styles = {
@@ -42,6 +43,8 @@ const ScheduleDatePlain = ({
                 }
                 setScheduleDate(e);
             }}
+            calendarType={systemSettingsStore.get().calendar}
+            dateFormat={systemSettingsStore.get().dateFormat}
         />
     </div>}
     <InfoBox
diff --git a/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/DateFieldForRelatedStages.js b/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/DateFieldForRelatedStages.js
index cbb55a1ef5..df5c37f2d3 100644
--- a/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/DateFieldForRelatedStages.js
+++ b/src/core_modules/capture-core/components/WidgetRelatedStages/FormComponents/DateFieldForRelatedStages.js
@@ -9,6 +9,7 @@ import {
 } from '../../FormFields/New';
 import labelTypeClasses from './dataEntryFieldLabels.module.css';
 import { baseInputStyles } from './commonProps';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 import type { ErrorMessagesForRelatedStages } from '../RelatedStagesActions';
 import type { RelatedStageDataValueStates } from '../WidgetRelatedStages.types';
 
@@ -46,7 +47,8 @@ export const DateFieldForRelatedStages = ({
         setTouched(true);
         onBlurDateField(event, internalComponentError);
     };
-
+    const calendarType = systemSettingsStore.get().calendar;
+    const dateFormat = systemSettingsStore.get().dateFormat;
     const shouldShowError = (touched || saveAttempted);
     return (
         <DateFieldForForm
@@ -60,6 +62,8 @@ export const DateFieldForRelatedStages = ({
             calendarWidth={350}
             onBlur={onBlur}
             errorMessage={shouldShowError && errorMessages?.scheduledAt}
+            calendarType={calendarType}
+            dateFormat={dateFormat}
         />
     );
 };
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
index f238469b0f..45b497a108 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
@@ -1,5 +1,6 @@
 // @flow
 import { Temporal } from '@js-temporal/polyfill';
+import { convertStringToTemporal } from 'capture-core/utils/converters/date';
 import { isValidDateTime } from './dateTimeValidator';
 
 function isValidDateTimeWithEmptyCheck(value: ?Object) {
@@ -8,7 +9,12 @@ function isValidDateTimeWithEmptyCheck(value: ?Object) {
 const convertDateTimeToTemporal = (value: Object) => {
     const { date, time } = value;
 
-    const [year, month, day] = date.split('-').map(Number);
+    const dateInTemporal = convertStringToTemporal(date);
+
+    if (!dateInTemporal) {
+        return null;
+    }
+    const { year, month, day } = dateInTemporal;
 
     let hour;
     let minutes;
@@ -46,6 +52,8 @@ export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
 
         const fromDateTime = convertDateTimeToTemporal(value.from);
         const toDateTime = convertDateTimeToTemporal(value.to);
-
+        if (!fromDateTime || !toDateTime) {
+            return false;
+        }
         return Temporal.PlainDateTime.compare(fromDateTime, toDateTime) <= 0;
     };

From 068c3ef7ef988110ed3aff212ae853209aff6c6e Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 16 Jan 2025 01:36:54 +0200
Subject: [PATCH 22/42] fix: status label in the StagesAndEvents widget display
 ISO date

---
 .../Stages/Stage/StageDetail/hooks/helpers.js                | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js
index 9d7f31f63f..eb5c2726b7 100644
--- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js
+++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js
@@ -3,13 +3,14 @@ import React from 'react';
 import moment from 'moment';
 import i18n from '@dhis2/d2-i18n';
 import { statusTypes, translatedStatusTypes } from 'capture-core/events/statusTypes';
-import { convertMomentToDateFormatString } from '../../../../../../utils/converters/date';
+import { convertClientToList } from '../../../../../../converters';
 import { getSubValues } from '../../getEventDataWithSubValue';
 import type { StageDataElement } from '../../../../types/common.types';
 import { Notes } from '../Notes.component';
 import type { QuerySingleResource } from '../../../../../../utils/api/api.types';
 import { isEventOverdue } from '../../../../../../utils/isEventOverdue';
 import { TooltipOrgUnit } from '../../../../../Tooltips/TooltipOrgUnit/TooltipOrgUnit.component';
+import { dataElementTypes } from '../../../../../../metaData';
 
 const getEventStatus = (event: ApiEnrollmentEvent) => {
     const today = moment().startOf('day');
@@ -37,7 +38,7 @@ const getEventStatus = (event: ApiEnrollmentEvent) => {
         if (daysUntilDueDate < 14) {
             return { status: statusTypes.SCHEDULE, options: dueDateFromNow };
         }
-        return { status: statusTypes.SCHEDULE, options: convertMomentToDateFormatString(dueDate) };
+        return { status: statusTypes.SCHEDULE, options: convertClientToList(dueDate, dataElementTypes.DATE) };
     }
     return { status: event.status, options: undefined };
 };

From cda3dab8b651e6dbf5d69d16e4b8fb60ce593be1 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 16 Jan 2025 02:05:34 +0200
Subject: [PATCH 23/42] fix: currentSearchTerms are not converted correctly

---
 src/core_modules/capture-core/converters/clientToList.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js
index f026191a4e..c64c30db9e 100644
--- a/src/core_modules/capture-core/converters/clientToList.js
+++ b/src/core_modules/capture-core/converters/clientToList.js
@@ -62,7 +62,7 @@ function convertImageForDisplay(clientValue: ImageClientValue) {
     return <PreviewImage url={clientValue.url} previewUrl={clientValue.previewUrl} />;
 }
 
-function convertRangeForDisplay(parser: any, clientValue: any) {
+function convertRangeForDisplay(parser: any = (value: any) => value, clientValue: any) {
     return (
         <span>
             {parser(clientValue.from)} {'->'} {parser(clientValue.to)}
@@ -106,9 +106,9 @@ const valueConvertersForType = {
     [dataElementTypes.INTEGER_NEGATIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
     [dataElementTypes.PERCENTAGE]: (value: number) => `${stringifyNumber(value)} %`,
     [dataElementTypes.DATE]: convertDateForListDisplay,
-    [dataElementTypes.DATE_RANGE]: value => convertRangeForDisplay(convertDateForListDisplay, value),
+    [dataElementTypes.DATE_RANGE]: value => convertRangeForDisplay(undefined, value),
     [dataElementTypes.DATETIME]: convertDateTimeForListDisplay,
-    [dataElementTypes.DATETIME_RANGE]: value => convertRangeForDisplay(convertDateTimeForListDisplay, value),
+    [dataElementTypes.DATETIME_RANGE]: value => convertRangeForDisplay(undefined, value),
     [dataElementTypes.TIME]: convertTimeForListDisplay,
     [dataElementTypes.TIME_RANGE]: value => convertRangeForDisplay(convertTimeForListDisplay, value),
     [dataElementTypes.TRUE_ONLY]: () => i18n.t('Yes'),

From 217dc2509ee7eb5814eff48ce6d46e7ede6b6fa1 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 16 Jan 2025 02:19:37 +0200
Subject: [PATCH 24/42] fix: enrollment and incident date display in iso

---
 .../components/WidgetEnrollment/Date/Date.component.js         | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
index 1b8dc31ee1..5d4f6e83b8 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollment/Date/Date.component.js
@@ -10,6 +10,7 @@ import {
 } from '@dhis2/ui';
 import i18n from '@dhis2/d2-i18n';
 import { withStyles } from '@material-ui/core';
+import { systemSettingsStore } from '../../../metaDataMemoryStores';
 import { convertValue as convertValueClientToView } from '../../../converters/clientToView';
 import { convertValue as convertValueFormToClient } from '../../../converters/formToClient';
 import { convertValue as convertValueClientToServer } from '../../../converters/clientToServer';
@@ -114,6 +115,8 @@ const DateComponentPlain = ({
                     label={dateLabel}
                     dense
                     locale={locale}
+                    calendarType={systemSettingsStore.get().calendar}
+                    dateFormat={systemSettingsStore.get().dateFormat}
                 />
                 <div className={classes.error}>
                     {validation && validation.error ? i18n.t('Please provide a valid date') : ''}

From d05011a73441b58cd3310a659d50fbdb9db9e7b8 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 16 Jan 2025 03:01:51 +0200
Subject: [PATCH 25/42] fix: runtime error when selecting a date in age field
 in ethiopian calendar

---
 .../date/temporalToString.js                  |  4 +++-
 .../capture-ui/AgeField/AgeField.component.js | 23 +++++++++++--------
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/core_modules/capture-core-utils/date/temporalToString.js b/src/core_modules/capture-core-utils/date/temporalToString.js
index 72f650b149..69caedbe7a 100644
--- a/src/core_modules/capture-core-utils/date/temporalToString.js
+++ b/src/core_modules/capture-core-utils/date/temporalToString.js
@@ -1,4 +1,5 @@
 // @flow
+import { systemSettingsStore } from 'capture-core/metaDataMemoryStores';
 import { padWithZeros } from './padWithZeros';
 
 /**
@@ -20,7 +21,8 @@ export function temporalToString(temporalDate: PlainDate | null, dateFormat: ?st
     }
 
     try {
-        const year = temporalDate.year;
+        const calendar = systemSettingsStore.get().calendar;
+        const year = calendar === 'ethiopian' ? temporalDate.eraYear : temporalDate.year;
         const month = temporalDate.month;
         const day = temporalDate.day;
 
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index f2b96b19a5..1684c97f8a 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -1,6 +1,10 @@
 // @flow
 import React, { Component } from 'react';
 import { Temporal } from '@js-temporal/polyfill';
+import {
+    convertFromIso8601,
+    convertToIso8601,
+} from '@dhis2/multi-calendar-dates';
 import { isValidPositiveInteger } from 'capture-core-utils/validators/form';
 import i18n from '@dhis2/d2-i18n';
 import classNames from 'classnames';
@@ -11,7 +15,7 @@ import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
 import { orientations } from '../constants/orientations.const';
 import { withInternalChangeHandler } from '../HOC/withInternalChangeHandler';
-import { stringToTemporal, temporalToString } from '../../capture-core-utils/date';
+import { temporalToString } from '../../capture-core-utils/date';
 
 type AgeValues = {
     date?: ?string,
@@ -56,19 +60,17 @@ type Props = {
 };
 
 function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateFormat: ?string): AgeValues {
-    const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+    const nowIso = Temporal.Now.plainDateISO();
 
-    const age = stringToTemporal(dateValue, calendarType, dateFormat);
+    const ageIso = convertToIso8601(dateValue, calendarType);
 
-    const diff = now.since(age, {
+    const diff = nowIso.since(ageIso, {
         largestUnit: 'years',
         smallestUnit: 'days',
     });
 
-    const date = temporalToString(age, dateFormat);
-
     return {
-        date,
+        date: dateValue,
         years: diff.years.toString(),
         months: diff.months.toString(),
         days: diff.days.toString(),
@@ -118,14 +120,15 @@ class D2AgeFieldPlain extends Component<Props> {
             return;
         }
 
-        const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+        const nowIso = Temporal.Now.plainDateISO();
 
-        const calculatedDate = now.subtract({
+        const calculatedDateIso = nowIso.subtract({
             years: D2AgeFieldPlain.getNumberOrZero(values.years),
             months: D2AgeFieldPlain.getNumberOrZero(values.months),
             days: D2AgeFieldPlain.getNumberOrZero(values.days),
         });
-        const dateString = temporalToString(calculatedDate, dateFormat);
+        const localCalculatedDate = convertFromIso8601(calculatedDateIso.toString(), calendarType);
+        const dateString = temporalToString(localCalculatedDate, dateFormat);
         const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
         this.props.onBlur(calculatedValues);
     }

From fbdfe2039c50961222b8a341c4d661062b2bf34e Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Tue, 21 Jan 2025 06:36:56 +0200
Subject: [PATCH 26/42] fix: use localDate in chip component

---
 .../components/WidgetEnrollment/WidgetEnrollment.component.js   | 2 +-
 .../components/WidgetNote/NoteSection/NoteSection.js            | 2 +-
 .../Stages/Stage/StageOverview/StageOverview.component.js       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
index 66b9321c2a..b5eae0842b 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js
@@ -158,7 +158,7 @@ export const WidgetEnrollmentPlain = ({
                                 <IconClock16 color={colors.grey600} />
                             </span>
                             {i18n.t('Last updated')}
-                            <Tooltip content={(fromServerDate(localDateTime).toLocaleString())}>
+                            <Tooltip content={(localDateTime)}>
                                 {moment(fromServerDate(enrollment.updatedAt)).fromNow()}
                             </Tooltip>
                         </div>
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index 825d27d527..0069b42f3e 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -110,7 +110,7 @@ const NoteSectionPlain = ({
                             {createdBy.firstName} {' '} {createdBy.surname}
                         </span>}
                         <span className={cx(classes.headerText, classes.lastUpdated)}>
-                            <Tooltip content={fromServerDate(localDateTime).toLocaleString()}>
+                            <Tooltip content={localDateTime}>
                                 {moment(fromServerDate(storedAt)).fromNow()}
                             </Tooltip>
                         </span>
diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
index 5c72a76670..a9e6a225fa 100644
--- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
+++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageOverview/StageOverview.component.js
@@ -78,7 +78,7 @@ const getLastUpdatedAt = (events, fromServerDate) => {
             ? (
                 <>
                     {i18n.t('Last updated')}&nbsp;
-                    <Tooltip content={fromServerDate(localDateTime).toLocaleString()}>
+                    <Tooltip content={localDateTime}>
                         {moment(fromServerDate(updatedAt)).fromNow()}
                     </Tooltip>
                 </>

From 4ceacde6c84b3b90a2dc8b58d5779329e4fbf4db Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 23 Jan 2025 11:22:03 +0200
Subject: [PATCH 27/42] fix: revert to iso date calculations when calendar
 isn't supported

---
 .../capture-core-utils/date/index.js          |  1 +
 .../date/isCalendarSupported.js               | 10 ++++
 .../date/stringToTemporal.js                  |  3 +-
 .../capture-ui/AgeField/AgeField.component.js | 57 ++++++++++++++-----
 4 files changed, 57 insertions(+), 14 deletions(-)
 create mode 100644 src/core_modules/capture-core-utils/date/isCalendarSupported.js

diff --git a/src/core_modules/capture-core-utils/date/index.js b/src/core_modules/capture-core-utils/date/index.js
index f29e3a8760..d21873de1f 100644
--- a/src/core_modules/capture-core-utils/date/index.js
+++ b/src/core_modules/capture-core-utils/date/index.js
@@ -3,3 +3,4 @@ export { getFormattedStringFromMomentUsingEuropeanGlyphs } from './date.utils';
 export { padWithZeros } from './padWithZeros';
 export { temporalToString } from './temporalToString';
 export { stringToTemporal } from './stringToTemporal';
+export { isCalendarSupported } from './isCalendarSupported';
diff --git a/src/core_modules/capture-core-utils/date/isCalendarSupported.js b/src/core_modules/capture-core-utils/date/isCalendarSupported.js
new file mode 100644
index 0000000000..691a09cadf
--- /dev/null
+++ b/src/core_modules/capture-core-utils/date/isCalendarSupported.js
@@ -0,0 +1,10 @@
+const supportedTemporalCalendars = ['coptic', 'gregory', 'islamic', 'persian', 'islamic-umalqura', 'islamic-tbla',
+    'islamic-civil', 'islamic-rgsa', 'hebrew', 'chinese', 'indian', 'buddhist', 'japanese', 'roc', 'dangi'];
+
+/**
+ * Checks if a calendar is supported by Temporal.
+ *
+ * @param {string} calendar - The calendar to check (e.g., 'islamic')
+ * @returns {boolean} - True if the calendar is supported, false otherwise.
+ */
+export const isCalendarSupported = calendar => supportedTemporalCalendars.includes(calendar);
diff --git a/src/core_modules/capture-core-utils/date/stringToTemporal.js b/src/core_modules/capture-core-utils/date/stringToTemporal.js
index 3be34f0775..38d62a12c2 100644
--- a/src/core_modules/capture-core-utils/date/stringToTemporal.js
+++ b/src/core_modules/capture-core-utils/date/stringToTemporal.js
@@ -13,7 +13,8 @@ import { Temporal } from '@js-temporal/polyfill';
 type PlainDate = {
     year: number,
     month: number,
-    day: number
+    day: number,
+    eraYear: number,
 };
 
 export function stringToTemporal(dateString: ?string,
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index 215e51ea87..245dbbc0fa 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -15,7 +15,7 @@ import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
 import { orientations } from '../constants/orientations.const';
 import { withInternalChangeHandler } from '../HOC/withInternalChangeHandler';
-import { temporalToString } from '../../capture-core-utils/date';
+import { stringToTemporal, temporalToString, isCalendarSupported } from '../../capture-core-utils/date';
 
 type AgeValues = {
     date?: ?string,
@@ -59,24 +59,42 @@ type Props = {
     calendarType: ?string,
 };
 
-function getCalculatedValues(dateValue: ?string, calendarType: ?string): AgeValues {
-    const nowIso = Temporal.Now.plainDateISO();
+function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateFormat: ?string): AgeValues {
+    if (!isCalendarSupported(calendarType)) {
+        const nowIso = Temporal.Now.plainDateISO();
+
+        const ageIso = convertToIso8601(dateValue, calendarType);
 
-    const ageIso = convertToIso8601(dateValue, calendarType);
+        const diff = nowIso.since(ageIso, {
+            largestUnit: 'years',
+            smallestUnit: 'days',
+        });
 
-    const diff = nowIso.since(ageIso, {
+        return {
+            date: dateValue,
+            years: diff.years.toString(),
+            months: diff.months.toString(),
+            days: diff.days.toString(),
+        };
+    }
+    const age = stringToTemporal(dateValue, calendarType, dateFormat);
+    const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+    const diff = now.since(age, {
         largestUnit: 'years',
         smallestUnit: 'days',
     });
 
+    const date = temporalToString(age, dateFormat);
+
     return {
-        date: dateValue,
+        date,
         years: diff.years.toString(),
         months: diff.months.toString(),
         days: diff.days.toString(),
     };
 }
 
+
 const messageTypeClass = {
     error: 'innerInputError',
     info: 'innerInputInfo',
@@ -120,21 +138,34 @@ class D2AgeFieldPlain extends Component<Props> {
             return;
         }
 
-        const nowIso = Temporal.Now.plainDateISO();
+        if (!isCalendarSupported(calendarType)) {
+            const nowIso = Temporal.Now.plainDateISO();
+            const calculatedDateIso = nowIso.subtract({
+                years: D2AgeFieldPlain.getNumberOrZero(values.years),
+                months: D2AgeFieldPlain.getNumberOrZero(values.months),
+                days: D2AgeFieldPlain.getNumberOrZero(values.days),
+            });
+
+            const localCalculatedDate = convertFromIso8601(calculatedDateIso.toString(), calendarType);
+            const dateString = temporalToString(localCalculatedDate, dateFormat);
+            const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
+            this.props.onBlur(calculatedValues);
+            return;
+        }
 
-        const calculatedDateIso = nowIso.subtract({
+        const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+        const calculatedDate = now.subtract({
             years: D2AgeFieldPlain.getNumberOrZero(values.years),
             months: D2AgeFieldPlain.getNumberOrZero(values.months),
             days: D2AgeFieldPlain.getNumberOrZero(values.days),
         });
-        const localCalculatedDate = convertFromIso8601(calculatedDateIso.toString(), calendarType);
-        const dateString = temporalToString(localCalculatedDate, dateFormat);
-        const calculatedValues = getCalculatedValues(dateString, calendarType);
+        const dateString = temporalToString(calculatedDate, dateFormat);
+        const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
         this.props.onBlur(calculatedValues);
     }
 
     handleDateBlur = (date: ?string, options: ?ValidationOptions) => {
-        const { onRemoveFocus, calendarType = 'gregory' } = this.props;
+        const { onRemoveFocus, calendarType = 'iso8601', dateFormat = 'YYYY-MM-DD' } = this.props;
         onRemoveFocus && onRemoveFocus();
         const isDateValid = options && !options.error;
         if (!date) {
@@ -151,7 +182,7 @@ class D2AgeFieldPlain extends Component<Props> {
             this.props.onBlur(calculatedValues, options);
             return;
         }
-        const calculatedValues = getCalculatedValues(date, calendarType);
+        const calculatedValues = getCalculatedValues(date, calendarType, dateFormat);
         this.props.onBlur(calculatedValues, options);
     }
 

From 79102e20e125d3d187f20951d7eba69d59ae8933 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Fri, 24 Jan 2025 11:37:24 +0200
Subject: [PATCH 28/42] fix: age calculations

---
 .../capture-core-utils/date/index.js          |  1 +
 .../date/isCalendarSupported.js               |  4 +-
 .../date/mapDhis2CalendarToTemporal.js        | 17 +++++
 .../date/stringToTemporal.js                  |  7 +-
 .../capture-ui/AgeField/AgeField.component.js | 65 +++++++++++++------
 5 files changed, 71 insertions(+), 23 deletions(-)
 create mode 100644 src/core_modules/capture-core-utils/date/mapDhis2CalendarToTemporal.js

diff --git a/src/core_modules/capture-core-utils/date/index.js b/src/core_modules/capture-core-utils/date/index.js
index d21873de1f..fdb9dd94e3 100644
--- a/src/core_modules/capture-core-utils/date/index.js
+++ b/src/core_modules/capture-core-utils/date/index.js
@@ -3,4 +3,5 @@ export { getFormattedStringFromMomentUsingEuropeanGlyphs } from './date.utils';
 export { padWithZeros } from './padWithZeros';
 export { temporalToString } from './temporalToString';
 export { stringToTemporal } from './stringToTemporal';
+export { mapDhis2CalendarToTemporal } from './mapDhis2CalendarToTemporal';
 export { isCalendarSupported } from './isCalendarSupported';
diff --git a/src/core_modules/capture-core-utils/date/isCalendarSupported.js b/src/core_modules/capture-core-utils/date/isCalendarSupported.js
index 691a09cadf..5c4c8ab19b 100644
--- a/src/core_modules/capture-core-utils/date/isCalendarSupported.js
+++ b/src/core_modules/capture-core-utils/date/isCalendarSupported.js
@@ -1,10 +1,10 @@
-const supportedTemporalCalendars = ['coptic', 'gregory', 'islamic', 'persian', 'islamic-umalqura', 'islamic-tbla',
+const supportedTemporalCalendars = ['coptic', 'gregory', 'ethiopic', 'islamic', 'persian', 'islamic-umalqura', 'islamic-tbla',
     'islamic-civil', 'islamic-rgsa', 'hebrew', 'chinese', 'indian', 'buddhist', 'japanese', 'roc', 'dangi'];
 
 /**
  * Checks if a calendar is supported by Temporal.
  *
- * @param {string} calendar - The calendar to check (e.g., 'islamic')
+ * @param {string} calendar - The calendar to check in temporal names (e.g., 'islamic')
  * @returns {boolean} - True if the calendar is supported, false otherwise.
  */
 export const isCalendarSupported = calendar => supportedTemporalCalendars.includes(calendar);
diff --git a/src/core_modules/capture-core-utils/date/mapDhis2CalendarToTemporal.js b/src/core_modules/capture-core-utils/date/mapDhis2CalendarToTemporal.js
new file mode 100644
index 0000000000..9986de88b9
--- /dev/null
+++ b/src/core_modules/capture-core-utils/date/mapDhis2CalendarToTemporal.js
@@ -0,0 +1,17 @@
+// @flow
+
+/**
+ * A mapping of DHIS2 calendar system names to Temporal API calendar names.
+ *
+ * @typedef {string} SupportedCalendar - Temporal-compatible calendar identifier
+ */
+export const mapDhis2CalendarToTemporal = {
+    ethiopian: 'ethiopic',
+    coptic: 'coptic',
+    gregorian: 'gregory',
+    islamic: 'islamic',
+    iso8601: 'iso8601',
+    nepali: 'nepali',
+    thai: 'buddhist',
+    persian: 'persian',
+};
diff --git a/src/core_modules/capture-core-utils/date/stringToTemporal.js b/src/core_modules/capture-core-utils/date/stringToTemporal.js
index 38d62a12c2..c3e74b5318 100644
--- a/src/core_modules/capture-core-utils/date/stringToTemporal.js
+++ b/src/core_modules/capture-core-utils/date/stringToTemporal.js
@@ -10,16 +10,19 @@ import { Temporal } from '@js-temporal/polyfill';
  * @returns {(Temporal.PlainDate | null)}
  */
 
-type PlainDate = {
+type PlainDateType = {
     year: number,
     month: number,
     day: number,
     eraYear: number,
+    with: (fields: { year?: number }) => PlainDateType,
+    since: (other: PlainDateType, options: { largestUnit: string, smallestUnit: string }) =>
+        { years: number, months: number, days: number }
 };
 
 export function stringToTemporal(dateString: ?string,
     calendar: ?string,
-    dateFormat: ?string): PlainDate | null {
+    dateFormat: ?string): PlainDateType | null {
     if (!dateString) {
         return null;
     }
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index 245dbbc0fa..076e40ae64 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -15,7 +15,7 @@ import { AgeDateInput } from '../internal/AgeInput/AgeDateInput.component';
 import defaultClasses from './ageField.module.css';
 import { orientations } from '../constants/orientations.const';
 import { withInternalChangeHandler } from '../HOC/withInternalChangeHandler';
-import { stringToTemporal, temporalToString, isCalendarSupported } from '../../capture-core-utils/date';
+import { stringToTemporal, temporalToString, mapDhis2CalendarToTemporal, isCalendarSupported } from '../../capture-core-utils/date';
 
 type AgeValues = {
     date?: ?string,
@@ -59,8 +59,8 @@ type Props = {
     calendarType: ?string,
 };
 
-function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateFormat: ?string): AgeValues {
-    if (!isCalendarSupported(calendarType)) {
+function getCalculatedValues(dateValue: ?string, calendarType: string, dateFormat: string): AgeValues {
+    if (!isCalendarSupported(mapDhis2CalendarToTemporal[calendarType])) {
         const nowIso = Temporal.Now.plainDateISO();
 
         const ageIso = convertToIso8601(dateValue, calendarType);
@@ -77,17 +77,41 @@ function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateForm
             days: diff.days.toString(),
         };
     }
-    const age = stringToTemporal(dateValue, calendarType, dateFormat);
-    const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+    const now = Temporal.Now.plainDateISO().withCalendar(mapDhis2CalendarToTemporal[calendarType]);
+    const age = stringToTemporal(dateValue, mapDhis2CalendarToTemporal[calendarType], dateFormat);
+
+    if (calendarType === 'ethiopian') {
+        if (!age || !age.eraYear) {
+            return {
+                date: dateValue,
+                years: '',
+                months: '',
+                days: '',
+            };
+        }
+        const ethiopianNow = now.with({ year: now.eraYear });
+        const ethiopianAge = age.with({ year: age.eraYear });
+
+        const diff = ethiopianNow.since(ethiopianAge, {
+            largestUnit: 'years',
+            smallestUnit: 'days',
+        });
+
+        return {
+            date: temporalToString(ethiopianAge, dateFormat),
+            years: diff.years.toString(),
+            months: diff.months.toString(),
+            days: diff.days.toString(),
+        };
+    }
+
     const diff = now.since(age, {
         largestUnit: 'years',
         smallestUnit: 'days',
     });
 
-    const date = temporalToString(age, dateFormat);
-
     return {
-        date,
+        date: temporalToString(age, dateFormat),
         years: diff.years.toString(),
         months: diff.months.toString(),
         days: diff.days.toString(),
@@ -125,8 +149,9 @@ class D2AgeFieldPlain extends Component<Props> {
     }
 
     handleNumberBlur = (values: AgeValues) => {
-        const { onRemoveFocus, calendarType = 'gregory', dateFormat = 'YYYY-MM-DD' } = this.props;
-
+        const { onRemoveFocus, calendarType, dateFormat } = this.props;
+        const calendar = calendarType || 'iso8601';
+        const format = dateFormat || 'YYYY-MM-DD';
         onRemoveFocus && onRemoveFocus();
         if (D2AgeFieldPlain.isEmptyNumbers(values)) {
             this.props.onBlur(values.date ? { date: values.date } : null);
@@ -138,7 +163,7 @@ class D2AgeFieldPlain extends Component<Props> {
             return;
         }
 
-        if (!isCalendarSupported(calendarType)) {
+        if (!isCalendarSupported(mapDhis2CalendarToTemporal[calendar])) {
             const nowIso = Temporal.Now.plainDateISO();
             const calculatedDateIso = nowIso.subtract({
                 years: D2AgeFieldPlain.getNumberOrZero(values.years),
@@ -146,26 +171,28 @@ class D2AgeFieldPlain extends Component<Props> {
                 days: D2AgeFieldPlain.getNumberOrZero(values.days),
             });
 
-            const localCalculatedDate = convertFromIso8601(calculatedDateIso.toString(), calendarType);
-            const dateString = temporalToString(localCalculatedDate, dateFormat);
-            const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
+            const localCalculatedDate = convertFromIso8601(calculatedDateIso.toString(), calendar);
+            const dateString = temporalToString(localCalculatedDate, format);
+            const calculatedValues = getCalculatedValues(dateString, calendar, format);
             this.props.onBlur(calculatedValues);
             return;
         }
 
-        const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
+        const now = Temporal.Now.plainDateISO().withCalendar(mapDhis2CalendarToTemporal[calendar]);
         const calculatedDate = now.subtract({
             years: D2AgeFieldPlain.getNumberOrZero(values.years),
             months: D2AgeFieldPlain.getNumberOrZero(values.months),
             days: D2AgeFieldPlain.getNumberOrZero(values.days),
         });
-        const dateString = temporalToString(calculatedDate, dateFormat);
-        const calculatedValues = getCalculatedValues(dateString, calendarType, dateFormat);
+        const dateString = temporalToString(calculatedDate, format);
+        const calculatedValues = getCalculatedValues(dateString, calendar, format);
         this.props.onBlur(calculatedValues);
     }
 
     handleDateBlur = (date: ?string, options: ?ValidationOptions) => {
-        const { onRemoveFocus, calendarType = 'iso8601', dateFormat = 'YYYY-MM-DD' } = this.props;
+        const { onRemoveFocus, calendarType, dateFormat } = this.props;
+        const calendar = calendarType || 'iso8601';
+        const format = dateFormat || 'YYYY-MM-DD';
         onRemoveFocus && onRemoveFocus();
         const isDateValid = options && !options.error;
         if (!date) {
@@ -182,7 +209,7 @@ class D2AgeFieldPlain extends Component<Props> {
             this.props.onBlur(calculatedValues, options);
             return;
         }
-        const calculatedValues = getCalculatedValues(date, calendarType, dateFormat);
+        const calculatedValues = getCalculatedValues(date, calendar, format);
         this.props.onBlur(calculatedValues, options);
     }
 

From 3493da4c8a29332163faf8eaa87ae214f25142d1 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Tue, 28 Jan 2025 03:25:04 +0200
Subject: [PATCH 29/42] fix: age and dateTimeRange searchterms

---
 .../SearchForm/SearchForm.container.js        | 19 +++++-
 .../form/getDateTimeRangeValidator.js         | 62 ++++++++++++++-----
 2 files changed, 64 insertions(+), 17 deletions(-)

diff --git a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
index 7628f46705..5a40fadd62 100644
--- a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
+++ b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
@@ -2,6 +2,7 @@
 import { connect } from 'react-redux';
 import type { ComponentType } from 'react';
 import { isObject, isString } from 'd2-utilizr/src';
+import { convertFormToClient } from 'capture-core/converters';
 import { SearchFormComponent } from './SearchForm.component';
 import type { CurrentSearchTerms, DispatchersFromRedux, OwnProps, Props, PropsFromRedux } from './SearchForm.types';
 import {
@@ -14,6 +15,7 @@ import {
 } from '../SearchBox.actions';
 import { addFormData, removeFormData } from '../../D2Form/actions/form.actions';
 
+// eslint-disable-next-line complexity
 const isValueContainingCharacter = (value: any) => {
     if (!value) {
         return false;
@@ -22,6 +24,20 @@ const isValueContainingCharacter = (value: any) => {
         return Boolean(value.replace(/\s/g, '').length);
     }
 
+    if (isObject(value) && ('from' in value && 'to' in value)) {
+        const fromValid = value.from &&
+            isObject(value.from) &&
+            value.from.time &&
+            value.from.date;
+
+        const toValid = value.to &&
+            isObject(value.to) &&
+            value.to.time &&
+            value.to.date;
+
+        return fromValid && toValid;
+    }
+
     if (isObject(value)) {
         const numberOfValuesWithLength = Object.values(value)
             .filter(v => isString(v))
@@ -48,7 +64,8 @@ const collectCurrentSearchTerms = (searchGroupsForSelectedScope, formsValues): C
             const { name, id, type } = attributeSearchForm.getElement(attributeValueKey);
             const value = searchTerms[attributeValueKey];
             if (isValueContainingCharacter(value)) {
-                return [...accumulated, { name, value, id, type }];
+                const convertedValue = convertFormToClient(value, type);
+                return [...accumulated, { name, value: convertedValue, id, type }];
             }
             return accumulated;
         }, []);
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
index 45b497a108..a92c7af0c4 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
@@ -6,33 +6,54 @@ import { isValidDateTime } from './dateTimeValidator';
 function isValidDateTimeWithEmptyCheck(value: ?Object) {
     return value && isValidDateTime(value);
 }
-const convertDateTimeToTemporal = (value: Object) => {
+/* eslint-disable complexity */
+const convertDateTimeToTemporal = (value: ?Object) => {
+    if (!value || !value.date || !value.time) {
+        return null;
+    }
+
     const { date, time } = value;
 
-    const dateInTemporal = convertStringToTemporal(date);
+    const dateInTemporalFormat = convertStringToTemporal(date);
 
-    if (!dateInTemporal) {
+    if (!dateInTemporalFormat) {
         return null;
     }
-    const { year, month, day } = dateInTemporal;
+    const { year, month, day } = dateInTemporalFormat;
 
     let hour;
     let minutes;
-    if (/[:.]/.test(time)) {
-        [hour, minutes] = time.split(/[:.]/).map(Number);
-    } else if (time.length === 3) {
-        hour = Number(time.substring(0, 1));
-        minutes = Number(time.substring(2, 3));
-    } else {
-        hour = Number(time.substring(0, 2));
-        minutes = Number(time.substring(3, 4));
-    }
+    try {
+        if (/[:.]/.test(time)) {
+            [hour, minutes] = time.split(/[:.]/).map(Number);
+        } else if (time.length === 3) {
+            hour = Number(time.substring(0, 1));
+            minutes = Number(time.substring(1, 3));
+        } else if (time.length === 4) {
+            hour = Number(time.substring(0, 2));
+            minutes = Number(time.substring(2, 4));
+        } else {
+            return null;
+        }
+
+        if (isNaN(hour) || isNaN(minutes) || hour < 0 || hour > 23 || minutes < 0 || minutes > 59) {
+            return null;
+        }
 
-    return new Temporal.PlainDateTime(year, month, day, hour, minutes);
+        return new Temporal.PlainDateTime(year, month, day, hour, minutes);
+    } catch (error) {
+        return null;
+    }
 };
 
 export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
     (value: { from?: ?Object, to?: ?Object }) => {
+        if (!value) {
+            return {
+                valid: false,
+                errorMessage: { from: invalidDateTimeMessage, to: invalidDateTimeMessage },
+            };
+        }
         const errorResult = [];
         if (!isValidDateTimeWithEmptyCheck(value.from)) {
             errorResult.push({ from: invalidDateTimeMessage });
@@ -53,7 +74,16 @@ export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
         const fromDateTime = convertDateTimeToTemporal(value.from);
         const toDateTime = convertDateTimeToTemporal(value.to);
         if (!fromDateTime || !toDateTime) {
-            return false;
+            return {
+                valid: false,
+                errorMessage: {
+                    from: !fromDateTime ? invalidDateTimeMessage : undefined,
+                    to: !toDateTime ? invalidDateTimeMessage : undefined,
+                },
+            };
         }
-        return Temporal.PlainDateTime.compare(fromDateTime, toDateTime) <= 0;
+        return {
+            valid: Temporal.PlainDateTime.compare(fromDateTime, toDateTime) <= 0,
+            errorMessage: undefined,
+        };
     };

From 79f33683bb9292e9929fd348c6be020749541974 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Tue, 28 Jan 2025 03:27:05 +0200
Subject: [PATCH 30/42] fix: invalidDate in event notes

---
 .../components/WidgetEventNote/WidgetEventNote.epics.js       | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
index 8aae3f0f24..315c0dba5b 100644
--- a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
@@ -5,8 +5,6 @@ import { featureAvailable, FEATURES } from 'capture-core-utils';
 import { map, switchMap } from 'rxjs/operators';
 import uuid from 'd2-utilizr/lib/uuid';
 import moment from 'moment';
-import { convertValue as convertListValue } from '../../converters/clientToList';
-import { dataElementTypes } from '../../metaData';
 import { actionTypes, batchActionTypes, startAddNoteForEvent } from './WidgetEventNote.actions';
 
 import {
@@ -58,7 +56,7 @@ export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore,
                 };
                 const formNote = {
                     ...clientNote,
-                    storedAt: convertListValue(clientNote.storedAt, dataElementTypes.DATETIME),
+                    storedAt: clientNote.storedAt,
                     createdBy: {
                         firstName,
                         surname,

From 8aee1e6265bb0db0cbba5a57f6554a74e96a7b54 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 29 Jan 2025 20:43:33 +0200
Subject: [PATCH 31/42] fix: dateTime search query

---
 .../SearchForm/SearchForm.container.js        | 29 ++++++++++---------
 .../SearchFormElementConverter.js             |  3 +-
 .../capture-core/converters/clientToList.js   |  4 +--
 3 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
index 5a40fadd62..f2fc157af6 100644
--- a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
+++ b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchForm.container.js
@@ -15,7 +15,6 @@ import {
 } from '../SearchBox.actions';
 import { addFormData, removeFormData } from '../../D2Form/actions/form.actions';
 
-// eslint-disable-next-line complexity
 const isValueContainingCharacter = (value: any) => {
     if (!value) {
         return false;
@@ -24,21 +23,25 @@ const isValueContainingCharacter = (value: any) => {
         return Boolean(value.replace(/\s/g, '').length);
     }
 
-    if (isObject(value) && ('from' in value && 'to' in value)) {
-        const fromValid = value.from &&
-            isObject(value.from) &&
-            value.from.time &&
-            value.from.date;
+    if (isObject(value)) {
+        if ('from' in value && 'to' in value) {
+            const fromValues = isObject(value.from) ?
+                [value.from.time, value.from.date] :
+                [value.from];
 
-        const toValid = value.to &&
-            isObject(value.to) &&
-            value.to.time &&
-            value.to.date;
+            const toValues = isObject(value.to) ?
+                [value.to.time, value.to.date] :
+                [value.to];
 
-        return fromValid && toValid;
-    }
+            const allValues = [...fromValues, ...toValues];
+            const validValues = allValues
+                .filter(v => isString(v))
+                .filter(v => Boolean(v?.replace(/\s/g, '').length))
+                .length;
+
+            return validValues === allValues.length;
+        }
 
-    if (isObject(value)) {
         const numberOfValuesWithLength = Object.values(value)
             .filter(v => isString(v))
             .filter((v: any) => Boolean(v.replace(/\s/g, '').length))
diff --git a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchFormElementConverter/SearchFormElementConverter.js b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchFormElementConverter/SearchFormElementConverter.js
index 5153fc5454..d887acf182 100644
--- a/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchFormElementConverter/SearchFormElementConverter.js
+++ b/src/core_modules/capture-core/components/SearchBox/SearchForm/SearchFormElementConverter/SearchFormElementConverter.js
@@ -22,7 +22,8 @@ const convertRange = (formValues: FormValues, dataElement: DataElement) => {
     const convertedFrom = from && (dataElement.convertValue(from, pipeD2(convertFormToClient, convertClientToServer)));
     const convertedTo = to && (dataElement.convertValue(to, pipeD2(convertFormToClient, convertClientToServer)));
     if (from || to) {
-        return `${dataElement.id}${convertedFrom ? (`:ge:${convertedFrom}`) : ''}${convertedTo ? (`:le:${convertedTo}`) : ''}`;
+        return `${dataElement.id}${convertedFrom ? `:ge:${escapeString(String(convertedFrom))}`
+            : ''}${convertedTo ? `:le:${escapeString(String(convertedTo))}` : ''}`;
     }
     return null;
 };
diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js
index d148d1f871..18359666b7 100644
--- a/src/core_modules/capture-core/converters/clientToList.js
+++ b/src/core_modules/capture-core/converters/clientToList.js
@@ -103,9 +103,9 @@ const valueConvertersForType = {
     [dataElementTypes.BOOLEAN]: (rawValue: boolean) => (rawValue ? i18n.t('Yes') : i18n.t('No')),
     [dataElementTypes.COORDINATE]: MinimalCoordinates,
     [dataElementTypes.DATE]: convertDateForListDisplay,
-    [dataElementTypes.DATE_RANGE]: value => convertRangeForDisplay(undefined, value),
+    [dataElementTypes.DATE_RANGE]: value => convertRangeForDisplay(convertIsoToLocalCalendar, value),
     [dataElementTypes.DATETIME]: convertDateTimeForListDisplay,
-    [dataElementTypes.DATETIME_RANGE]: value => convertRangeForDisplay(undefined, value),
+    [dataElementTypes.DATETIME_RANGE]: value => convertRangeForDisplay(convertIsoToLocalCalendar, value),
     [dataElementTypes.FILE_RESOURCE]: convertFileForDisplay,
     [dataElementTypes.IMAGE]: convertImageForDisplay,
     [dataElementTypes.INTEGER]: stringifyNumber,

From ea0aca4079f7863338c5d2d0271428f05b43c8b9 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 29 Jan 2025 20:48:49 +0200
Subject: [PATCH 32/42] fix: dateRange validator

---
 .../dateRangeField/getDateRangeFieldConfig.js |  6 +-
 .../utils/validation/validateValue.js         |  2 +-
 .../validators/form/getDateRangeValidator.js  | 21 ++++---
 .../DateRangeField.component.js               | 57 ++++++++++++++-----
 4 files changed, 58 insertions(+), 28 deletions(-)

diff --git a/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js b/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
index 90fec23265..9f2aa22cc6 100644
--- a/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
+++ b/src/core_modules/capture-core/components/D2Form/field/configs/dateRangeField/getDateRangeFieldConfig.js
@@ -11,9 +11,9 @@ export const getDateRangeFieldConfig = (metaData: MetaDataElement, options: Obje
     const props = createProps({
         formHorizontal: options.formHorizontal,
         fieldLabelMediaBasedClass: options.fieldLabelMediaBasedClass,
-        width: options.formHorizontal ? 150 : '100%',
-        maxWidth: options.formHorizontal ? 150 : 350,
-        calendarWidth: options.formHorizontal ? 250 : 350,
+        dateWidth: options.formHorizontal ? '150px' : '100%',
+        dateMaxWidth: options.formHorizontal ? '150px' : '350px',
+        calendarWidth: options.formHorizontal ? '250px' : '350px',
         popupAnchorPosition: getCalendarAnchorPosition(options.formHorizontal),
         calendarType: systemSettingsStore.get().calendar,
         dateFormat: systemSettingsStore.get().dateFormat,
diff --git a/src/core_modules/capture-core/utils/validation/validateValue.js b/src/core_modules/capture-core/utils/validation/validateValue.js
index c91bf08fda..50f8c466ec 100644
--- a/src/core_modules/capture-core/utils/validation/validateValue.js
+++ b/src/core_modules/capture-core/utils/validation/validateValue.js
@@ -33,7 +33,7 @@ export const validateValue = async ({
         if (pass === true) {
             let result = currentValidator.validator(
                 value,
-                { error: commitOptions?.error, errorCode: commitOptions?.errorCode },
+                commitOptions,
                 validationContext,
             );
             if (result instanceof Promise) {
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
index f54f241d7d..36f6618d0c 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
@@ -1,7 +1,7 @@
 // @flow
 import { Temporal } from '@js-temporal/polyfill';
 import { isValidDate } from './dateValidator';
-import { convertStringToDateFormat } from '../../../converters/date';
+import { convertStringToTemporal } from '../../../converters/date';
 /**
  *
  * @export
@@ -9,18 +9,18 @@ import { convertStringToDateFormat } from '../../../converters/date';
  * @returns {boolean}
  */
 
-function isValidDateWithEmptyCheck(value: ?string) {
-    return value && isValidDate(value);
+function isValidDateWithEmptyCheck(value: ?string, internalError?: ?{error: ?string, errorCode: ?string}) {
+    return isValidDate(value, internalError);
 }
 
 export const getDateRangeValidator = (invalidDateMessage: string) =>
-    (value: { from?: ?string, to?: ?string}) => {
+    (value: { from?: ?string, to?: ?string}, internalComponentError?: ?{fromError: ?{error: ?string, errorCode: ?string}, toError: ?{error: ?string, errorCode: ?string}}) => {
         const errorResult = [];
-        if (!isValidDateWithEmptyCheck(value.from)) {
+        if (!isValidDateWithEmptyCheck(value.from, internalComponentError?.fromError).valid) {
             errorResult.push({ from: invalidDateMessage });
         }
 
-        if (!isValidDateWithEmptyCheck(value.to)) {
+        if (!isValidDateWithEmptyCheck(value.to, internalComponentError?.toError).valid) {
             errorResult.push({ to: invalidDateMessage });
         }
 
@@ -33,7 +33,10 @@ export const getDateRangeValidator = (invalidDateMessage: string) =>
         }
         const { from, to } = value;
         // $FlowFixMe
-        const formattedFrom = convertStringToDateFormat(from, 'YYYY-MM-DD');
-        const fromattedTo = convertStringToDateFormat(to, 'YYYY-MM-DD');
-        return Temporal.PlainDate.compare(formattedFrom, fromattedTo) <= 0;
+        const formattedFrom = convertStringToTemporal(from);
+        const fromattedTo = convertStringToTemporal(to);
+        return {
+            valid: Temporal.PlainDateTime.compare(formattedFrom, fromattedTo) <= 0,
+            errorMessage: undefined,
+        };
     };
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
index c069700c64..884a9eb8c2 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
@@ -15,6 +15,11 @@ type DateRangeValue = {
     to?: ?string,
 }
 
+type State = {
+    fromError: { error: ?string, errorCode: ?string },
+    toError: { error: ?string, errorCode: ?string },
+};
+
 type Props = {
     value: DateRangeValue,
     onBlur: (value: ?DateRangeValue, opts: any) => void,
@@ -29,11 +34,15 @@ const inputKeys = {
 };
 
 
-export class DateRangeField extends React.Component<Props> {
+export class DateRangeField extends React.Component<Props, State> {
     touchedFields: Set<string>;
     constructor(props: Props) {
         super(props);
         this.touchedFields = new Set();
+        this.state = {
+            fromError: { error: null, errorCode: null },
+            toError: { error: null, errorCode: null },
+        };
     }
 
     getValue = () => this.props.value || {};
@@ -52,25 +61,41 @@ export class DateRangeField extends React.Component<Props> {
         });
     }
 
-    handleFromBlur = (value: string) => {
+    handleFromBlur = (value: string, options: ?Object) => {
         this.touchedFields.add('fromTouched');
-        const currentValue = this.getValue();
-        this.handleBlur({
-            from: value,
-            to: currentValue.to,
-        }, !!currentValue.to);
+        this.setState(() => ({
+            fromError: { error: options?.error, errorCode: options?.errorCode },
+        }), () => {
+            const currentValue = this.getValue();
+            this.handleBlur({
+                from: value,
+                to: currentValue.to,
+            }, {
+                touched: !!currentValue.to,
+                fromError: this.state.fromError,
+                toError: this.state.toError,
+            });
+        });
     }
 
-    handleToBlur = (value: string) => {
+    handleToBlur = (value: string, options: ?Object) => {
         this.touchedFields.add('toTouched');
-        const currentValue = this.getValue();
-        this.handleBlur({
-            from: currentValue.from,
-            to: value,
-        }, !!currentValue.from);
+        this.setState(() => ({
+            toError: { error: options?.error, errorCode: options?.errorCode },
+        }), () => {
+            const currentValue = this.getValue();
+            this.handleBlur({
+                from: currentValue.from,
+                to: value,
+            }, {
+                touched: !!currentValue.to,
+                fromError: this.state.fromError,
+                toError: this.state.toError,
+            });
+        });
     }
 
-    handleBlur = (value: DateRangeValue, otherFieldHasValue: boolean) => {
+    handleBlur = (value: DateRangeValue, options: ?Object) => {
         const touched = this.touchedFields.size === 2;
         if (!value.from && !value.to) {
             this.props.onBlur(undefined, {
@@ -79,7 +104,9 @@ export class DateRangeField extends React.Component<Props> {
             return;
         }
         this.props.onBlur(value, {
-            touched: touched || otherFieldHasValue,
+            touched: touched || options?.touched,
+            fromError: options?.fromError,
+            toError: options?.toError,
         });
     }
 

From 47ebd75111ea69225619ccc89301e565d7822b71 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Fri, 31 Jan 2025 10:30:26 +0200
Subject: [PATCH 33/42] chore: remove convertStringToDateFormat function

---
 .../Date/DateFilter.component.js                |  6 +++---
 .../EnrollmentEditEventPage.container.js        |  3 ++-
 .../hooks/useDetermineSuggestedScheduleDate.js  |  6 ++----
 .../capture-core/utils/converters/date/index.js |  1 -
 .../converters/date/stringToMomentDateFormat.js | 17 -----------------
 5 files changed, 7 insertions(+), 26 deletions(-)
 delete mode 100644 src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js

diff --git a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
index 1e03d9167e..605ca6b6cc 100644
--- a/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
+++ b/src/core_modules/capture-core/components/FiltersForTypes/Date/DateFilter.component.js
@@ -17,7 +17,7 @@ import './calendarFilterStyles.css';
 import { mainOptionKeys, mainOptionTranslatedTexts } from './options';
 import { getDateFilterData } from './dateFilterDataGetter';
 import { RangeFilter } from './RangeFilter.component';
-import { convertStringToDateFormat } from '../../../utils/converters/date';
+import { convertStringToTemporal } from '../../../utils/converters/date';
 
 const getStyles = (theme: Theme) => ({
     fromToContainer: {
@@ -190,8 +190,8 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
     }
 
     static isFromAfterTo(valueFrom: string, valueTo: string) {
-        const formattedFrom = convertStringToDateFormat(valueFrom, 'YYYY-MM-DD');
-        const fromattedTo = convertStringToDateFormat(valueTo, 'YYYY-MM-DD');
+        const formattedFrom = convertStringToTemporal(valueFrom);
+        const fromattedTo = convertStringToTemporal(valueTo);
         return Temporal.PlainDate.compare(formattedFrom, fromattedTo) > 0;
     }
 
diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
index f459cff54d..79d7e14bf0 100644
--- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
+++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
@@ -46,6 +46,7 @@ import { ReactQueryAppNamespace } from '../../../utils/reactQueryHelpers';
 import { statusTypes } from '../../../enrollment';
 import { cancelEditEventDataEntry } from '../../WidgetEventEdit/EditEventDataEntry/editEventDataEntry.actions';
 import { setCurrentDataEntry } from '../../DataEntry/actions/dataEntry.actions';
+import { convertIsoToLocalCalendar } from '../../../utils/converters/date'
 
 const getEventDate = (event) => {
     const eventDataConvertValue = convertDateWithTimeForView(event?.occurredAt || event?.scheduledAt);
@@ -55,7 +56,7 @@ const getEventDate = (event) => {
 
 const getEventScheduleDate = (event) => {
     if (!event?.scheduledAt) { return undefined; }
-    const eventDataConvertValue = convertValue(event?.scheduledAt, dataElementTypes.DATETIME);
+    const eventDataConvertValue = convertIsoToLocalCalendar(event?.scheduledAt);
     return eventDataConvertValue?.toString();
 };
 
diff --git a/src/core_modules/capture-core/components/WidgetEventSchedule/hooks/useDetermineSuggestedScheduleDate.js b/src/core_modules/capture-core/components/WidgetEventSchedule/hooks/useDetermineSuggestedScheduleDate.js
index 455c2f590c..5ad7f42d16 100644
--- a/src/core_modules/capture-core/components/WidgetEventSchedule/hooks/useDetermineSuggestedScheduleDate.js
+++ b/src/core_modules/capture-core/components/WidgetEventSchedule/hooks/useDetermineSuggestedScheduleDate.js
@@ -2,8 +2,6 @@
 import moment from 'moment';
 import { convertServerToClient, convertClientToForm } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
-import { convertStringToDateFormat } from '../../../utils/converters/date';
-
 
 const convertDate = (date): any => convertServerToClient(date, dataElementTypes.DATE);
 
@@ -84,7 +82,7 @@ export const useDetermineSuggestedScheduleDate = ({
     initialScheduleDate,
     hideDueDate,
 }: Props) => {
-    if (initialScheduleDate && !hideDueDate) { return convertStringToDateFormat(initialScheduleDate); }
+    if (initialScheduleDate && !hideDueDate) { return initialScheduleDate; }
     if (!programStageScheduleConfig) { return undefined; }
 
     const {
@@ -127,5 +125,5 @@ export const useDetermineSuggestedScheduleDate = ({
     , undefined);
 
     // $FlowFixMe dataElementTypes flow error
-    return convertStringToDateFormat(convertClientToForm(suggestedDate, dataElementTypes.DATE));
+    return convertClientToForm(suggestedDate, dataElementTypes.DATE);
 };
diff --git a/src/core_modules/capture-core/utils/converters/date/index.js b/src/core_modules/capture-core/utils/converters/date/index.js
index 6c755de6c3..ec6c4b5690 100644
--- a/src/core_modules/capture-core/utils/converters/date/index.js
+++ b/src/core_modules/capture-core/utils/converters/date/index.js
@@ -1,7 +1,6 @@
 // @flow
 export { convertDateObjectToDateFormatString } from './dateObjectToDateFormatString';
 export { convertMomentToDateFormatString } from './momentToDateFormatString';
-export { convertStringToDateFormat } from './stringToMomentDateFormat';
 export { convertIsoToLocalCalendar } from './convertIsoToLocalCalendar';
 export { convertLocalToIsoCalendar } from './convertLocalToIsoCalendar';
 export { convertStringToTemporal } from './convertStringToTemporal';
diff --git a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js b/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
deleted file mode 100644
index f75fb68255..0000000000
--- a/src/core_modules/capture-core/utils/converters/date/stringToMomentDateFormat.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// @flow
-import moment from 'moment';
-import { systemSettingsStore } from '../../../metaDataMemoryStores';
-
-/**
- * Converts a string date to a string date with default format based on the system date format
- * @export
- * @param {*} string - the string instance
- * @param {string} [format] - optional date format. If not provided, the function uses system date format
- * @returns {string}
- */
-export function convertStringToDateFormat(date: ?string, format?: string) {
-    if (!date || !date.length) { return ''; }
-    const dateFormat = format || systemSettingsStore.get().dateFormat;
-    const formattedDateString = moment(date, dateFormat).format(dateFormat);
-    return formattedDateString;
-}

From 76af22d16c8dd1d59a448a7e7e3450abbff97c01 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Fri, 31 Jan 2025 16:57:44 +0200
Subject: [PATCH 34/42] fix: dateTimeRange validator to recieve internalError

---
 .../EnrollmentEditEventPage.container.js      |  4 +-
 .../validators/form/dateTimeValidator.js      |  2 +-
 .../validators/form/getDateRangeValidator.js  |  2 +-
 .../form/getDateTimeRangeValidator.js         | 35 ++++-----
 .../DateTimeRange.component.js                | 72 +++++++++++++------
 5 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
index 79d7e14bf0..5568707b09 100644
--- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
+++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js
@@ -24,7 +24,7 @@ import { buildUrlQueryString, useLocationQuery } from '../../../utils/routing';
 import { deleteEnrollment, fetchEnrollments } from '../Enrollment/EnrollmentPage.actions';
 import { changeEventFromUrl } from '../ViewEvent/ViewEventComponent/viewEvent.actions';
 import { buildEnrollmentsAsOptions } from '../../ScopeSelector';
-import { convertDateWithTimeForView, convertValue } from '../../../converters/clientToView';
+import { convertDateWithTimeForView } from '../../../converters/clientToView';
 import { dataElementTypes } from '../../../metaData/DataElement';
 import { useAssignedUserSaveContext, useAssignee, useEvent } from './hooks';
 import type { Props } from './EnrollmentEditEventPage.types';
@@ -46,7 +46,7 @@ import { ReactQueryAppNamespace } from '../../../utils/reactQueryHelpers';
 import { statusTypes } from '../../../enrollment';
 import { cancelEditEventDataEntry } from '../../WidgetEventEdit/EditEventDataEntry/editEventDataEntry.actions';
 import { setCurrentDataEntry } from '../../DataEntry/actions/dataEntry.actions';
-import { convertIsoToLocalCalendar } from '../../../utils/converters/date'
+import { convertIsoToLocalCalendar } from '../../../utils/converters/date';
 
 const getEventDate = (event) => {
     const eventDataConvertValue = convertDateWithTimeForView(event?.occurredAt || event?.scheduledAt);
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/dateTimeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/dateTimeValidator.js
index f35bd67c25..0e47945b95 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/dateTimeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/dateTimeValidator.js
@@ -23,7 +23,7 @@ const CUSTOM_VALIDATION_MESSAGES = {
     MISSING_DATE: i18n.t('Please enter a date'),
 };
 
-export function isValidDateTime(value: DateTimeValue,
+export function isValidDateTime(value: ?DateTimeValue,
     internalComponentError?: ?{error: ?string, errorCode: ?string}): ValidationResult {
     if (!value) {
         return { valid: true };
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
index 36f6618d0c..856b970b11 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateRangeValidator.js
@@ -36,7 +36,7 @@ export const getDateRangeValidator = (invalidDateMessage: string) =>
         const formattedFrom = convertStringToTemporal(from);
         const fromattedTo = convertStringToTemporal(to);
         return {
-            valid: Temporal.PlainDateTime.compare(formattedFrom, fromattedTo) <= 0,
+            valid: Temporal.PlainDate.compare(formattedFrom, fromattedTo) <= 0,
             errorMessage: undefined,
         };
     };
diff --git a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
index a92c7af0c4..a00be8f396 100644
--- a/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
+++ b/src/core_modules/capture-core/utils/validation/validators/form/getDateTimeRangeValidator.js
@@ -1,10 +1,11 @@
 // @flow
 import { Temporal } from '@js-temporal/polyfill';
+import { systemSettingsStore } from 'capture-core/metaDataMemoryStores';
 import { convertStringToTemporal } from 'capture-core/utils/converters/date';
 import { isValidDateTime } from './dateTimeValidator';
 
-function isValidDateTimeWithEmptyCheck(value: ?Object) {
-    return value && isValidDateTime(value);
+function isValidDateTimeWithEmptyCheck(value: ?{date?: ?string, time?: ?string}, internalError?: ?{error: ?string, errorCode: ?string}) {
+    return isValidDateTime(value, internalError);
 }
 /* eslint-disable complexity */
 const convertDateTimeToTemporal = (value: ?Object) => {
@@ -13,7 +14,7 @@ const convertDateTimeToTemporal = (value: ?Object) => {
     }
 
     const { date, time } = value;
-
+    const calendar = systemSettingsStore.get().calendar;
     const dateInTemporalFormat = convertStringToTemporal(date);
 
     if (!dateInTemporalFormat) {
@@ -24,14 +25,15 @@ const convertDateTimeToTemporal = (value: ?Object) => {
     let hour;
     let minutes;
     try {
-        if (/[:.]/.test(time)) {
-            [hour, minutes] = time.split(/[:.]/).map(Number);
-        } else if (time.length === 3) {
-            hour = Number(time.substring(0, 1));
-            minutes = Number(time.substring(1, 3));
-        } else if (time.length === 4) {
-            hour = Number(time.substring(0, 2));
-            minutes = Number(time.substring(2, 4));
+        const timeStr = time.toString();
+        if (/[:.]/.test(timeStr)) {
+            [hour, minutes] = timeStr.split(/[:.]/).map(Number);
+        } else if (timeStr.length === 3) {
+            hour = Number(timeStr.substring(0, 1));
+            minutes = Number(timeStr.substring(1, 3));
+        } else if (timeStr.length === 4) {
+            hour = Number(timeStr.substring(0, 2));
+            minutes = Number(timeStr.substring(2, 4));
         } else {
             return null;
         }
@@ -40,26 +42,27 @@ const convertDateTimeToTemporal = (value: ?Object) => {
             return null;
         }
 
-        return new Temporal.PlainDateTime(year, month, day, hour, minutes);
+        return new Temporal.PlainDateTime(year, month, day, hour, minutes, 0, 0, 0, 0, calendar);
     } catch (error) {
         return null;
     }
 };
 
 export const getDateTimeRangeValidator = (invalidDateTimeMessage: string) =>
-    (value: { from?: ?Object, to?: ?Object }) => {
-        if (!value) {
+    // eslint-disable-next-line complexity
+    (value: { from?: ?Object, to?: ?Object }, internalComponentError?: ?{fromDateError: ?{error: ?string, errorCode: ?string}, toDateError: ?{error: ?string, errorCode: ?string}}) => {
+        if (!value?.from && value?.to) {
             return {
                 valid: false,
                 errorMessage: { from: invalidDateTimeMessage, to: invalidDateTimeMessage },
             };
         }
         const errorResult = [];
-        if (!isValidDateTimeWithEmptyCheck(value.from)) {
+        if (!isValidDateTimeWithEmptyCheck(value?.from, internalComponentError?.fromDateError).valid) {
             errorResult.push({ from: invalidDateTimeMessage });
         }
 
-        if (!isValidDateTimeWithEmptyCheck(value.to)) {
+        if (!isValidDateTimeWithEmptyCheck(value?.to, internalComponentError?.toDateError).valid) {
             errorResult.push({ to: invalidDateTimeMessage });
         }
 
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
index ff41792839..e16a915a59 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
@@ -17,11 +17,7 @@ type DateTimeValue = {
 type DateTimeRangeValue = {
     from?: ?DateTimeValue,
     to?: ?DateTimeValue,
-}
-
-type BlurOpts = {
-    touched?: ?boolean,
-}
+};
 
 type Props = {
     classes?: ?Object,
@@ -29,21 +25,35 @@ type Props = {
     value: DateTimeRangeValue,
     onBlur: (value: ?DateTimeRangeValue, options: Object) => void,
     onChange: (value: ?DateTimeRangeValue) => void,
-}
+};
+
+type State = {
+    fromDateError: {
+        error: ?string,
+        errorCode: ?string
+    },
+    toDateError: {
+        error: ?string,
+        errorCode: ?string
+    }
+};
 
 const inputKeys = {
     FROM: 'from',
     TO: 'to',
 };
 
-export class DateTimeRangeField extends React.Component<Props> {
+export class DateTimeRangeField extends React.Component<Props, State> {
     touchedFields: Set<string>;
     constructor(props: Props) {
         super(props);
         this.touchedFields = new Set();
+        this.state = {
+            fromDateError: { error: null, errorCode: null },
+            toDateError: { error: null, errorCode: null },
+        };
     }
 
-
     getValue = () => this.props.value || {};
 
 
@@ -82,27 +92,43 @@ export class DateTimeRangeField extends React.Component<Props> {
         return !!(value.to && value.to.date && value.to.time);
     }
 
-    handleFromBlur = (value: ?DateTimeValue, opts?: ?BlurOpts) => {
-        if (opts && opts.touched) {
+    handleFromBlur = (value: ?DateTimeValue, options: ?Object) => {
+        if (options?.touched) {
             this.touchedFields.add('fromTouched');
         }
-        this.handleBlur({
-            from: value,
-            to: this.getValue().to,
-        }, this.toHasValue());
+        this.setState(() => ({
+            fromDateError: { error: options?.error, errorCode: options?.errorCode },
+        }), () => {
+            this.handleBlur({
+                from: value,
+                to: this.getValue().to,
+            }, {
+                touched: !!this.toHasValue(),
+                fromDateError: this.state.fromDateError,
+                toDateError: this.state.toDateError,
+            });
+        });
     }
 
-    handleToBlur = (value: ?DateTimeValue, opts?: ?BlurOpts) => {
-        if (opts && opts.touched) {
+    handleToBlur = (value: ?DateTimeValue, options: ?Object) => {
+        if (options?.touched) {
             this.touchedFields.add('toTouched');
         }
-        this.handleBlur({
-            from: this.getValue().from,
-            to: value,
-        }, this.fromHasValue());
+        this.setState(() => ({
+            toDateError: { error: options?.error, errorCode: options?.errorCode },
+        }), () => {
+            this.handleBlur({
+                from: this.getValue().from,
+                to: value,
+            }, {
+                touched: !!this.fromHasValue(),
+                fromDateError: this.state.fromDateError,
+                toDateError: this.state.toDateError,
+            });
+        });
     }
 
-    handleBlur = (value: DateTimeRangeValue, otherFieldHasValue: boolean) => {
+    handleBlur = (value: DateTimeRangeValue, options: ?Object) => {
         const touched = this.touchedFields.size === 2;
         if (!value.from && !value.to) {
             this.props.onBlur(undefined, {
@@ -111,7 +137,9 @@ export class DateTimeRangeField extends React.Component<Props> {
             return;
         }
         this.props.onBlur(value, {
-            touched: touched || otherFieldHasValue,
+            touched: touched || options?.touched,
+            fromDateError: options?.fromDateError,
+            toDateError: options?.toDateError,
         });
     }
 

From ace2fe6b957f4cf8c7ec7a9ae50281ba42314e38 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Mon, 3 Feb 2025 17:05:50 +0200
Subject: [PATCH 35/42] fix: notes

---
 .../epics/addNoteForNewSingleEvent.epics.js   |   2 +-
 .../DataEntry/actions/dataEntryLoad.utils.js  |   4 +-
 .../components/Notes/Notes.component.js       | 269 +++++++++---------
 .../ViewEvent/Notes/viewEventNotes.epics.js   |  10 +-
 .../DataEntry/epics/dataEntryNote.epics.js    |   2 +-
 .../WidgetEnrollmentNote.epics.js             |   2 +-
 .../WidgetEventNote/WidgetEventNote.epics.js  |   2 +-
 .../WidgetEventSchedule.container.js          |   2 +-
 .../WidgetNote/NoteSection/NoteSection.js     |   4 +-
 9 files changed, 139 insertions(+), 158 deletions(-)

diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
index 15108d291a..a1506e6cc3 100644
--- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
+++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
@@ -37,7 +37,7 @@ export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: Re
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: convertListValue(storedAt, dataElementTypes.DATETIME),
+                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js b/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
index 88e7bb9208..51d78ede59 100644
--- a/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
+++ b/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
@@ -1,6 +1,6 @@
 // @flow
 import { convertValue } from '../../../converters/clientToForm';
-import { convertValue as convertListValue } from '../../../converters/clientToList';
+import { convertServerToClient } from '../../../converters';
 import { type RenderFoundation, dataElementTypes, DataElement } from '../../../metaData';
 
 import { getValidationError } from '../dataEntryField/internal/dataEntryField.utils';
@@ -88,7 +88,7 @@ export function getDataEntryNotes(
     const notes = clientValuesForDataEntry.notes || [];
     return notes.map((note, index) => ({
         ...note,
-        storedAt: convertListValue(note.storedAt, dataElementTypes.DATETIME),
+        storedAt: convertServerToClient(note.storedAt, dataElementTypes.DATETIME),
         key: index,
     }));
 }
diff --git a/src/core_modules/capture-core/components/Notes/Notes.component.js b/src/core_modules/capture-core/components/Notes/Notes.component.js
index ec25efb246..c695651729 100644
--- a/src/core_modules/capture-core/components/Notes/Notes.component.js
+++ b/src/core_modules/capture-core/components/Notes/Notes.component.js
@@ -1,13 +1,18 @@
 // @flow
 
 import * as React from 'react';
+import { useState, useEffect } from 'react';
+import moment from 'moment';
 import { Editor, Parser } from '@dhis2/d2-ui-rich-text';
 import { withStyles } from '@material-ui/core';
-import { colors, spacersNum, Menu, MenuItem, Button } from '@dhis2/ui';
+import { colors, spacersNum, Menu, MenuItem, Button, Tooltip } from '@dhis2/ui';
 import i18n from '@dhis2/d2-i18n';
+import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { withFocusSaver } from 'capture-ui';
 import { ConditionalTooltip } from 'capture-core/components/Tooltips/ConditionalTooltip';
 import { TextField } from '../FormFields/New';
+import { convertClientToList } from '../../converters';
+import { dataElementTypes } from '../../metaData';
 import type { Note } from './notes.types';
 
 
@@ -82,152 +87,134 @@ type Props = {
     },
 };
 
-type State = {
-    addIsOpen: boolean,
-    value: ?string,
-}
-
-class NotesPlain extends React.Component<Props, State> {
-    static defaultProps = {
-        entityAccess: { read: true, write: true },
-    }
-    constructor(props: Props) {
-        super(props);
-        this.state = {
-            addIsOpen: !!this.props.value,
-            value: this.props.value || null,
-        };
-    }
-
-    UNSAFE_componentWillReceiveProps(nextProps: Props) {
-        if (nextProps.value !== this.props.value
-            || this.props.value !== this.state.value) {
-            this.setState({
-                value: nextProps.value,
-            });
-        }
-    }
-
-    toggleIsOpen = () => {
-        this.setState(prevState => ({
-            addIsOpen: !prevState.addIsOpen,
-        }));
-    }
-
-    onCancel = () => {
-        this.props.onBlur(null, { touched: false });
-        this.toggleIsOpen();
-    }
-
-    onNewNoteEditorBlur = (value: string) => {
-        this.props.onBlur(value, { touched: false });
-    }
-
-    handleAddNote = () => {
-        if (this.props.value) {
-            this.props.onAddNote(this.props.value);
+const NotesPlain = ({
+    notes,
+    onAddNote,
+    onBlur,
+    value: propValue,
+    entityAccess = { read: true, write: true },
+    smallMainButton,
+    classes,
+}: Props) => {
+    const [addIsOpen, setAddIsOpen] = useState(false);
+    const [inputValue, setInputValue] = useState('');
+    const { fromServerDate } = useTimeZoneConversion();
+
+    useEffect(() => {
+        setAddIsOpen(!!propValue);
+        setInputValue(propValue || '');
+    }, [propValue]);
+
+    const toggleIsOpen = () => {
+        setAddIsOpen(prev => !prev);
+    };
+
+    const onCancel = () => {
+        onBlur(null, { touched: false });
+        toggleIsOpen();
+    };
+
+    const onNewNoteEditorBlur = (value: string) => {
+        onBlur(value, { touched: false });
+    };
+
+    const handleAddNote = () => {
+        if (propValue) {
+            onAddNote(propValue);
         }
-        this.toggleIsOpen();
-        this.props.onBlur(null, { touched: false });
-    }
-
-    handleChange = (value: ?string) => {
-        this.setState({ value });
-    }
-
-    renderInput = () => {
-        const { classes } = this.props;
-        return (
-            <div className={classes.newNoteFormContainer}>
-
-                <Editor onEdit={this.handleChange}>
-                    <FocusTextField
-                        onBlur={this.onNewNoteEditorBlur}
-                        onChange={this.handleChange}
-                        value={this.state.value}
-                        multiLine
-                        data-test="note-textfield"
-                    />
-                </Editor>
-                <div className={classes.newNoteButtonsContainer} data-test="note-buttons-container">
-                    <Button
-                        onClick={this.handleAddNote}
-                        className={classes.addNoteContainer}
-                        primary
-                        small
-                    >
-                        {i18n.t('Add note')}
-                    </Button>
-                    <Button
-                        onClick={this.onCancel}
-                        secondary
-                        small
-                    >
-                        {i18n.t('Cancel')}
-                    </Button>
-                </div>
-
+        toggleIsOpen();
+        onBlur(null, { touched: false });
+    };
+
+    const handleChange = (value: ?string) => {
+        setInputValue(value);
+    };
+
+    const renderInput = () => (
+        <div className={classes.newNoteFormContainer}>
+            <Editor onEdit={handleChange}>
+                <FocusTextField
+                    onBlur={onNewNoteEditorBlur}
+                    onChange={handleChange}
+                    value={inputValue}
+                    multiLine
+                    data-test="note-textfield"
+                />
+            </Editor>
+            <div className={classes.newNoteButtonsContainer} data-test="note-buttons-container">
+                <Button
+                    onClick={handleAddNote}
+                    className={classes.addNoteContainer}
+                    primary
+                    small
+                >
+                    {i18n.t('Add note')}
+                </Button>
+                <Button
+                    onClick={onCancel}
+                    secondary
+                    small
+                >
+                    {i18n.t('Cancel')}
+                </Button>
             </div>
-        );
-    }
-
-    renderButton = (canAddNote: boolean) => {
-        const { smallMainButton } = this.props;
-        return (
-            <div
-                data-test="new-note-button"
+
+        </div>
+    );
+
+
+    const renderButton = (canAddNote: boolean) => (
+        <div data-test="new-note-button">
+            <ConditionalTooltip
+                content={i18n.t('You don\'t have access to write notes')}
+                enabled={!canAddNote}
             >
-                <ConditionalTooltip
-                    content={i18n.t('You don\'t have access to write notes')}
-                    enabled={!canAddNote}
+                <Button
+                    onClick={toggleIsOpen}
+                    disabled={!canAddNote}
+                    // eslint-disable-next-line indent
+                    small={smallMainButton}
                 >
-                    <Button
-                        onClick={this.toggleIsOpen}
-                        disabled={!canAddNote}
-                        small={smallMainButton}
-                    >
-                        {i18n.t('Write note')}
-                    </Button>
-                </ConditionalTooltip>
-            </div>
-        );
-    }
-
-    render = () => {
-        const { notes, classes, entityAccess } = this.props;
-        return (
-            <div className={classes.notesContainer}>
-                <Menu dense className={classes.notesList} data-test="notes-list">
-                    {notes.map(n => (
-                        <MenuItem
-                            className={classes.noteItem}
-                            key={n.clientId}
-                            data-test="note"
-                            label={<>
-                                <div className={classes.noteItemHeader}>
-                                    <div className={classes.noteItemUser} data-test="note-user">
-                                        {n.createdBy ? `${n.createdBy.firstName} ${n.createdBy.surname}` : `${n.storedBy}` }
-                                    </div>
-                                    <div className={classes.noteItemDate} data-test="note-date">
-                                        {n.storedDate}
-                                    </div>
-                                </div>
-                                <div data-test="note-text">
-                                    <Parser>{n.value}</Parser>
+                    {i18n.t('Write note')}
+                </Button>
+            </ConditionalTooltip>
+        </div>
+    );
+
+    return (
+        <div className={classes.notesContainer}>
+            <Menu dense className={classes.notesList} data-test="notes-list">
+                {notes.map(n => (
+                    <MenuItem
+                        className={classes.noteItem}
+                        key={n.clientId}
+                        data-test="note"
+                        label={<>
+                            <div className={classes.noteItemHeader}>
+                                <div className={classes.noteItemUser} data-test="note-user">
+                                    {n.createdBy ? `${n.createdBy.firstName} ${n.createdBy.surname}` : `${n.storedBy}` }
                                 </div>
-                            </>}
-                        />
-                    ))}
-                </Menu>
-                {
-                    <div className={classes.newNoteContainer} data-test="new-note-container">
-                        { this.state.addIsOpen ? this.renderInput() : this.renderButton(entityAccess.write) }
-                    </div>
-                }
+                                <div className={classes.noteItemDate} data-test="note-date">
+                                    <span>
+                                        <Tooltip content={convertClientToList(fromServerDate(n.storedAt), dataElementTypes.DATETIME)}>
+                                            {moment(fromServerDate(n.storedAt)).fromNow()}
+                                        </Tooltip>
+                                    </span>
 
+                                </div>
+                            </div>
+                            <div data-test="note-text">
+                                <Parser>{n.value}</Parser>
+                            </div>
+                        </>}
+                    />
+                ))}
+            </Menu>
+            <div className={classes.newNoteContainer} data-test="new-note-container">
+                {addIsOpen ? renderInput() : renderButton(entityAccess.write)}
             </div>
-        );
-    }
-}
+        </div>
+    );
+};
 
 export const Notes = withStyles(styles)(NotesPlain);
diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
index 2c2c78fd8b..c3f8b33dfc 100644
--- a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
+++ b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
@@ -5,8 +5,6 @@ import { featureAvailable, FEATURES } from 'capture-core-utils';
 import { map, switchMap } from 'rxjs/operators';
 import uuid from 'd2-utilizr/lib/uuid';
 import moment from 'moment';
-import { convertValue as convertListValue } from '../../../../converters/clientToList';
-import { dataElementTypes } from '../../../../metaData';
 import {
     actionTypes as viewEventNotesActionTypes,
     batchActionTypes as viewEventNotesBatchActionTypes,
@@ -41,15 +39,11 @@ export const loadNotesForViewEventEpic = (action$: InputObservable) =>
         map((action) => {
             const eventContainer = action.payload.eventContainer;
             const notes = (eventContainer && eventContainer.event && eventContainer.event.notes) || [];
-            const convertedNotes = notes.map(note => ({
-                ...note,
-                storedDate: convertListValue(note.storedAt, dataElementTypes.DATETIME),
-            }));
             // Load event relationships
 
             return batchActions([
                 eventNotesLoaded(),
-                setNotes(noteKey, convertedNotes),
+                setNotes(noteKey, notes),
             ], viewEventNotesBatchActionTypes.LOAD_EVENT_NOTES_BATCH);
         }));
 
@@ -80,7 +74,7 @@ export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxSt
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedDate: convertListValue(moment().toISOString(), dataElementTypes.DATETIME),
+                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
                     clientId: uuid(),
                 };
                 return batchActions([
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
index 6fec8522f7..39c0a16f11 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
@@ -36,7 +36,7 @@ export const addNoteForNewEnrollmentEventEpic = (action$: InputObservable, store
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: convertListValue(storedAt, dataElementTypes.DATETIME),
+                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
index 5a0fb28ebb..d39bb599a3 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
@@ -42,7 +42,7 @@ export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxS
                     },
                     updatedAt: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
                 };
 
                 const saveContext = {
diff --git a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
index 315c0dba5b..d4aea8bc06 100644
--- a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
@@ -52,7 +52,7 @@ export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore,
                     },
                     lastUpdated: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
                 };
                 const formNote = {
                     ...clientNote,
diff --git a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
index 4132d024e9..022de2e577 100644
--- a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
+++ b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
@@ -123,7 +123,7 @@ export const WidgetEventSchedule = ({
     const onAddNote = (note) => {
         const newNote = {
             storedBy: currentUser.userName,
-            storedAt: moment().toISOString(),
+            storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
             value: note,
             createdBy: {
                 firstName: currentUser.firstName,
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index 0069b42f3e..078f3a78d5 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -9,7 +9,7 @@ import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
-import { convertValue as convertValueClientToView } from '../../../converters/clientToView';
+import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
 
 const FocusTextField = withFocusSaver()(TextField);
@@ -100,7 +100,7 @@ const NoteSectionPlain = ({
     }, [handleAddNote, newNoteValue]);
 
     const NoteItem = ({ value, storedAt, createdBy }) => {
-        const localDateTime: string = (convertValueClientToView(storedAt, dataElementTypes.DATETIME): any);
+        const localDateTime: string = convertClientToList(fromServerDate(storedAt), dataElementTypes.DATETIME);
         return (
             <div data-test="note-item" className={cx(classes.item)}>
                 {/* TODO: add avatar */}

From 680fa4c8b5ac3d71401d94ef6fd95cd287cbea2d Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 5 Feb 2025 14:46:19 +0200
Subject: [PATCH 36/42] fix: search when adding relationship

---
 .../SearchResults/TeiRelationshipSearchResults.component.js    | 3 ++-
 .../common/TEIRelationshipsWidget/TeiSearch/serverToFilters.js | 2 +-
 .../capture-core/components/TeiSearch/serverToFilters.js       | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js
index 461424f302..0edbfbfd1f 100644
--- a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js
+++ b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js
@@ -5,6 +5,7 @@ import i18n from '@dhis2/d2-i18n';
 import { withStyles } from '@material-ui/core';
 import { Pagination } from 'capture-ui';
 import { Button } from '@dhis2/ui';
+import { convertFormToClient } from 'capture-core/converters';
 import { withNavigation } from '../../../../Pagination/withDefaultNavigation';
 import { makeAttributesSelector } from './teiRelationshipSearchResults.selectors';
 import { CardList } from '../../../../CardList';
@@ -130,7 +131,7 @@ class TeiRelationshipSearchResultsPlain extends React.Component<Props> {
             .filter(key => searchValues[key] !== null)
             .map((key) => {
                 const element = searchForm.getElement(key);
-                const value = searchValues[key];
+                const value = convertFormToClient(searchValues[key], element.type);
                 return { name: element.formName, value, id: element.id, type: element.type };
             });
     }
diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TeiSearch/serverToFilters.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TeiSearch/serverToFilters.js
index 78763397f6..e8380896bc 100644
--- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TeiSearch/serverToFilters.js
+++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TeiSearch/serverToFilters.js
@@ -12,7 +12,7 @@ const like = (value: any, elementId: string) => `${elementId}:like:${escapeStrin
 
 
 function convertRange(value: RangeValue, elementId: string) {
-    return `${elementId}:ge:${value.from}:le:${value.to}`;
+    return `${elementId}:ge:${escapeString(String(value.from))}:le:${escapeString(String(value.to))}`;
 }
 
 const valueConvertersForType = {
diff --git a/src/core_modules/capture-core/components/TeiSearch/serverToFilters.js b/src/core_modules/capture-core/components/TeiSearch/serverToFilters.js
index afbf8a2b49..c6027af214 100644
--- a/src/core_modules/capture-core/components/TeiSearch/serverToFilters.js
+++ b/src/core_modules/capture-core/components/TeiSearch/serverToFilters.js
@@ -12,7 +12,7 @@ const like = (value: any, elementId: string) => `${elementId}:like:${escapeStrin
 
 
 const convertRange = (value: RangeValue, { id: elementId }: DataElement) => (
-    `${elementId}:ge:${value.from}:le:${value.to}`
+    `${elementId}:ge:${escapeString(String(value.from))}:le:${escapeString(String(value.to))}`
 );
 
 const convertString = (value: any, metaElement: DataElement) => {

From 1e444306f0acc8997aa27d370dc8ebfcc7a75c29 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 13 Feb 2025 03:18:57 +0200
Subject: [PATCH 37/42] fix: notes not working when changing server timeZone

---
 .../epics/addNoteForNewSingleEvent.epics.js   |  2 +-
 .../DataEntry/actions/dataEntryLoad.utils.js  |  5 +-
 .../components/Notes/Notes.component.js       | 56 +++++++++++--------
 .../ViewEvent/Notes/viewEventNotes.epics.js   |  2 +-
 .../DataEntry/epics/dataEntryNote.epics.js    |  2 +-
 .../WidgetEnrollmentNote.epics.js             |  2 +-
 .../WidgetEventNote/WidgetEventNote.epics.js  |  2 +-
 .../WidgetEventSchedule.container.js          |  2 +-
 .../WidgetNote/NoteSection/NoteSection.js     | 10 ++--
 .../systemSettings/cacheSystemSetttings.js    |  2 +-
 10 files changed, 47 insertions(+), 38 deletions(-)

diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
index 457ff951cf..b7ae51ceb5 100644
--- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
+++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
@@ -33,7 +33,7 @@ export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: Re
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+                    storedAt: moment().toISOString(),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js b/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
index 51d78ede59..9bf9778d2c 100644
--- a/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
+++ b/src/core_modules/capture-core/components/DataEntry/actions/dataEntryLoad.utils.js
@@ -1,7 +1,6 @@
 // @flow
 import { convertValue } from '../../../converters/clientToForm';
-import { convertServerToClient } from '../../../converters';
-import { type RenderFoundation, dataElementTypes, DataElement } from '../../../metaData';
+import { type RenderFoundation, DataElement } from '../../../metaData';
 
 import { getValidationError } from '../dataEntryField/internal/dataEntryField.utils';
 import type { ValidatorContainer } from '../dataEntryField/internal/dataEntryField.utils';
@@ -88,7 +87,7 @@ export function getDataEntryNotes(
     const notes = clientValuesForDataEntry.notes || [];
     return notes.map((note, index) => ({
         ...note,
-        storedAt: convertServerToClient(note.storedAt, dataElementTypes.DATETIME),
+        storedAt: note.storedAt,
         key: index,
     }));
 }
diff --git a/src/core_modules/capture-core/components/Notes/Notes.component.js b/src/core_modules/capture-core/components/Notes/Notes.component.js
index c695651729..169c9e85aa 100644
--- a/src/core_modules/capture-core/components/Notes/Notes.component.js
+++ b/src/core_modules/capture-core/components/Notes/Notes.component.js
@@ -98,7 +98,7 @@ const NotesPlain = ({
 }: Props) => {
     const [addIsOpen, setAddIsOpen] = useState(false);
     const [inputValue, setInputValue] = useState('');
-    const { fromServerDate } = useTimeZoneConversion();
+    const { fromServerDate, fromClientDate } = useTimeZoneConversion();
 
     useEffect(() => {
         setAddIsOpen(!!propValue);
@@ -184,31 +184,39 @@ const NotesPlain = ({
     return (
         <div className={classes.notesContainer}>
             <Menu dense className={classes.notesList} data-test="notes-list">
-                {notes.map(n => (
-                    <MenuItem
-                        className={classes.noteItem}
-                        key={n.clientId}
-                        data-test="note"
-                        label={<>
-                            <div className={classes.noteItemHeader}>
-                                <div className={classes.noteItemUser} data-test="note-user">
-                                    {n.createdBy ? `${n.createdBy.firstName} ${n.createdBy.surname}` : `${n.storedBy}` }
+                {notes.map((n) => {
+                    const formattedDate = n.storedAt && n.storedAt.endsWith('Z') ?
+                        fromClientDate(n.storedAt) :
+                        fromServerDate(n.storedAt);
+
+                    return (
+                        <MenuItem
+                            className={classes.noteItem}
+                            key={n.clientId}
+                            data-test="note"
+                            label={<>
+                                <div className={classes.noteItemHeader}>
+                                    <div className={classes.noteItemUser} data-test="note-user">
+                                        {n.createdBy ?
+                                            `${n.createdBy.firstName} ${n.createdBy.surname}`
+                                            : `${n.storedBy}` }
+                                    </div>
+                                    <div className={classes.noteItemDate} data-test="note-date">
+                                        <span>
+                                            <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
+                                                {moment(formattedDate).fromNow()}
+                                            </Tooltip>
+                                        </span>
+
+                                    </div>
                                 </div>
-                                <div className={classes.noteItemDate} data-test="note-date">
-                                    <span>
-                                        <Tooltip content={convertClientToList(fromServerDate(n.storedAt), dataElementTypes.DATETIME)}>
-                                            {moment(fromServerDate(n.storedAt)).fromNow()}
-                                        </Tooltip>
-                                    </span>
-
+                                <div data-test="note-text">
+                                    <Parser>{n.value}</Parser>
                                 </div>
-                            </div>
-                            <div data-test="note-text">
-                                <Parser>{n.value}</Parser>
-                            </div>
-                        </>}
-                    />
-                ))}
+                            </>}
+                        />
+                    );
+                })}
             </Menu>
             <div className={classes.newNoteContainer} data-test="new-note-container">
                 {addIsOpen ? renderInput() : renderButton(entityAccess.write)}
diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
index c3f8b33dfc..50666730e1 100644
--- a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
+++ b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
@@ -74,7 +74,7 @@ export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxSt
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+                    storedAt: moment().toISOString(),
                     clientId: uuid(),
                 };
                 return batchActions([
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
index b4d5f90de7..03a28a5517 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
@@ -33,7 +33,7 @@ export const addNoteForNewEnrollmentEventEpic = (action$: InputObservable, store
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+                    storedAt: moment().toISOString(),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
index d39bb599a3..5a0fb28ebb 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
@@ -42,7 +42,7 @@ export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxS
                     },
                     updatedAt: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+                    storedAt: moment().toISOString(),
                 };
 
                 const saveContext = {
diff --git a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
index d4aea8bc06..315c0dba5b 100644
--- a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
@@ -52,7 +52,7 @@ export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore,
                     },
                     lastUpdated: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+                    storedAt: moment().toISOString(),
                 };
                 const formNote = {
                     ...clientNote,
diff --git a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
index 9a9b393ec9..aefbecd9ed 100644
--- a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
+++ b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
@@ -127,7 +127,7 @@ export const WidgetEventSchedule = ({
     const onAddNote = (note) => {
         const newNote = {
             storedBy: currentUser.userName,
-            storedAt: moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
+            storedAt: moment().toISOString(),
             value: note,
             createdBy: {
                 firstName: currentUser.firstName,
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index 79b8dd113c..e05a66555d 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -81,7 +81,7 @@ const NoteSectionPlain = ({
 }: Props) => {
     const [isEditing, setEditing] = useState(false);
     const [newNoteValue, setNewNoteValue] = useState('');
-    const { fromServerDate } = useTimeZoneConversion();
+    const { fromServerDate, fromClientDate } = useTimeZoneConversion();
 
     const handleChange = useCallback((value) => {
         setEditing(true);
@@ -100,7 +100,9 @@ const NoteSectionPlain = ({
     }, [handleAddNote, newNoteValue]);
 
     const NoteItem = ({ value, storedAt, createdBy }) => {
-        const localDateTime = convertClientToList(fromServerDate(storedAt), dataElementTypes.DATETIME);
+        const formattedDate = storedAt && storedAt.endsWith('Z') ?
+            fromClientDate(storedAt) :
+            fromServerDate(storedAt);
         return (
             <div data-test="note-item" className={cx(classes.item)}>
                 {/* TODO: add avatar */}
@@ -110,8 +112,8 @@ const NoteSectionPlain = ({
                             {createdBy.firstName} {' '} {createdBy.surname}
                         </span>}
                         <span className={cx(classes.headerText, classes.lastUpdated)}>
-                            <Tooltip content={localDateTime}>
-                                {moment(fromServerDate(storedAt)).fromNow()}
+                            <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
+                                {moment(formattedDate).fromNow()}
                             </Tooltip>
                         </span>
                     </div>
diff --git a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
index 225c4261ab..308f785765 100644
--- a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
+++ b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
@@ -27,7 +27,7 @@ export async function cacheSystemSettings(
         },
         {
             id: 'calendar',
-            value: systemSettings.calendar,
+            value: systemSettings.calendar !== 'julian' ? systemSettings.calendar : 'iso8601',
         },
     ];
 

From fe85e5df00064d5665950ffb028c0cfb1072a2a6 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 13 Feb 2025 12:59:56 +0200
Subject: [PATCH 38/42] chore: pass fromClientDate to storedAt

---
 src/components/AppLoader/AppLoader.component.js | 10 +++++++---
 .../epics/addNoteForNewSingleEvent.epics.js     |  4 ++--
 .../components/Notes/Notes.component.js         | 17 ++++++++++-------
 .../ViewEvent/Notes/viewEventNotes.epics.js     |  4 ++--
 4 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/components/AppLoader/AppLoader.component.js b/src/components/AppLoader/AppLoader.component.js
index 49af80126f..1bad4586ce 100644
--- a/src/components/AppLoader/AppLoader.component.js
+++ b/src/components/AppLoader/AppLoader.component.js
@@ -2,7 +2,7 @@
 import React, { useCallback, useMemo, useEffect } from 'react';
 import log from 'loglevel';
 import { useHistory } from 'react-router-dom';
-import { useDataEngine, useConfig } from '@dhis2/app-runtime';
+import { useDataEngine, useConfig, useTimeZoneConversion } from '@dhis2/app-runtime';
 import { LoadingMaskForPage } from 'capture-core/components/LoadingMasks';
 import { DisplayException } from 'capture-core/utils/exceptions';
 import { makeQuerySingleResource } from 'capture-core/utils/api';
@@ -20,18 +20,20 @@ type Props = {
 const useApiUtils = () => {
     const dataEngine = useDataEngine();
     const { serverVersion } = useConfig();
+    const { fromClientDate } = useTimeZoneConversion();
     return useMemo(() => ({
         querySingleResource: makeQuerySingleResource(dataEngine.query.bind(dataEngine)),
         mutate: dataEngine.mutate.bind(dataEngine),
         absoluteApiPath: buildUrl(dataEngine.link.config.baseUrl, dataEngine.link.versionedApiPath),
         serverVersion,
-    }), [dataEngine, serverVersion]);
+        fromClientDate,
+    }), [dataEngine, serverVersion, fromClientDate]);
 };
 
 export const AppLoader = (props: Props) => {
     const { onRunApp, onCacheExpired } = props;
     const [loadError, setLoadError] = React.useState(null);
-    const { querySingleResource, mutate, absoluteApiPath, serverVersion } = useApiUtils();
+    const { querySingleResource, mutate, absoluteApiPath, serverVersion, fromClientDate } = useApiUtils();
     const history = useHistory();
 
     const logError = useCallback((error) => {
@@ -56,6 +58,7 @@ export const AppLoader = (props: Props) => {
                     mutate,
                     absoluteApiPath,
                     serverVersion,
+                    fromClientDate,
                 },
                 // $FlowFixMe[prop-missing] automated comment
                 () => onRunApp(store));
@@ -83,6 +86,7 @@ export const AppLoader = (props: Props) => {
         absoluteApiPath,
         history,
         serverVersion,
+        fromClientDate,
     ]);
 
     useEffect(() => {
diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
index b7ae51ceb5..2e1c534ca4 100644
--- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
+++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
@@ -11,7 +11,7 @@ import {
     addNote,
 } from '../../../../../DataEntry/actions/dataEntry.actions';
 
-export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource }: ApiUtils) =>
+export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource, fromClientDate }: ApiUtils) =>
     action$.pipe(
         ofType(newEventDataEntryActionTypes.ADD_NEW_EVENT_NOTE),
         switchMap((action) => {
@@ -33,7 +33,7 @@ export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: Re
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: fromClientDate(moment().toISOString()),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/Notes/Notes.component.js b/src/core_modules/capture-core/components/Notes/Notes.component.js
index 169c9e85aa..765d1d8dc7 100644
--- a/src/core_modules/capture-core/components/Notes/Notes.component.js
+++ b/src/core_modules/capture-core/components/Notes/Notes.component.js
@@ -184,12 +184,12 @@ const NotesPlain = ({
     return (
         <div className={classes.notesContainer}>
             <Menu dense className={classes.notesList} data-test="notes-list">
-                {notes.map((n) => {
-                    const formattedDate = n.storedAt && n.storedAt.endsWith('Z') ?
+                {notes.map(n =>
+                /*                     const formattedDate = n.storedAt && n.storedAt.endsWith('Z') ?
                         fromClientDate(n.storedAt) :
-                        fromServerDate(n.storedAt);
+                        fromServerDate(n.storedAt); */
 
-                    return (
+                    (
                         <MenuItem
                             className={classes.noteItem}
                             key={n.clientId}
@@ -203,8 +203,11 @@ const NotesPlain = ({
                                     </div>
                                     <div className={classes.noteItemDate} data-test="note-date">
                                         <span>
-                                            <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
+                                            {/*                                             <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
                                                 {moment(formattedDate).fromNow()}
+                                            </Tooltip> */}
+                                            <Tooltip content={convertClientToList(fromServerDate(n.storedAt), dataElementTypes.DATETIME)}>
+                                                {moment(fromServerDate(n.storedAt)).fromNow()}
                                             </Tooltip>
                                         </span>
 
@@ -215,8 +218,8 @@ const NotesPlain = ({
                                 </div>
                             </>}
                         />
-                    );
-                })}
+                    ),
+                )}
             </Menu>
             <div className={classes.newNoteContainer} data-test="new-note-container">
                 {addIsOpen ? renderInput() : renderButton(entityAccess.write)}
diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
index 50666730e1..1e31dcb4c9 100644
--- a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
+++ b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
@@ -47,7 +47,7 @@ export const loadNotesForViewEventEpic = (action$: InputObservable) =>
             ], viewEventNotesBatchActionTypes.LOAD_EVENT_NOTES_BATCH);
         }));
 
-export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource }: ApiUtils) =>
+export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource, fromClientDate }: ApiUtils) =>
     action$.pipe(
         ofType(viewEventNotesActionTypes.REQUEST_SAVE_EVENT_NOTE),
         switchMap((action) => {
@@ -74,7 +74,7 @@ export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxSt
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: fromClientDate(moment().toISOString()),
                     clientId: uuid(),
                 };
                 return batchActions([

From c59a0f3e75d9b37b24241fd0e84dd966eea1e923 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Thu, 13 Feb 2025 13:56:36 +0200
Subject: [PATCH 39/42] fix: notes

---
 .../epics/addNoteForNewSingleEvent.epics.js   |  2 +-
 .../components/Notes/Notes.component.js       |  9 +---
 .../ViewEvent/Notes/viewEventNotes.epics.js   |  2 +-
 .../DataEntry/epics/dataEntryNote.epics.js    |  4 +-
 .../WidgetEnrollmentNote.epics.js             |  4 +-
 .../WidgetEventNote/WidgetEventNote.epics.js  |  4 +-
 .../WidgetEventSchedule.container.js          |  4 +-
 .../WidgetNote/NoteSection/NoteSection.js     | 43 ++++++++-----------
 .../capture-core/flow/typeDeclarations.js     |  3 ++
 9 files changed, 34 insertions(+), 41 deletions(-)

diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
index 2e1c534ca4..0fd0029628 100644
--- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
+++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/epics/addNoteForNewSingleEvent.epics.js
@@ -33,7 +33,7 @@ export const addNoteForNewSingleEventEpic = (action$: InputObservable, store: Re
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: fromClientDate(moment().toISOString()),
+                    storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/Notes/Notes.component.js b/src/core_modules/capture-core/components/Notes/Notes.component.js
index 765d1d8dc7..1f1174accb 100644
--- a/src/core_modules/capture-core/components/Notes/Notes.component.js
+++ b/src/core_modules/capture-core/components/Notes/Notes.component.js
@@ -98,7 +98,7 @@ const NotesPlain = ({
 }: Props) => {
     const [addIsOpen, setAddIsOpen] = useState(false);
     const [inputValue, setInputValue] = useState('');
-    const { fromServerDate, fromClientDate } = useTimeZoneConversion();
+    const { fromServerDate } = useTimeZoneConversion();
 
     useEffect(() => {
         setAddIsOpen(!!propValue);
@@ -185,10 +185,6 @@ const NotesPlain = ({
         <div className={classes.notesContainer}>
             <Menu dense className={classes.notesList} data-test="notes-list">
                 {notes.map(n =>
-                /*                     const formattedDate = n.storedAt && n.storedAt.endsWith('Z') ?
-                        fromClientDate(n.storedAt) :
-                        fromServerDate(n.storedAt); */
-
                     (
                         <MenuItem
                             className={classes.noteItem}
@@ -203,9 +199,6 @@ const NotesPlain = ({
                                     </div>
                                     <div className={classes.noteItemDate} data-test="note-date">
                                         <span>
-                                            {/*                                             <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
-                                                {moment(formattedDate).fromNow()}
-                                            </Tooltip> */}
                                             <Tooltip content={convertClientToList(fromServerDate(n.storedAt), dataElementTypes.DATETIME)}>
                                                 {moment(fromServerDate(n.storedAt)).fromNow()}
                                             </Tooltip>
diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
index 1e31dcb4c9..2c473aaf9e 100644
--- a/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
+++ b/src/core_modules/capture-core/components/Pages/ViewEvent/Notes/viewEventNotes.epics.js
@@ -74,7 +74,7 @@ export const addNoteForViewEventEpic = (action$: InputObservable, store: ReduxSt
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: fromClientDate(moment().toISOString()),
+                    storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                     clientId: uuid(),
                 };
                 return batchActions([
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
index 03a28a5517..868eb36689 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/epics/dataEntryNote.epics.js
@@ -11,7 +11,7 @@ import {
     addNote,
 } from '../../../DataEntry/actions/dataEntry.actions';
 
-export const addNoteForNewEnrollmentEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource }: ApiUtils) =>
+export const addNoteForNewEnrollmentEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource, fromClientDate }: ApiUtils) =>
     action$.pipe(
         ofType(newEventWidgetDataEntryActionTypes.EVENT_NOTE_ADD),
         switchMap((action) => {
@@ -33,7 +33,7 @@ export const addNoteForNewEnrollmentEventEpic = (action$: InputObservable, store
                         uid: clientId,
                     },
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                     clientId: uuid(),
                 };
 
diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
index 5a0fb28ebb..46e60fc834 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
@@ -15,7 +15,7 @@ const createServerData = (note, useNewEndpoint) => {
     return { notes: [{ value: note }] };
 };
 
-export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource }: ApiUtils) =>
+export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource, fromClientDate }: ApiUtils) =>
     action$.pipe(
         ofType(actionTypes.REQUEST_ADD_NOTE_FOR_ENROLLMENT),
         switchMap((action) => {
@@ -42,7 +42,7 @@ export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxS
                     },
                     updatedAt: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                 };
 
                 const saveContext = {
diff --git a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
index 315c0dba5b..69be90b443 100644
--- a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
@@ -24,7 +24,7 @@ const createServerData = (eventId, note, useNewEndpoint) => {
     return { event: eventId, notes: [{ value: note }] };
 };
 
-export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource }: ApiUtils) =>
+export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore, { querySingleResource, fromClientDate }: ApiUtils) =>
     action$.pipe(
         ofType(actionTypes.REQUEST_ADD_NOTE_FOR_EVENT),
         switchMap((action) => {
@@ -52,7 +52,7 @@ export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore,
                     },
                     lastUpdated: moment().toISOString(),
                     storedBy: userName,
-                    storedAt: moment().toISOString(),
+                    storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                 };
                 const formNote = {
                     ...clientNote,
diff --git a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
index aefbecd9ed..69f03772c9 100644
--- a/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
+++ b/src/core_modules/capture-core/components/WidgetEventSchedule/WidgetEventSchedule.container.js
@@ -2,6 +2,7 @@
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import i18n from '@dhis2/d2-i18n';
 import { useDispatch } from 'react-redux';
+import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import moment from 'moment';
 import { getProgramAndStageForProgram, TrackerProgram, getProgramEventAccess, dataElementTypes } from '../../metaData';
 import { getCachedOrgUnitName } from '../../metadataRetrieval/orgUnitName';
@@ -43,6 +44,7 @@ export const WidgetEventSchedule = ({
     const suggestedScheduleDate = useDetermineSuggestedScheduleDate({
         programStageScheduleConfig, programConfig, initialScheduleDate, ...passOnProps,
     });
+    const { fromClientDate } = useTimeZoneConversion();
     const orgUnitName = getCachedOrgUnitName(initialOrgUnitId);
     const { currentUser, noteId } = useNoteDetails();
     const [scheduleDate, setScheduleDate] = useState('');
@@ -127,7 +129,7 @@ export const WidgetEventSchedule = ({
     const onAddNote = (note) => {
         const newNote = {
             storedBy: currentUser.userName,
-            storedAt: moment().toISOString(),
+            storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
             value: note,
             createdBy: {
                 firstName: currentUser.firstName,
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index e05a66555d..b800cacc28 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -81,7 +81,7 @@ const NoteSectionPlain = ({
 }: Props) => {
     const [isEditing, setEditing] = useState(false);
     const [newNoteValue, setNewNoteValue] = useState('');
-    const { fromServerDate, fromClientDate } = useTimeZoneConversion();
+    const { fromServerDate } = useTimeZoneConversion();
 
     const handleChange = useCallback((value) => {
         setEditing(true);
@@ -99,31 +99,26 @@ const NoteSectionPlain = ({
         setEditing(false);
     }, [handleAddNote, newNoteValue]);
 
-    const NoteItem = ({ value, storedAt, createdBy }) => {
-        const formattedDate = storedAt && storedAt.endsWith('Z') ?
-            fromClientDate(storedAt) :
-            fromServerDate(storedAt);
-        return (
-            <div data-test="note-item" className={cx(classes.item)}>
-                {/* TODO: add avatar */}
-                <div className={classes.rightColumn}>
-                    <div className={classes.header}>
-                        {createdBy && <span className={cx(classes.headerText, classes.name)}>
-                            {createdBy.firstName} {' '} {createdBy.surname}
-                        </span>}
-                        <span className={cx(classes.headerText, classes.lastUpdated)}>
-                            <Tooltip content={convertClientToList(formattedDate, dataElementTypes.DATETIME)}>
-                                {moment(formattedDate).fromNow()}
-                            </Tooltip>
-                        </span>
-                    </div>
-                    <div className={classes.body}>
-                        <Parser>{value}</Parser>
-                    </div>
+    const NoteItem = ({ value, storedAt, createdBy }) => (
+        <div data-test="note-item" className={cx(classes.item)}>
+            {/* TODO: add avatar */}
+            <div className={classes.rightColumn}>
+                <div className={classes.header}>
+                    {createdBy && <span className={cx(classes.headerText, classes.name)}>
+                        {createdBy.firstName} {' '} {createdBy.surname}
+                    </span>}
+                    <span className={cx(classes.headerText, classes.lastUpdated)}>
+                        <Tooltip content={convertClientToList(fromServerDate(storedAt), dataElementTypes.DATETIME)}>
+                            {moment(fromServerDate(storedAt)).fromNow()}
+                        </Tooltip>
+                    </span>
+                </div>
+                <div className={classes.body}>
+                    <Parser>{value}</Parser>
                 </div>
             </div>
-        );
-    };
+        </div>
+    );
 
 
     return (
diff --git a/src/core_modules/capture-core/flow/typeDeclarations.js b/src/core_modules/capture-core/flow/typeDeclarations.js
index 44d8d4f9ce..ad0065f3b7 100644
--- a/src/core_modules/capture-core/flow/typeDeclarations.js
+++ b/src/core_modules/capture-core/flow/typeDeclarations.js
@@ -1,5 +1,6 @@
 // @flow
 import type { QuerySingleResource } from '../utils/api/api.types';
+import type { DHIS2Date } from '@dhis2/app-runtime';
 
 declare type D2 = {
     models: Object,
@@ -203,6 +204,7 @@ declare type ApiUtilsWithoutHistory = {|
     mutate: DataEngineMutate,
     absoluteApiPath: string,
     serverVersion: { minor: number },
+    fromClientDate: (date?: string | Date | number | null) => DHIS2Date,
 |}
 
 declare type ApiUtils = {|
@@ -214,4 +216,5 @@ declare type ApiUtils = {|
         push: () => void,
         ...PassOnProps,
     },
+    fromClientDate: (date?: string | Date | number | null) => DHIS2Date,
 |};

From 5581ce5d65f6acd4e7df81c7a3bad10005393305 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joakim=20Storl=C3=B8kken=20Melseth?= <joakim@dhis2.org>
Date: Fri, 14 Feb 2025 18:00:27 +0100
Subject: [PATCH 40/42] fix: calendar locale, moment locale, note client value
 format

---
 .../New/Fields/AgeField/AgeField.component.js          |  1 +
 .../DateAndTimeFields/DateField/DateField.component.js |  1 +
 .../DateRangeField/DateRangeField.component.js         |  2 ++
 .../DateTimeField/DateTimeField.component.js           |  2 ++
 .../DateTimeRangeField/DateTimeRangeField.component.js |  2 ++
 .../capture-core/components/Notes/Notes.component.js   |  2 +-
 .../components/WidgetNote/NoteSection/NoteSection.js   |  2 +-
 .../capture-core/converters/clientToList.js            | 10 ++--------
 .../capture-core/converters/clientToView.js            |  7 +------
 .../metaData/SystemSettings/SystemSettings.js          |  1 +
 .../systemSettings/cacheSystemSetttings.js             |  5 +++++
 .../utils/converters/date/convertIsoToLocalCalendar.js |  2 +-
 .../capture-ui/AgeField/AgeField.component.js          |  1 +
 .../DateAndTimeFields/DateField/Date.component.js      |  3 +++
 .../DateRangeField/DateRangeField.component.js         |  1 +
 .../DateTimeField/DateTime.component.js                |  3 ++-
 .../DateTimeRangeField/DateTimeRange.component.js      |  1 +
 17 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
index c160c34475..ea0b0f53b6 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/AgeField/AgeField.component.js
@@ -49,6 +49,7 @@ const AgeFieldPlain = (props: Props) => {
         // $FlowFixMe[cannot-spread-inexact] automated comment
         <UIAgeField
             datePlaceholder={systemSettingsStore.get().dateFormat.toLowerCase()}
+            locale={systemSettingsStore.get().uiLocale}
             {...passOnProps}
         />
     );
diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateField/DateField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateField/DateField.component.js
index 8ed4bf9b8c..9bb08ad56f 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateField/DateField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateField/DateField.component.js
@@ -37,6 +37,7 @@ class DateFieldPlain extends React.Component<Props> {
             // $FlowFixMe[cannot-spread-inexact] automated comment
             <UIDateField
                 placeholder={systemSettingsStore.get().dateFormat.toLowerCase()}
+                locale={systemSettingsStore.get().uiLocale}
                 {...passOnProps}
             />
         );
diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateRangeField/DateRangeField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateRangeField/DateRangeField.component.js
index 496d330d03..5dcb8217a3 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateRangeField/DateRangeField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateRangeField/DateRangeField.component.js
@@ -2,6 +2,7 @@
 import * as React from 'react';
 import { withStyles, withTheme } from '@material-ui/core/styles';
 import { DateRangeField as UIDateRangeField } from 'capture-ui';
+import { systemSettingsStore } from '../../../../../../metaDataMemoryStores';
 
 const getStyles = (theme: Theme) => ({
     innerInputError: {
@@ -44,6 +45,7 @@ const DateRangeFieldPlain = (props: Props) => {
     return (
         <UIDateRangeField
             {...passOnProps}
+            locale={systemSettingsStore.get().uiLocale}
         />
     );
 };
diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeField/DateTimeField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeField/DateTimeField.component.js
index a8d7a72d82..ca751a3c12 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeField/DateTimeField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeField/DateTimeField.component.js
@@ -2,6 +2,7 @@
 import * as React from 'react';
 import { withStyles, withTheme } from '@material-ui/core/styles';
 import { DateTimeField as UIDateTimeField } from 'capture-ui';
+import { systemSettingsStore } from '../../../../../../metaDataMemoryStores';
 
 const getStyles = (theme: Theme) => ({
     innerInputError: {
@@ -36,6 +37,7 @@ class DateTimeFieldPlain extends React.Component<Props> {
         return (
             <UIDateTimeField
                 {...passOnProps}
+                locale={systemSettingsStore.get().uiLocale}
             />
         );
     }
diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeRangeField/DateTimeRangeField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeRangeField/DateTimeRangeField.component.js
index 852b33525f..3b8d9abbed 100644
--- a/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeRangeField/DateTimeRangeField.component.js
+++ b/src/core_modules/capture-core/components/FormFields/New/Fields/DateAndTimeFields/DateTimeRangeField/DateTimeRangeField.component.js
@@ -2,6 +2,7 @@
 import * as React from 'react';
 import { withStyles, withTheme } from '@material-ui/core/styles';
 import { DateTimeRangeField as UIDateTimeRangeField } from 'capture-ui';
+import { systemSettingsStore } from '../../../../../../metaDataMemoryStores';
 
 const getStyles = (theme: Theme) => ({
     innerInputError: {
@@ -36,6 +37,7 @@ class DateTimeRangeFieldPlain extends React.Component<Props> {
         return (
             <UIDateTimeRangeField
                 {...passOnProps}
+                locale={systemSettingsStore.get().uiLocale}
             />
         );
     }
diff --git a/src/core_modules/capture-core/components/Notes/Notes.component.js b/src/core_modules/capture-core/components/Notes/Notes.component.js
index 1f1174accb..9895d0ca03 100644
--- a/src/core_modules/capture-core/components/Notes/Notes.component.js
+++ b/src/core_modules/capture-core/components/Notes/Notes.component.js
@@ -199,7 +199,7 @@ const NotesPlain = ({
                                     </div>
                                     <div className={classes.noteItemDate} data-test="note-date">
                                         <span>
-                                            <Tooltip content={convertClientToList(fromServerDate(n.storedAt), dataElementTypes.DATETIME)}>
+                                            <Tooltip content={convertClientToList(moment(fromServerDate(n.storedAt).getClientZonedISOString()).toISOString(), dataElementTypes.DATETIME)}>
                                                 {moment(fromServerDate(n.storedAt)).fromNow()}
                                             </Tooltip>
                                         </span>
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
index b800cacc28..91d18f4c3e 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
@@ -108,7 +108,7 @@ const NoteSectionPlain = ({
                         {createdBy.firstName} {' '} {createdBy.surname}
                     </span>}
                     <span className={cx(classes.headerText, classes.lastUpdated)}>
-                        <Tooltip content={convertClientToList(fromServerDate(storedAt), dataElementTypes.DATETIME)}>
+                        <Tooltip content={convertClientToList(moment(fromServerDate(storedAt).getClientZonedISOString()).toISOString(), dataElementTypes.DATETIME)}>
                             {moment(fromServerDate(storedAt)).fromNow()}
                         </Tooltip>
                     </span>
diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js
index 18359666b7..5f7f347fa7 100644
--- a/src/core_modules/capture-core/converters/clientToList.js
+++ b/src/core_modules/capture-core/converters/clientToList.js
@@ -15,17 +15,12 @@ function convertDateForListDisplay(rawValue: string): string {
 }
 
 function convertDateTimeForListDisplay(rawValue: string): string {
-    const momentDate = moment(rawValue);
+    const momentDate = moment(rawValue).locale('en');
     const timeString = momentDate.format('HH:mm');
     const localDate = convertIsoToLocalCalendar(rawValue);
     return `${localDate} ${timeString}`;
 }
 
-function convertTimeForListDisplay(rawValue: string): string {
-    const momentDate = moment(rawValue, 'HH:mm', true);
-    return momentDate.format('HH:mm');
-}
-
 type FileClientValue = {
     name: string,
     url: string,
@@ -122,8 +117,7 @@ const valueConvertersForType = {
     [dataElementTypes.PERCENTAGE]: (value: number) => `${stringifyNumber(value)} %`,
     [dataElementTypes.POLYGON]: convertPolygonForDisplay,
     [dataElementTypes.STATUS]: convertStatusForDisplay,
-    [dataElementTypes.TIME]: convertTimeForListDisplay,
-    [dataElementTypes.TIME_RANGE]: value => convertRangeForDisplay(convertTimeForListDisplay, value),
+    [dataElementTypes.TIME_RANGE]: value => convertRangeForDisplay(undefined, value),
     [dataElementTypes.TRUE_ONLY]: () => i18n.t('Yes'),
 };
 
diff --git a/src/core_modules/capture-core/converters/clientToView.js b/src/core_modules/capture-core/converters/clientToView.js
index 68c7bed02c..24530798c0 100644
--- a/src/core_modules/capture-core/converters/clientToView.js
+++ b/src/core_modules/capture-core/converters/clientToView.js
@@ -14,17 +14,13 @@ function convertDateForView(rawValue: string): string {
     return convertIsoToLocalCalendar(rawValue);
 }
 function convertDateTimeForView(rawValue: string): string {
-    const momentDate = moment(rawValue);
+    const momentDate = moment(rawValue).locale('en');
     const timeString = momentDate.format('HH:mm');
 
     const localDate = convertIsoToLocalCalendar(rawValue);
     return `${localDate} ${timeString}`;
 }
 
-function convertTimeForView(rawValue: string): string {
-    const momentDate = moment(rawValue, 'HH:mm', true);
-    return momentDate.format('HH:mm');
-}
 type FileClientValue = {
     name: string,
     url: string,
@@ -67,7 +63,6 @@ const valueConvertersForType = {
     [dataElementTypes.PERCENTAGE]: (value: number) => `${stringifyNumber(value)} %`,
     [dataElementTypes.DATE]: convertDateForView,
     [dataElementTypes.DATETIME]: convertDateTimeForView,
-    [dataElementTypes.TIME]: convertTimeForView,
     [dataElementTypes.TRUE_ONLY]: () => i18n.t('Yes'),
     [dataElementTypes.BOOLEAN]: (rawValue: boolean) => (rawValue ? i18n.t('Yes') : i18n.t('No')),
     [dataElementTypes.COORDINATE]: MinimalCoordinates,
diff --git a/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js b/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
index ec8334b437..5e481a3239 100644
--- a/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
+++ b/src/core_modules/capture-core/metaData/SystemSettings/SystemSettings.js
@@ -5,4 +5,5 @@ export class SystemSettings {
     dir: string;
     trackerAppRelativePath: string;
     calendar: string;
+    uiLocale: string;
 }
diff --git a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
index 308f785765..e09850360d 100644
--- a/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
+++ b/src/core_modules/capture-core/metaDataStoreLoaders/systemSettings/cacheSystemSetttings.js
@@ -17,6 +17,11 @@ export async function cacheSystemSettings(
             id: 'dateFormat',
             value: systemSettings.dateFormat.toUpperCase(),
         },
+        // This is a user setting, and both this and the dir property below should be placed somewhere else. Will do this in https://dhis2.atlassian.net/browse/DHIS2-19015.
+        {
+            id: 'uiLocale',
+            value: uiLocale,
+        },
         {
             id: 'dir',
             value: isLangRTL(uiLocale) ? 'rtl' : 'ltr',
diff --git a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
index fa09f77c53..eb2fdbce78 100644
--- a/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
+++ b/src/core_modules/capture-core/utils/converters/date/convertIsoToLocalCalendar.js
@@ -18,7 +18,7 @@ export function convertIsoToLocalCalendar(isoDate: ?string): string {
         return '';
     }
 
-    const momentDate = moment(isoDate);
+    const momentDate = moment(isoDate).locale('en');
     if (!momentDate.isValid()) {
         return '';
     }
diff --git a/src/core_modules/capture-ui/AgeField/AgeField.component.js b/src/core_modules/capture-ui/AgeField/AgeField.component.js
index a772560e5b..133eeaf928 100644
--- a/src/core_modules/capture-ui/AgeField/AgeField.component.js
+++ b/src/core_modules/capture-ui/AgeField/AgeField.component.js
@@ -57,6 +57,7 @@ type Props = {
     disabled?: ?boolean,
     dateFormat: ?string,
     calendarType: ?string,
+    locale?: string,
 };
 
 function getCalculatedValues(dateValue: ?string, calendarType: string, dateFormat: string): AgeValues {
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
index 8fa5d01b6e..05b5e6897b 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateField/Date.component.js
@@ -23,6 +23,7 @@ type Props = {
     innerMessage?: any,
     dateFormat: ?string,
     calendarType: ?string,
+    locale?: string,
 };
 
 type Validation = {|
@@ -67,6 +68,7 @@ export class DateField extends React.Component<Props, State> {
             innerMessage,
             calendarType,
             dateFormat,
+            locale,
         } = this.props;
         const calculatedInputWidth = inputWidth || width;
         const calculatedCalendarWidth = calendarWidth || width;
@@ -97,6 +99,7 @@ export class DateField extends React.Component<Props, State> {
                     disabled={this.props.disabled}
                     {...errorProps}
                     maxDate={calendarMax}
+                    locale={locale}
                 />
             </div>
         );
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
index 884a9eb8c2..23045c3865 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateRangeField/DateRangeField.component.js
@@ -26,6 +26,7 @@ type Props = {
     onChange: (value: ?DateRangeValue) => void,
     classes: Object,
     innerMessage?: ?Object,
+    locale?: string,
 }
 
 const inputKeys = {
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeField/DateTime.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeField/DateTime.component.js
index a97077e1ff..4d2c5f282c 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeField/DateTime.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeField/DateTime.component.js
@@ -23,7 +23,8 @@ type Props = {
     classes: Object,
     dateLabel: string,
     timeLabel: string,
-    innerMessage: Object
+    innerMessage: Object,
+    locale?: string,
 };
 
 type State = {
diff --git a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
index e16a915a59..1c604336a2 100644
--- a/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
+++ b/src/core_modules/capture-ui/DateAndTimeFields/DateTimeRangeField/DateTimeRange.component.js
@@ -25,6 +25,7 @@ type Props = {
     value: DateTimeRangeValue,
     onBlur: (value: ?DateTimeRangeValue, options: Object) => void,
     onChange: (value: ?DateTimeRangeValue) => void,
+    locale?: string,
 };
 
 type State = {

From 77813733cd6ee2cbe171dce7344317278f6928e6 Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Tue, 18 Feb 2025 10:14:47 +0200
Subject: [PATCH 41/42] fix: remove unused updateAt

---
 .../WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js           | 1 -
 .../components/WidgetEventNote/WidgetEventNote.epics.js          | 1 -
 2 files changed, 2 deletions(-)

diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
index 46e60fc834..87d308c9ba 100644
--- a/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEnrollmentNote/WidgetEnrollmentNote.epics.js
@@ -40,7 +40,6 @@ export const addNoteForEnrollmentEpic = (action$: InputObservable, store: ReduxS
                         surname,
                         uid: clientId,
                     },
-                    updatedAt: moment().toISOString(),
                     storedBy: userName,
                     storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                 };
diff --git a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
index 69be90b443..843f59d51c 100644
--- a/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
+++ b/src/core_modules/capture-core/components/WidgetEventNote/WidgetEventNote.epics.js
@@ -50,7 +50,6 @@ export const addNoteForEventEpic = (action$: InputObservable, store: ReduxStore,
                         surname,
                         uid: clientId,
                     },
-                    lastUpdated: moment().toISOString(),
                     storedBy: userName,
                     storedAt: fromClientDate(moment().toISOString()).getServerZonedISOString(),
                 };

From 2318917cb362bd544733430a7fe0b2c5b5f8adde Mon Sep 17 00:00:00 2001
From: alaa-yahia <alaay873@live.com>
Date: Wed, 26 Feb 2025 13:26:15 +0200
Subject: [PATCH 42/42] fix: runtime error when rules run

---
 src/core_modules/capture-core/converters/formToClient.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/core_modules/capture-core/converters/formToClient.js b/src/core_modules/capture-core/converters/formToClient.js
index 187549d140..446f53a0d1 100644
--- a/src/core_modules/capture-core/converters/formToClient.js
+++ b/src/core_modules/capture-core/converters/formToClient.js
@@ -36,7 +36,11 @@ function convertDateTime(formValue: DateTimeValue): ?string {
 }
 
 function convertDate(dateValue: string) {
-    return convertLocalToIsoCalendar(dateValue);
+    try {
+        return convertLocalToIsoCalendar(dateValue);
+    } catch (error) {
+        return '';
+    }
 }
 
 function convertTime(timeValue: string) {