Skip to content

Commit

Permalink
Repeating obs to re-display more consistently (#101)
Browse files Browse the repository at this point in the history
* Repeating obs to re-display more consistently

* Remove empty files
  • Loading branch information
samuelmale authored Jul 30, 2023
1 parent e535be1 commit ae36707
Show file tree
Hide file tree
Showing 15 changed files with 1,457 additions and 526 deletions.
867 changes: 867 additions & 0 deletions __mocks__/use-initial-values/encounter.mock.json

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions __mocks__/use-initial-values/patient.mock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"resourceType": "Patient",
"id": "0c044d55-6bbb-4809-979c-8322d3095765",
"meta": {
"lastUpdated": "2023-05-17T17:10:01.000+00:00",
"tag": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue",
"code": "SUBSETTED",
"display": "Resource encoded in summary mode"
}
]
},
"identifier": [
{
"id": "1fc8ba6f-e311-46cd-89f5-4ab76b251049",
"extension": [
{
"url": "http://fhir.openmrs.org/ext/patient/identifier#location",
"valueReference": {
"reference": "Location/44c3efb0-2583-4c80-a79e-1f756a03c0a1",
"type": "Location",
"display": "Outpatient Clinic"
}
}
],
"use": "official",
"type": {
"coding": [
{
"code": "05a29f94-c0ed-11e2-94be-8c13b969e334"
}
],
"text": "OpenMRS ID"
},
"value": "10014EW"
}
],
"active": true,
"name": [
{
"id": "3ef34554-056e-4284-95b6-ea796b965896",
"family": "Wills",
"given": [
"smith",
"Kimoti"
]
}
],
"gender": "male",
"birthDate": "1956",
"deceasedBoolean": false,
"address": [
{
"id": "54859289-a35a-498e-b84d-2c8ba375fdb8",
"extension": [
{
"url": "http://fhir.openmrs.org/ext/address",
"extension": [
{
"url": "http://fhir.openmrs.org/ext/address#address1",
"valueString": "Kampala"
}
]
}
],
"use": "home",
"city": "Kitale",
"district": "Central",
"country": "Uganda"
}
]
}
4 changes: 3 additions & 1 deletion src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export interface OHRIFormField {
type: string;
questionOptions: OHRIFormQuestionOptions;
id: string;
groupId?: string;
questions?: Array<OHRIFormField>;
value?: any;
hide?: HideProps;
Expand Down Expand Up @@ -155,7 +156,7 @@ export interface OHRIFormQuestionOptions {
locationTag?: string;
rows?: number;
toggleOptions?: { labelTrue: string; labelFalse: string };
repeatOptions?: { addText?: string; limit?: string; limitExpression?: string };
repeatOptions?: { addText?: string; limit?: string; limitExpression?: string; isCloned?: boolean };
defaultValue?: any;
calculate?: {
calculateExpression: string;
Expand Down Expand Up @@ -205,6 +206,7 @@ export interface OpenmrsEncounter {
encounterProviders?: Array<Record<string, any>>;
form?: {
uuid: string;
[anythingElse: string]: any;
};
}

Expand Down
14 changes: 6 additions & 8 deletions src/components/encounter/ohri-encounter-form.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
return [flattenedFieldsTemp, conceptReferencesTemp];
}, []);

const { initialValues: tempInitialValues, isFieldEncounterBindingComplete } = useInitialValues(
const { initialValues: tempInitialValues, isBindingComplete } = useInitialValues(
flattenedFields,
encounter,
encounterContext,
);

// look up concepts via their references
const { concepts } = useConcepts(conceptReferences);
const { concepts, isLoading: isLoadingConcepts } = useConcepts(conceptReferences);

const addScrollablePages = useCallback(() => {
formJson.pages.forEach(page => {
Expand All @@ -181,7 +181,7 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
}, [location, encounter]);

useEffect(() => {
if (tempInitialValues && Object.keys(tempInitialValues).length) {
if (Object.keys(tempInitialValues ?? {}).length && !isFieldInitializationComplete) {
setFields(
flattenedFields.map(field => {
if (field.hide) {
Expand Down Expand Up @@ -267,14 +267,12 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
});
});
setForm(form);
setAllInitialValues({ ...allInitialValues, ...tempInitialValues });
if (sessionMode == 'enter') {
setIsFieldInitializationComplete(true);
} else if (isFieldEncounterBindingComplete) {
setAllInitialValues({ ...allInitialValues, ...values, ...tempInitialValues });
if (isBindingComplete && !isLoadingConcepts) {
setIsFieldInitializationComplete(true);
}
}
}, [tempInitialValues, concepts]);
}, [tempInitialValues, concepts, isLoadingConcepts, isBindingComplete]);

useEffect(() => {
if (sessionMode == 'enter' && !isTrue(formJson.formOptions?.usePreviousValueDisabled)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { updateFieldIdInExpression } from './ohri-repeat.component';
import { updateFieldIdInExpression } from './helpers';

describe('OhriRepeatComponent - handleExpressionFieldIdUpdate', () => {
it('Should handle update of expression with ids in repeat group', () => {
Expand Down
101 changes: 101 additions & 0 deletions src/components/repeat/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { getConcept } from '../../api/api';
import { OHRIFormField } from '../../api/types';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import { ConceptTrue } from '../../constants';

export function cloneObsGroup(
srcField: OHRIFormField,
obsGroup: any,
idSuffix: number,
allFieldValues: Record<string, any>,
) {
const originalGroupMembersIds: string[] = [];
const clonedField = cloneDeep(srcField) as OHRIFormField;
clonedField.questionOptions.repeatOptions = { ...(clonedField.questionOptions.repeatOptions ?? {}), isCloned: true };
clonedField.value = obsGroup;
clonedField.id = `${clonedField.id}_${idSuffix}`;
clonedField.questions.forEach(childField => {
originalGroupMembersIds.push(childField.id);
childField.id = `${childField.id}_${idSuffix}`;
childField['groupId'] = clonedField.id;
childField.value = null;
const initialValue = obsGroup ? getInitialValueFromObs(childField, obsGroup) : null;
allFieldValues[`${childField.id}`] = initialValue
? initialValue
: childField.questionOptions.rendering == 'checkbox'
? []
: '';

// cleanup expressions

if (childField['hide'] && childField['hide'].hideWhenExpression) {
childField['hide'].hideWhenExpression = updateFieldIdInExpression(
childField['hide'].hideWhenExpression,
idSuffix,
originalGroupMembersIds,
);
}
if (childField.validators?.length) {
childField.validators.forEach(validator => {
if (validator.type === 'js_expression') {
validator.failsWhenExpression = updateFieldIdInExpression(
validator.failsWhenExpression,
idSuffix,
originalGroupMembersIds,
);
}
});
}
if (childField.questionOptions.calculate?.calculateExpression) {
childField.questionOptions.calculate.calculateExpression = updateFieldIdInExpression(
childField.questionOptions.calculate?.calculateExpression,
idSuffix,
originalGroupMembersIds,
);
}
});
return clonedField;
}

export const getInitialValueFromObs = (field: OHRIFormField, obsGroup: any) => {
const rendering = field.questionOptions.rendering;
const obs = obsGroup.groupMembers.filter(o => o.concept.uuid == field.questionOptions.concept);
if (obs.length) {
field.value = obs[0];
if (rendering == 'radio' || rendering == 'content-switcher') {
// TODO: Now that concepts are fetched at initialization, we don't need to perform this API call.
// Link the concepts with the associated form fields for future references
getConcept(field.questionOptions.concept, 'custom:(uuid,display,datatype:(uuid,display,name))').subscribe(
result => {
if (result.datatype.name == 'Boolean') {
field.value.value = obs[0].value.uuid;
}
},
);
}
if (typeof obs[0].value == 'string' || typeof obs[0].value == 'number') {
return field.questionOptions.rendering == 'date' ? dayjs(obs[0].value).toDate() : obs[0].value;
}
if (field.questionOptions.rendering == 'checkbox') {
field.value = obs;
return field.value.map(o => o.value.uuid);
}
if (field.questionOptions.rendering == 'toggle') {
field.value.value = obs[0].value.uuid;
return obs[0].value == ConceptTrue;
}
return obs[0].value?.uuid;
}
return '';
};

export const updateFieldIdInExpression = (expression: string, index: number, questionIds: string[]) => {
let uniqueQuestionIds = [...new Set(questionIds)];
uniqueQuestionIds.forEach(id => {
if (expression.match(id)) {
expression = expression.replace(new RegExp(id, 'g'), `${id}_${index}`);
}
});
return expression;
};
Loading

0 comments on commit ae36707

Please sign in to comment.