diff --git a/i18n/es.po b/i18n/es.po index 5246fed..c04bd60 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-05-23T16:02:03.207Z\n" +"POT-Creation-Date: 2024-05-22T13:24:01.336Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/package.json b/package.json index ef4bfd2..e6ed1d9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "amr-surveys", "description": "AMR Surveys App", - "version": "0.2.0", + "version": "0.5.0", "license": "GPL-3.0", "author": "EyeSeeTea team", "homepage": ".", diff --git a/src/CompositionRoot.ts b/src/CompositionRoot.ts index 34180fd..f9fcfb9 100644 --- a/src/CompositionRoot.ts +++ b/src/CompositionRoot.ts @@ -38,6 +38,7 @@ import { GetASTGuidelinesUseCase } from "./domain/usecases/GetASTGuidelinesUseCa import { ASTGuidelinesD2Repository } from "./data/repositories/ASTGuidelinesD2Repository"; import { ASTGuidelinesTestRepository } from "./data/repositories/testRepositories/ASTGuidelinesTestRepository"; import { GetSurveyAntibioticsBlacklistUseCase } from "./domain/usecases/GetSurveyAntibioticsBlacklistUseCase"; +import { RemoveRepeatableProgramStageUseCase } from "./domain/usecases/RemoveRepeatableProgramStageUseCase"; export type CompositionRoot = ReturnType; @@ -91,6 +92,9 @@ function getCompositionRoot(repositories: Repositories) { getSurveyAntibioticsBlacklist: new GetSurveyAntibioticsBlacklistUseCase( repositories.surveyFormRepository ), + removeRepeatableStage: new RemoveRepeatableProgramStageUseCase( + repositories.surveyFormRepository + ), }, astGuidelines: { getGuidelines: new GetASTGuidelinesUseCase(repositories.astGuidelinesRepository), diff --git a/src/data/repositories/SurveyFormD2Repository.ts b/src/data/repositories/SurveyFormD2Repository.ts index d987fd6..b7ff161 100644 --- a/src/data/repositories/SurveyFormD2Repository.ts +++ b/src/data/repositories/SurveyFormD2Repository.ts @@ -27,7 +27,7 @@ import { AMR_SURVEYS_PREVALENCE_DEA_CUSTOM_AST_GUIDE, AMR_SURVEYS_PREVALENCE_DEA_AST_GUIDELINES, } from "../entities/D2Survey"; -import { ProgramMetadata } from "../entities/D2Program"; +import { ProgramDataElement, ProgramMetadata } from "../entities/D2Program"; import { mapProgramToQuestionnaire, mapQuestionnaireToEvent, @@ -60,6 +60,16 @@ export class SurveyD2Repository implements SurveyRepository { const programDataElements = resp.programStageDataElements.map( psde => psde.dataElement ); + const dataElementsWithSortOrder: ProgramDataElement[] = resp.dataElements.map( + de => { + return { + ...de, + sortOrder: resp.programStageDataElements.find( + psde => psde.dataElement.id === de.id + )?.sortOrder, + }; + } + ); const sortedTrackedentityAttr = resp.programTrackedEntityAttributes ? _( @@ -93,7 +103,7 @@ export class SurveyD2Repository implements SurveyRepository { undefined, trackedEntity, programDataElements, - resp.dataElements, + dataElementsWithSortOrder, sortedOptions, resp.programStages, resp.programStageSections, @@ -119,7 +129,7 @@ export class SurveyD2Repository implements SurveyRepository { event, undefined, programDataElements, - resp.dataElements, + dataElementsWithSortOrder, resp.options, resp.programStages, resp.programStageSections, @@ -143,7 +153,7 @@ export class SurveyD2Repository implements SurveyRepository { undefined, undefined, programDataElements, - resp.dataElements, + dataElementsWithSortOrder, resp.options, resp.programStages, resp.programStageSections, diff --git a/src/data/repositories/testRepositories/SurveyFormTestRepository.ts b/src/data/repositories/testRepositories/SurveyFormTestRepository.ts index da52c00..9e91d89 100644 --- a/src/data/repositories/testRepositories/SurveyFormTestRepository.ts +++ b/src/data/repositories/testRepositories/SurveyFormTestRepository.ts @@ -11,6 +11,9 @@ import { ASTGUIDELINE_TYPES } from "../../../domain/entities/ASTGuidelines"; import { SurveyChildCountType } from "../../utils/surveyChildCountHelper"; export class SurveyTestRepository implements SurveyRepository { + deleteEventSurvey(_eventId: string, _orgUnitId: string, _programId: string): FutureData { + throw new Error("Method not implemented."); + } getSurveyNameAndASTGuidelineFromId( id: string, surveyFormType: SURVEY_FORM_TYPES diff --git a/src/data/utils/questionHelper.ts b/src/data/utils/questionHelper.ts index 29ea326..5ad546a 100644 --- a/src/data/utils/questionHelper.ts +++ b/src/data/utils/questionHelper.ts @@ -288,6 +288,7 @@ export const mapProgramDataElementToQuestions = ( }) ) .compact() + .sortBy(q => q.sortOrder) .value(); return questions; diff --git a/src/data/utils/surveyFormMappers.ts b/src/data/utils/surveyFormMappers.ts index 47b135b..adbc462 100644 --- a/src/data/utils/surveyFormMappers.ts +++ b/src/data/utils/surveyFormMappers.ts @@ -212,7 +212,7 @@ const getRepeatedStageEvents = ( dataElements: ProgramDataElement[], options: Option[], programStageSections?: ProgramStageSection[] -) => { +): QuestionnaireStage[] | undefined => { const repeatedStageEvents = trackedEntity?.enrollments ?.at(0) ?.events.filter(e => e.programStage === stage.id); @@ -254,7 +254,7 @@ const getRepeatedStageEvents = ( instanceId: repeatedStageEvt.event, sortOrder: stage.sortOrder, repeatable: stage.repeatable, - userAdded: index === 0 ? false : true, + isAddedByUser: index === 0 ? false : true, }; }); }; diff --git a/src/domain/entities/AMRSurveyModule.ts b/src/domain/entities/AMRSurveyModule.ts index 373a87e..ff1e819 100644 --- a/src/domain/entities/AMRSurveyModule.ts +++ b/src/domain/entities/AMRSurveyModule.ts @@ -4,7 +4,7 @@ export type SURVEY_TYPE = "NationalSurvey" | "HospitalSurvey" | "SupranationalSu type UserGroups = { captureAccess: NamedRef[]; readAccess: NamedRef[]; adminAccess: NamedRef[] }; -type SurveyRuleType = "HIDEFIELD" | "HIDESECTION"; +type SurveyRuleType = "HIDEFIELD" | "HIDESECTION" | "HIDESTAGE"; type Rule = { id: Id; diff --git a/src/domain/entities/Questionnaire/Questionnaire.ts b/src/domain/entities/Questionnaire/Questionnaire.ts index 3d9ee52..d87fd11 100644 --- a/src/domain/entities/Questionnaire/Questionnaire.ts +++ b/src/domain/entities/Questionnaire/Questionnaire.ts @@ -174,6 +174,9 @@ export class Questionnaire { const updatedStages = questionnaire.stages.map(stage => { return { ...stage, + isVisible: !surveyRule.rules.find(rule => + rule.toHide?.find(ruleStage => ruleStage === stage.id) + ), sections: stage.sections.map(section => { const currentSectionRule = surveyRule.rules.find(rule => rule.toHide?.find(de => de === section.code) @@ -208,6 +211,29 @@ export class Questionnaire { updatedStages ); + const hideEntityQuestionRule = surveyRule.rules.find( + rule => + rule.type === "HIDEFIELD" && + rule.toHide.find(id => + updatedQuestionnaire.entity?.questions.find(q => q.id === id) + ) + ); + if (hideEntityQuestionRule && questionnaire.entity) { + const updatedEntityQuestions: Question[] = questionnaire.entity.questions.map( + question => { + return { + ...question, + isVisible: hideEntityQuestionRule.toHide.find(id => id === question.id) + ? false + : true, + }; + } + ); + + const updatedEntity = { ...questionnaire.entity, questions: updatedEntityQuestions }; + return Questionnaire.updateQuestionnaireEntity(updatedQuestionnaire, updatedEntity); + } + return updatedQuestionnaire; } diff --git a/src/domain/repositories/SurveyRepository.ts b/src/domain/repositories/SurveyRepository.ts index 06e8721..b771163 100644 --- a/src/domain/repositories/SurveyRepository.ts +++ b/src/domain/repositories/SurveyRepository.ts @@ -32,6 +32,7 @@ export interface SurveyRepository { ): FutureData; deleteSurvey(id: Id, orgUnitId: Id, programId: Id): FutureData; + deleteEventSurvey(eventId: Id, orgUnitId: Id, programId: Id): FutureData; getSurveyNameAndASTGuidelineFromId( id: Id, diff --git a/src/domain/usecases/RemoveRepeatableProgramStageUseCase.ts b/src/domain/usecases/RemoveRepeatableProgramStageUseCase.ts new file mode 100644 index 0000000..db9b9ec --- /dev/null +++ b/src/domain/usecases/RemoveRepeatableProgramStageUseCase.ts @@ -0,0 +1,28 @@ +import { FutureData } from "../../data/api-futures"; +import { PREVALENCE_FACILITY_LEVEL_FORM_ID } from "../../data/entities/D2Survey"; +import { Questionnaire } from "../entities/Questionnaire/Questionnaire"; +import { Future } from "../entities/generic/Future"; +import { SurveyRepository } from "../repositories/SurveyRepository"; + +export class RemoveRepeatableProgramStageUseCase { + constructor(private surveyRepository: SurveyRepository) {} + + execute(questionnaire: Questionnaire, stageId: string): FutureData { + //Repeatable Program Stages are only applicable to Prevalence Facility forms + + const eventId = questionnaire.stages.find(stage => stage.id === stageId)?.instanceId; + + if (!eventId) + return Future.error(new Error("Cannot find event Id correspoding to the stage")); + + return this.surveyRepository + .deleteEventSurvey(eventId, questionnaire.orgUnit.id, PREVALENCE_FACILITY_LEVEL_FORM_ID) + .flatMap(() => { + const updatedQuestionnaire = Questionnaire.removeProgramStage( + questionnaire, + stageId + ); + return Future.success(updatedQuestionnaire); + }); + } +} diff --git a/src/domain/usecases/SaveFormDataUseCase.ts b/src/domain/usecases/SaveFormDataUseCase.ts index f01a93b..b8b55a4 100644 --- a/src/domain/usecases/SaveFormDataUseCase.ts +++ b/src/domain/usecases/SaveFormDataUseCase.ts @@ -34,7 +34,7 @@ export class SaveFormDataUseCase { surveyFormType === "PPSSurveyForm" && orgUnitId === "" ? GLOBAL_OU_ID : orgUnitId; //Do not allow creation of multiple Prevalence Facility Level Forms for the same facility. - if (surveyFormType === "PrevalenceFacilityLevelForm") { + if (!eventId && surveyFormType === "PrevalenceFacilityLevelForm") { return this.surveyReporsitory .getSurveys(surveyFormType, programId, ouId, false) .flatMap(surveys => { diff --git a/src/webapp/components/survey-questions/QuestionWidget.tsx b/src/webapp/components/survey-questions/QuestionWidget.tsx index 30357bb..bce10e3 100644 --- a/src/webapp/components/survey-questions/QuestionWidget.tsx +++ b/src/webapp/components/survey-questions/QuestionWidget.tsx @@ -6,7 +6,6 @@ import TextWidget from "./widgets/TextWidget"; import YesNoWidget from "./widgets/YesNoWidget"; import DatePickerWidget from "./widgets/DatePickerWidget"; import { Maybe, assertUnreachable } from "../../../utils/ts-utils"; -import DropdownSelectWidget from "./widgets/DropdownSelectWidget"; import DateTimePickerWidget from "./widgets/DateTimePickerWidget"; import SearchableSelect from "./widgets/SearchableSelect"; import { @@ -28,18 +27,7 @@ export const QuestionWidget: React.FC = React.memo(props => switch (type) { case "select": { - if (question.options.length > 5 && question.options.length < 10) { - return ( - ) => - onChange(update(question, value)) - } - disabled={disabled} - /> - ); - } else if (question.options.length > 10) { + if (question.options.length > 5) { return ( op.id === question.value?.id) || null} diff --git a/src/webapp/components/survey-questions/widgets/DatePickerWidget.tsx b/src/webapp/components/survey-questions/widgets/DatePickerWidget.tsx index 2bcb511..8922de3 100644 --- a/src/webapp/components/survey-questions/widgets/DatePickerWidget.tsx +++ b/src/webapp/components/survey-questions/widgets/DatePickerWidget.tsx @@ -30,6 +30,7 @@ const DatePickerWidget: React.FC = props => { value={stateValue} disabled={props.disabled} onChange={newValue => notifyChange(newValue)} + format="dd-MM-yyyy" /> ); diff --git a/src/webapp/components/survey-questions/widgets/DateTimePickerWidget.tsx b/src/webapp/components/survey-questions/widgets/DateTimePickerWidget.tsx index ba4ad78..8f53cb5 100644 --- a/src/webapp/components/survey-questions/widgets/DateTimePickerWidget.tsx +++ b/src/webapp/components/survey-questions/widgets/DateTimePickerWidget.tsx @@ -32,6 +32,7 @@ const DateTimePickerWidget: React.FC = props => { disabled={props.disabled} onChange={newValue => notifyChange(newValue?.toISOString() ?? "")} ampm={false} + format="dd-MM-yyyy HH:mm" /> ); diff --git a/src/webapp/components/survey/hook/useSurveyForm.ts b/src/webapp/components/survey/hook/useSurveyForm.ts index 274a805..10b68d9 100644 --- a/src/webapp/components/survey/hook/useSurveyForm.ts +++ b/src/webapp/components/survey/hook/useSurveyForm.ts @@ -200,14 +200,20 @@ export function useSurveyForm( const removeProgramStage = useCallback( (stageId: Id) => { if (questionnaire) { - const updatedQuestionnaire = Questionnaire.removeProgramStage( - questionnaire, - stageId + setLoading(true); + compositionRoot.surveys.removeRepeatableStage.execute(questionnaire, stageId).run( + updatedQuestionnaire => { + setQuestionnaire(updatedQuestionnaire); + setLoading(false); + }, + err => { + setLoading(false); + setError(err.message); + } ); - setQuestionnaire(updatedQuestionnaire); } }, - [questionnaire] + [compositionRoot.surveys, questionnaire] ); return { diff --git a/src/webapp/pages/survey-list/useRedirectHome.ts b/src/webapp/pages/survey-list/useRedirectHome.ts index 5bba275..dab11fc 100644 --- a/src/webapp/pages/survey-list/useRedirectHome.ts +++ b/src/webapp/pages/survey-list/useRedirectHome.ts @@ -9,7 +9,6 @@ export function useRedirectHome() { const { currentPPSSurveyForm, currentPrevalenceSurveyForm, - currentCountryQuestionnaire, currentHospitalForm, currentWardRegister, currentFacilityLevelForm, @@ -30,9 +29,6 @@ export function useRedirectHome() { if ( (formType === "PPSCountryQuestionnaire" && !currentPPSSurveyForm) || - (hasAdminAccess && - formType === "PPSHospitalForm" && - !currentCountryQuestionnaire) || (formType === "PPSWardRegister" && !currentHospitalForm) || (formType === "PPSPatientRegister" && !currentWardRegister) || (hasAdminAccess && @@ -52,7 +48,6 @@ export function useRedirectHome() { currentModule, userGroups, currentPPSSurveyForm, - currentCountryQuestionnaire, currentHospitalForm, currentWardRegister, currentPrevalenceSurveyForm,