Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [DHIS2-18310] enable non-Gregorian calendars in views & lists & forms #3900

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

alaa-yahia
Copy link
Member

@alaa-yahia alaa-yahia commented Dec 5, 2024

Implements DHIS2-18310 & DHIS2-18311 & DHIS2-18309

  • Modified clientToList, clientToForm and clientToView converters to handle local format conversions (DHIS2-18310)
  • Modified FormToClient converter to handle ISO format (DHIS2-18309)
  • Ensure that all “Last updated” calculations are working when local calendar is used
  • Ensure all date pickers (form and working list) are using local calendar.
  • Enable non-Gregorian calendars in working list filters DHIS2-18311

@alaa-yahia alaa-yahia requested a review from a team as a code owner December 5, 2024 00:21
@alaa-yahia alaa-yahia force-pushed the DHIS2-18310-enable-non-gregorian-calendars-in-views-and-lists branch from 8983fd7 to 0294f8d Compare December 5, 2024 11:00
@simonadomnisoru
Copy link
Contributor

Hi @alaa-yahia,
I noticed two issues with the Age field:

  1. When selecting a date from the calendar, the year, months, and days are filled with incorrect values. (i.e. In the video below, I selected today's date from the Ethiopian calendar, so the year, months, and day should have been filled with 0).
  2. When typing a value into the days field, the calendar is filled with the gregorian value of the date.
Screen.Recording.2024-12-12.at.16.51.40.mov

In the video above I used the Ethiopian calendar. Can you have a look? Thank you!

Copy link
Member

@JoakimSM JoakimSM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good start, but some things we will have to work a bit more on.

In addition to the individual comments we should convert the "last updated" tooltips from ISO to local calendar (I saw this in both the stages and events Widget and the enrollment Widget). Had a very quick look at the stages and events Widget and for now I think you can use something like moment(fromServerDate(updatedAt)).toISOString() and put the result into the clientToView converter of type DATETIME (we should probably also use the serverToClient converter, but this will be checked and resolved in separate ticket)

@alaa-yahia alaa-yahia force-pushed the DHIS2-18310-enable-non-gregorian-calendars-in-views-and-lists branch from da9bf63 to 013db2c Compare January 6, 2025 11:22
Copy link
Member

@JoakimSM JoakimSM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to other comments:

  • We should fix the logic in getDateRangeValidator and getDateTimeRangeValidator. These validators are being used in the search form when using tracked entity attributes of value type date and dateTime. We should replace the parseDate logic and leverage the validation done in the ui library component.
  • Don't use parseDate on local dates. I think this means that we should remove parseDate from the codebase in its entirety (I didn't see any valid reason to use it anymore, but let me know if I missed something)

const years = now.diff(age, 'years');
age.add(years, 'years');
function getCalculatedValues(dateValue: ?string): AgeValues {
const calendar = systemSettingsStore.get().calendar;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One architectural principle that we have been trying to adhere to is that the capture-ui could be extracted and used in any app. I.e., it shouldn't retrieve data from app specific objects like we do here. Can we instead of this, pass in calendarType to the component?

@@ -10,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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the reasons mentioned in the comment on line 57; can we put these helper functions in capture-core-utils (capture-core-utils/date is probably fine for now) and pass in the calendarType and dateFormat instead of getting these values from systemSettingsStore internally in the functions? If needed, we can also create helper functions in the location where the helper functions currently are found, but these helpers shouldn't be invoked from files in capture-ui.

Comment on lines 20 to 25
const momentDate = moment(localDate);
if (!momentDate.isValid()) {
return '';
}

const formattedIsoDate = momentDate.format('YYYY-MM-DD');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use moment on our input here since the input is in the local calendar type.

@@ -36,7 +39,7 @@ function convertDateTime(formValue: DateTimeValue): ?string {
function convertDate(dateValue: string) {
const parsedDate = parseDate(dateValue);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside the parseDate function, the input date is being validated using moment and we shouldn't do that since the input is in the local calendar type. Looking into this a bit, there should be no need to validate the date in the converter here as this should be handled elsewhere. I.e., you can assume the date is valid at this point. So, using something like convertStringToTemporal and then passing the ISOstring of the returned Temporal to convertLocalToIsoCalendar could be the way to go (or alternatively convertLocalToIsoCalendar could handle the 'dd-mm-yyyy' format).

We should look into the dateTime converter as well and apply the same idea there.

const parseData = dateValue && onParseDate(dateValue);

function getCalculatedValues(dateValue: ?string): AgeValues {
const parseData = dateValue && parseDate(dateValue);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use the parseDate function as is because it is using moment internally (and we are passing in a date from a local calendar type). Let's use the following approach here:

  1. In handleDateBlur in this file, let's make sure we check the validation that is passed in from the component and only invoke getCalculatedVaules if the date is valid (currently, the app crashing sometimes with invalid dates). The validation executed in the component is the source of truth for whether the date is valid or not.
  2. Don't use parseDate here. After point .1, the value passed in to getCalculatedValues should always be valid and you should be able to use convertStringToTemporal directly.

In general, we should replace parseDate in our codebase if it is being used on dates of local calendar types.

Copy link
Contributor

@simonadomnisoru simonadomnisoru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @alaa-yahia, I found a few bugs that I hope you can look into. I tested with the Ethiopian and Nepalese calendars. Thank you!

const years = now.diff(age, 'years');
age.add(years, 'years');
function getCalculatedValues(dateValue: ?string, calendarType: ?string, dateFormat: ?string): AgeValues {
const now = Temporal.Now.plainDateISO().withCalendar(calendarType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a runtime error when selecting a date in the AGE field when using the ethiopian or the nepali calendar type.

Screenshot 2025-01-14 at 09 43 09

@@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event status label in the StagesAndEvents widget displayed the ISO date. Can you have a look?
Screenshot 2025-01-14 at 09 47 12
Thanks!

@@ -5,21 +5,20 @@ 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the search page, the currentSearchTerms are not converted correctly. I think the problem arises when using the clientToList converter in the SearchResultsHeader.component. In the screenshot bellow, the label Birth date: 05-05-2017 -> 07-05-2017 should be displayed.

Screenshot 2025-01-14 at 10 12 47

Comment on lines 110 to 117
<DateField
width={200}
value={selectedDate}
onBlur={dateChangeHandler}
label={dateLabel}
date={selectedDate}
dense
locale={locale}
onDateSelect={dateChangeHandler}
/>
Copy link
Contributor

@simonadomnisoru simonadomnisoru Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calendar for the enrollment date and incident date in the WidgetEnrollment component is in ISO format. After selecting a date, the value in the input field is also displayed in ISO format.

Screen.Recording.2025-01-14.at.10.58.47.mov

@alaa-yahia
Copy link
Member Author

@simonadomnisoru I've pushed bug fixes addressing your feedbacks. Could you take another look when you have a time?
Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants