From 8aa86956d8df15518a536b0dc51fa908cb9cb6db Mon Sep 17 00:00:00 2001 From: Tony Valle <79843014+superskip@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:57:17 +0200 Subject: [PATCH] fix: [DHIS2-17096] show/hide option groups in profile widget (#3593) --- .../WidgetProfile/WidgetProfile.component.js | 2 +- .../WidgetProfile/hooks/useApiProgram.js | 39 +++++++++++++ .../WidgetProfile/hooks/useOptionGroups.js | 58 +++++++++++++++++++ .../WidgetProfile/hooks/useProgram.js | 54 ++++++----------- 4 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 src/core_modules/capture-core/components/WidgetProfile/hooks/useApiProgram.js create mode 100644 src/core_modules/capture-core/components/WidgetProfile/hooks/useOptionGroups.js diff --git a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js index 66f14d57e5..2ad6516daf 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js +++ b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js @@ -88,7 +88,7 @@ const WidgetProfilePlain = ({ const error = programsError || trackedEntityInstancesError || userRolesError; const clientAttributesWithSubvalues = useClientAttributesWithSubvalues(teiId, program, trackedEntityInstanceAttributes); const teiDisplayName = useTeiDisplayName(program, storedAttributeValues, clientAttributesWithSubvalues, teiId); - const displayChangelog = supportsChangelog && program.trackedEntityType?.changelogEnabled; + const displayChangelog = supportsChangelog && program && program.trackedEntityType?.changelogEnabled; const displayInListAttributes = useMemo(() => clientAttributesWithSubvalues .filter(item => item.displayInList) diff --git a/src/core_modules/capture-core/components/WidgetProfile/hooks/useApiProgram.js b/src/core_modules/capture-core/components/WidgetProfile/hooks/useApiProgram.js new file mode 100644 index 0000000000..073ab42e40 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetProfile/hooks/useApiProgram.js @@ -0,0 +1,39 @@ +// @flow +import { useMemo } from 'react'; +import { useDataQuery } from '@dhis2/app-runtime'; + +const fields = + 'id,version,displayName,displayShortName,description,programType,style,minAttributesRequiredToSearch,enrollmentDateLabel,incidentDateLabel,featureType,selectEnrollmentDatesInFuture,selectIncidentDatesInFuture,displayIncidentDate,' + + 'access[*],' + + 'dataEntryForm[id,htmlCode],' + + 'categoryCombo[id,displayName,isDefault,categories[id,displayName]],' + + 'programIndicators[id,displayName,code,shortName,style,displayInForm,expression,displayDescription,description,filter,program[id]],' + + 'programSections[id, displayFormName, sortOrder, trackedEntityAttributes],' + + 'programRuleVariables[id,displayName,programRuleVariableSourceType,valueType,program[id],programStage[id],dataElement[id],trackedEntityAttribute[id],useCodeForOptionSet],' + + 'programStages[id,access,autoGenerateEvent,openAfterEnrollment,generatedByEnrollmentDate,reportDateToUse,minDaysFromStart,displayName,description,executionDateLabel,formType,featureType,validationStrategy,enableUserAssignment,style,' + + 'dataEntryForm[id,htmlCode],' + + 'programStageSections[id,displayName,displayDescription,sortOrder,dataElements[id]],' + + 'programStageDataElements[compulsory,displayInReports,renderOptionsAsRadio,allowFutureDate,renderType[*],dataElement[id,displayName,displayShortName,displayFormName,valueType,translations[*],description,optionSetValue,style,optionSet[id,displayName,version,valueType,options[id,displayName,code,style, translations]]]]' + + '],' + + 'programTrackedEntityAttributes[trackedEntityAttribute[id,displayName,displayShortName,displayFormName,description,valueType,optionSetValue,unique,orgunitScope,pattern,translations[property,locale,value],optionSet[id,displayName,version,valueType,options[id,displayName,name,code,style,translations]]],displayInList,searchable,mandatory,renderOptionsAsRadio,allowFutureDate],' + + 'trackedEntityType[id,access,displayName,allowAuditLog,minAttributesRequiredToSearch,featureType,trackedEntityTypeAttributes[trackedEntityAttribute[id],displayInList,mandatory,searchable],translations[property,locale,value]],' + + 'userRoles[id,displayName]'; + +export const useApiProgram = (programId: string) => { + const { error, loading, data } = useDataQuery( + useMemo( + () => ({ + programs: { + resource: 'programs', + id: programId, + params: { + fields, + }, + }, + }), + [programId], + ), + ); + + return { error, loading, program: data?.programs }; +}; diff --git a/src/core_modules/capture-core/components/WidgetProfile/hooks/useOptionGroups.js b/src/core_modules/capture-core/components/WidgetProfile/hooks/useOptionGroups.js new file mode 100644 index 0000000000..fe4533e6a5 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetProfile/hooks/useOptionGroups.js @@ -0,0 +1,58 @@ +// @flow +import { useMemo } from 'react'; +import { useApiMetadataQuery } from 'capture-core/utils/reactQueryHelpers'; + +const createOptionSetToOptionGroupDictionary = ({ optionGroups }) => optionGroups.reduce( + (acc, optionGroup) => { + const optionSetId = optionGroup.optionSet.id; + const transformedOptionGroup = { + id: optionGroup.id, + options: optionGroup.options.map(option => option.id), + }; + if (acc[optionSetId]) { + acc[optionSetId].push(transformedOptionGroup); + } else { + acc[optionSetId] = [transformedOptionGroup]; + } + return acc; + }, + {}, +); + +export const useOptionGroups = (program: any) => { + const params = useMemo(() => { + if (!program) { + return {}; + } + + const attributes = program.programTrackedEntityAttributes.reduce( + (acc, attribute) => { + const optionSet = attribute.trackedEntityAttribute.optionSet; + if (optionSet) { + acc.push(optionSet.id); + } + return acc; + }, + [], + ); + + return { + fields: 'id,optionSet,options', + filter: `optionSet.id:in:[${attributes.join(',')}]`, + }; + }, [program]); + + const queryKey = ['optionGroups', 'programAttributes', ...(program?.id ? [program.id] : [])]; + const queryFn = { resource: 'optionGroups', params }; + const queryOptions = { + enabled: Boolean(program), + select: createOptionSetToOptionGroupDictionary, + }; + const { data, isLoading, error } = useApiMetadataQuery(queryKey, queryFn, queryOptions); + + return { + optionGroups: program ? data : null, + loading: isLoading, + error, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetProfile/hooks/useProgram.js b/src/core_modules/capture-core/components/WidgetProfile/hooks/useProgram.js index b9c9a72f64..b23ddc9a9f 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/hooks/useProgram.js +++ b/src/core_modules/capture-core/components/WidgetProfile/hooks/useProgram.js @@ -1,51 +1,33 @@ // @flow import { useMemo } from 'react'; -import { useDataQuery } from '@dhis2/app-runtime'; - -const fields = - 'id,version,displayName,displayShortName,description,programType,style,minAttributesRequiredToSearch,enrollmentDateLabel,incidentDateLabel,featureType,selectEnrollmentDatesInFuture,selectIncidentDatesInFuture,displayIncidentDate,' + - 'access[*],' + - 'dataEntryForm[id,htmlCode],' + - 'categoryCombo[id,displayName,isDefault,categories[id,displayName]],' + - 'programIndicators[id,displayName,code,shortName,style,displayInForm,expression,displayDescription,description,filter,program[id]],' + - 'programSections[id, displayFormName, sortOrder, trackedEntityAttributes],' + - 'programRuleVariables[id,displayName,programRuleVariableSourceType,valueType,program[id],programStage[id],dataElement[id],trackedEntityAttribute[id],useCodeForOptionSet],' + - 'programStages[id,access,autoGenerateEvent,openAfterEnrollment,generatedByEnrollmentDate,reportDateToUse,minDaysFromStart,displayName,description,executionDateLabel,formType,featureType,validationStrategy,enableUserAssignment,style,' + - 'dataEntryForm[id,htmlCode],' + - 'programStageSections[id,displayName,displayDescription,sortOrder,dataElements[id]],' + - 'programStageDataElements[compulsory,displayInReports,renderOptionsAsRadio,allowFutureDate,renderType[*],dataElement[id,displayName,displayShortName,displayFormName,valueType,translations[*],description,optionSetValue,style,optionSet[id,displayName,version,valueType,options[id,displayName,code,style, translations]]]]' + - '],' + - 'programTrackedEntityAttributes[trackedEntityAttribute[id,displayName,displayShortName,displayFormName,description,valueType,optionSetValue,unique,orgunitScope,pattern,translations[property,locale,value],optionSet[id,displayName,version,valueType,options[id,displayName,name,code,style,translations]]],displayInList,searchable,mandatory,renderOptionsAsRadio,allowFutureDate],' + - 'trackedEntityType[id,access,displayName,allowAuditLog,minAttributesRequiredToSearch,featureType,trackedEntityTypeAttributes[trackedEntityAttribute[id],displayInList,mandatory,searchable],translations[property,locale,value]],' + - 'userRoles[id,displayName]'; +import { useApiProgram } from './useApiProgram'; +import { useOptionGroups } from './useOptionGroups'; export const useProgram = (programId: string) => { - const { error, loading, data } = useDataQuery( - useMemo( - () => ({ - programs: { - resource: 'programs', - id: programId, - params: { - fields, - }, - }, - }), - [programId], - ), - ); + const { error: programError, loading: programLoading, program } = useApiProgram(programId); + const { error: optionGroupsError, loading: optionGroupsLoading, optionGroups } = useOptionGroups(program); const programMetadata = useMemo(() => { - if (data?.programs) { - const program = data.programs; + if (program && optionGroups) { if (program.trackedEntityType) { program.trackedEntityType.changelogEnabled = program.trackedEntityType.allowAuditLog; delete program.trackedEntityType.allowAuditLog; } + program.programTrackedEntityAttributes = program.programTrackedEntityAttributes.map((attribute) => { + const tea = attribute.trackedEntityAttribute; + if (tea.optionSet) { + tea.optionSet.optionGroups = optionGroups[tea.optionSet.id]; + } + return attribute; + }); return program; } return null; - }, [data]); + }, [program, optionGroups]); - return { error, loading, program: !loading && programMetadata }; + return { + program: programMetadata, + loading: programLoading || optionGroupsLoading, + error: programError || optionGroupsError, + }; };