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

validations: Prepare to move the InterviewStats component to typescript #878

Merged
merged 4 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/demo_survey/src/admin/app-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ setApplicationConfiguration<EvolutionApplicationConfiguration>({
sections: surveySections,
widgets: widgetsConfig as any,
allowedUrlFields: ['source', 'household.carNumber'],
projectHelpers,
projectHelpers: projectHelpers as any,
templateMapping: { ...appConfig.templateMapping, 'visitedPlaces': VisitedPlaceSection }
});

Expand Down
2 changes: 1 addition & 1 deletion example/demo_survey/src/app-survey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import VisitedPlaceSection from './survey/templates/VisitedPlacesSection';
setApplicationConfiguration<EvolutionApplicationConfiguration>({
sections: surveySections,
widgets: widgetsConfig as any,
projectHelpers,
projectHelpers: projectHelpers as any,
allowedUrlFields: ['source', 'household.carNumber'],
templateMapping: { ...appConfig.templateMapping, 'visitedPlaces': VisitedPlaceSection }
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2023, Polytechnique Montreal and contributors
*
* This file is licensed under the MIT License.
* License text available at https://opensource.org/licenses/MIT
*/
import React from 'react';
import { useTranslation } from 'react-i18next';
import InputText from 'chaire-lib-frontend/lib/components/input/InputText';
import { WithTranslation } from 'react-i18next';
import { UserInterviewAttributes } from 'evolution-common/lib/services/questionnaire/types';
import { useDispatch } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { SurveyAction } from '../../../store/survey';
import { RootState } from '../../../store/configureStore';
import { startUpdateSurveyValidateInterview } from '../../../actions/SurveyAdmin';

interface ValidationCommentFormProps extends WithTranslation {
interview: UserInterviewAttributes;
}

const ValidationCommentForm = ({ interview }: ValidationCommentFormProps) => {
const { t } = useTranslation('admin');
const dispatch = useDispatch<ThunkDispatch<RootState, unknown, SurveyAction>>();

const onValueChange = (e) => {
if (e && e.stopPropagation) {
e.stopPropagation();
}

const valuesByPath = {
'responses._validationComment': e.target.value
};
dispatch(
startUpdateSurveyValidateInterview(null, valuesByPath, null, null, () => {
/* parameter required, but nothing to do */
})
);
};

return (
<React.Fragment>
<label htmlFor={'surveyValidationComment'}>{t('admin:validatorComment')}</label>
<br />
<InputText
id={'surveyValidationComment'}
value={interview.responses._validationComment}
onValueChange={onValueChange}
/>
</React.Fragment>
);
};

export default ValidationCommentForm;
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
*/
import moment from 'moment';
import i18n from '../../../config/i18n.config';
import _cloneDeep from 'lodash/cloneDeep';

import { TFunction } from 'i18next';
import { getGenderedStrings, getFormattedDate, secondsSinceMidnightToTimeStrWithSuffix, validateButtonAction, validateButtonActionWithCompleteSection } from '../frontendHelper';
import { getGenderedStrings, getFormattedDate, secondsSinceMidnightToTimeStrWithSuffix, validateButtonAction, validateButtonActionWithCompleteSection, getVisitedPlaceDescription } from '../frontendHelper';
import { interviewAttributesForTestCases } from 'evolution-common/lib/tests/surveys';

jest.mock('../../../config/i18n.config', () => ({
Expand Down Expand Up @@ -217,3 +218,55 @@ describe('validateButtonActionWithCompleteSection', () => {

});

describe('getVisitedPlaceDescription', () => {
const interview = _cloneDeep(interviewAttributesForTestCases);
// Add times to places
const visitedPlaces = interview.responses.household!.persons!.personId1!.journeys!.journeyId1!.visitedPlaces!;

beforeEach(() => {
jest.clearAllMocks();
})

test('without times or html, place with no name', () => {
const description = getVisitedPlaceDescription(visitedPlaces.homePlace1P1, false, false);
expect(description).toEqual('survey:visitedPlace:activities:home');
});

test('without times or html, place with name', () => {
const description = getVisitedPlaceDescription(visitedPlaces.workPlace1P1, false, false);
expect(description).toEqual('survey:visitedPlace:activities:work • This is my work');
});

test('with times and html, complete place with no name', () => {
const visitedPlace = _cloneDeep(visitedPlaces.homePlace1P1);
visitedPlace.departureTime = 3600;
visitedPlace.arrivalTime = 1800;
const description = getVisitedPlaceDescription(visitedPlace, true, true);
expect(description).toEqual('survey:visitedPlace:activities:home 0:30 -> 1:00');
});

test('with times and html, complete place with no name, test midnight', () => {
const visitedPlace = _cloneDeep(visitedPlaces.homePlace1P1);
visitedPlace.departureTime = 3600;
visitedPlace.arrivalTime = 0;
const description = getVisitedPlaceDescription(visitedPlace, true, true);
expect(description).toEqual('survey:visitedPlace:activities:home 0:00 -> 1:00');
});

test('with times and html, complete place with name', () => {
const visitedPlace = _cloneDeep(visitedPlaces.workPlace1P1);
visitedPlace.departureTime = 3600;
visitedPlace.arrivalTime = 1800;
const description = getVisitedPlaceDescription(visitedPlace, true, true);
expect(description).toEqual('survey:visitedPlace:activities:work • <em>This is my work</em> 0:30 -> 1:00');
});

test('with times, but place with no times', () => {
const visitedPlace = _cloneDeep(visitedPlaces.workPlace1P1);
delete visitedPlace.departureTime;
delete visitedPlace.arrivalTime;
const description = getVisitedPlaceDescription(visitedPlace, true, true);
expect(description).toEqual('survey:visitedPlace:activities:work • <em>This is my work</em>');
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import moment from 'moment';

import i18n from '../../config/i18n.config';
import { secondsSinceMidnightToTimeStr } from 'chaire-lib-common/lib/utils/DateTimeUtils';
import { ButtonAction, Person } from 'evolution-common/lib/services/questionnaire/types';
import { ButtonAction, Person, VisitedPlace } from 'evolution-common/lib/services/questionnaire/types';

type GenderedData = {
[gender: string]: {
Expand Down Expand Up @@ -160,3 +160,32 @@ export const validateButtonActionWithCompleteSection: ButtonAction = (
}
});
};

/**
* Get the visited place description
* @param visitedPlace The visited place for which to get the description
* @param withTimes Whether to add the times to the description
* @param allowHtml Whether the description can contain HTML characters
* @returns
*/
export const getVisitedPlaceDescription = function (
visitedPlace: VisitedPlace,
withTimes: boolean = false,
allowHtml: boolean = true
): string {
let times = '';
if (withTimes) {
const arrivalTime =
visitedPlace.arrivalTime !== undefined ? ' ' + secondsSinceMidnightToTimeStr(visitedPlace.arrivalTime) : '';
const departureTime =
visitedPlace.departureTime !== undefined
? ' -> ' + secondsSinceMidnightToTimeStr(visitedPlace.departureTime)
: '';
times = arrivalTime + departureTime;
}
if (allowHtml) {
return `${i18n.t(`survey:visitedPlace:activities:${visitedPlace.activity}`)}${visitedPlace.name ? ` • <em>${visitedPlace.name}</em>` : ''}${times}`;
} else {
return `${i18n.t(`survey:visitedPlace:activities:${visitedPlace.activity}`)}${visitedPlace.name ? ` • ${visitedPlace.name}` : ''}${times}`;
}
};
Loading
Loading