Skip to content

Commit

Permalink
(fix) fix error editing obs group with more than one repeating group (#…
Browse files Browse the repository at this point in the history
…149)

* Add ability to edit obs group with more than one node

* add unit tests
  • Loading branch information
kajambiya authored Nov 27, 2023
1 parent 06b2746 commit cfe5cca
Show file tree
Hide file tree
Showing 8 changed files with 434 additions and 264 deletions.
137 changes: 137 additions & 0 deletions __mocks__/forms/ohri-forms/obs-group-test_form.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"name": "ObsGroup Test Form",
"version": "1",
"published": true,
"retired": false,
"pages": [
{
"label": "Introduction",
"sections": [
{
"label": "",
"isExpanded": "true",
"questions": [
{
"type": "markdown",
"questionOptions": {
"rendering": "markdown"
},
"id": "fooMarkdown",
"value": [
"**Use this form to:** Test Obs Group behaviour"
]
}
]
}
]
},
{
"label": "Obs Group Page",
"sections": [
{
"label": "Group Section",
"isExpanded": "true",
"questions": [
{
"id": "myGroup",
"label": "My Group",
"type": "obsGroup",
"questionOptions": {
"rendering": "repeating",
"concept": "1c70c490-cafa-4c95-9fdd-a30b62bb78b8"
},
"behaviours":[
{
"intent":"*",
"required":"false",
"unspecified":"false",
"hide":{
"hideWhenExpression":""
},
"validators":[]
}
],
"questions": [
{
"label": "Sex",
"type":"obs",
"required":"true",
"questionOptions":{
"rendering":"radio",
"concept":"1587AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"answers":[
{
"concept":"1535AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"label":"Female"
},
{
"concept":"1534AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"label":"Male"
}
]
},
"id":"childSex",
"behaviours":[
{
"intent":"*",
"required":"true",
"unspecified":"true",
"hide":{
"hideWhenExpression":"false"
},
"validators":[]
}
]
},
{
"label": "Date of Birth",
"type": "obs",
"questionOptions": {
"rendering": "date",
"concept": "164802AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"weeksList": ""
},
"id": "birthDate",
"behaviours": [
{
"intent": "*",
"required": "true",
"unspecified": "true",
"hide": {
"hideWhenExpression": "false"
},
"validators": [
{
"type": "date",
"allowFutureDates": "false"
},
{
"type": "js_expression",
"failsWhenExpression": "!isDateEqualTo(myValue, useFieldValue('visit_date'))",
"message": "Child birth date should be the same as the visit date!"
}
]
}
]
}
]
}
]
}
]
}
],
"availableIntents": [
{
"intent": "*",
"display": "ObsGroup Test Form"
}
],
"processor": "EncounterFormProcessor",
"uuid": "8f713e0e-94a0-3c57-9024-69520933802a",
"referencedForms": [],
"encounterType": "7e54cd64-f9c3-11eb-8e6a-57478ce139b0",
"encounter": "Obs Group Test",
"allowUnspecifiedAll": true
}

1 change: 1 addition & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface OHRIFormField {
type: string;
questionOptions: OHRIFormQuestionOptions;
id: string;
uuid?: string;
groupId?: string;
questions?: Array<OHRIFormField>;
value?: any;
Expand Down
3 changes: 1 addition & 2 deletions src/components/encounter/ohri-encounter-form.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -420,15 +420,14 @@ export const OHRIEncounterForm: React.FC<OHRIEncounterFormProps> = ({
.filter((field) => !field.questionOptions.isTransient && field.questionOptions.rendering !== 'file')
.forEach((field) => {
if (field.type == 'obsGroup') {
const obsGroupUuid = encounter?.obs?.find((m) => m.concept.uuid == field.questionOptions.concept)?.uuid;
const obsGroup = {
person: patient?.id,
obsDatetime: encounterDate,
concept: field.questionOptions.concept,
location: encounterLocation,
order: null,
groupMembers: [],
uuid: obsGroupUuid,
uuid: field.uuid,
voided: false,
};
let hasValue = false;
Expand Down
7 changes: 4 additions & 3 deletions src/components/repeat/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ export function cloneObsGroup(srcField: OHRIFormField, obsGroup: any, idSuffix:
const clonedField = cloneDeep(srcField) as OHRIFormField;
clonedField.questionOptions.repeatOptions = { ...(clonedField.questionOptions.repeatOptions ?? {}), isCloned: true };
clonedField.value = obsGroup;
clonedField.uuid = obsGroup?.uuid;
clonedField.id = `${clonedField.id}_${idSuffix}`;
clonedField.questions.forEach(childField => {
clonedField.questions.forEach((childField) => {
originalGroupMembersIds.push(childField.id);
childField.id = `${childField.id}_${idSuffix}`;
childField['groupId'] = clonedField.id;
Expand All @@ -23,7 +24,7 @@ export function cloneObsGroup(srcField: OHRIFormField, obsGroup: any, idSuffix:
);
}
if (childField.validators?.length) {
childField.validators.forEach(validator => {
childField.validators.forEach((validator) => {
if (validator.type === 'js_expression') {
validator.failsWhenExpression = updateFieldIdInExpression(
validator.failsWhenExpression,
Expand All @@ -46,7 +47,7 @@ export function cloneObsGroup(srcField: OHRIFormField, obsGroup: any, idSuffix:

export const updateFieldIdInExpression = (expression: string, index: number, questionIds: string[]) => {
let uniqueQuestionIds = [...new Set(questionIds)];
uniqueQuestionIds.forEach(id => {
uniqueQuestionIds.forEach((id) => {
if (expression.match(id)) {
expression = expression.replace(new RegExp(id, 'g'), `${id}_${index}`);
}
Expand Down
27 changes: 15 additions & 12 deletions src/components/repeat/ohri-repeat.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ export const showAddButton = (limit: string | number, counter: number) => {

export const OHRIRepeat: React.FC<OHRIFormFieldProps> = ({ question, onChange }) => {
const { t } = useTranslation();
const { fields: allFormFields, encounterContext, obsGroupsToVoid, formFieldHandlers } = React.useContext(
OHRIFormContext,
);
const {
fields: allFormFields,
encounterContext,
obsGroupsToVoid,
formFieldHandlers,
} = React.useContext(OHRIFormContext);
const { values, setValues } = useFormikContext();
const [counter, setCounter] = useState(1);
const [obsGroups, setObsGroups] = useState([]);

useEffect(() => {
const groups = allFormFields.filter(
field => field.questionOptions.concept === question.questionOptions.concept && field.id.startsWith(question.id),
(field) => field.questionOptions.concept === question.questionOptions.concept && field.id.startsWith(question.id),
);
setCounter(groups.length);
setObsGroups(groups);
Expand All @@ -37,7 +40,7 @@ export const OHRIRepeat: React.FC<OHRIFormFieldProps> = ({ question, onChange })
(counter: number) => {
const clonedGroupingField = cloneObsGroup(question, null, counter);
// run necessary expressions
clonedGroupingField.questions.forEach(childField => {
clonedGroupingField.questions.forEach((childField) => {
if (childField.hide?.hideWhenExpression) {
childField.isHidden = evaluateExpression(
childField.hide.hideWhenExpression,
Expand All @@ -60,7 +63,7 @@ export const OHRIRepeat: React.FC<OHRIFormFieldProps> = ({ question, onChange })
mode: encounterContext.sessionMode,
patient: encounterContext.patient,
},
).then(result => {
).then((result) => {
if (!isEmpty(result)) {
values[childField.id] = result;
formFieldHandlers[childField.type].handleFieldSubmission(childField, result, encounterContext);
Expand All @@ -83,20 +86,20 @@ export const OHRIRepeat: React.FC<OHRIFormFieldProps> = ({ question, onChange })
delete question.value.value;
obsGroupsToVoid.push(question.value);
}
setObsGroups(obsGroups.filter(q => q.id !== question.id));
setObsGroups(obsGroups.filter((q) => q.id !== question.id));

// cleanup
const dueFields = [question.id, ...question.questions.map(q => q.id)];
dueFields.forEach(field => {
const index = allFormFields.findIndex(f => f.id === field);
const dueFields = [question.id, ...question.questions.map((q) => q.id)];
dueFields.forEach((field) => {
const index = allFormFields.findIndex((f) => f.id === field);
allFormFields.splice(index, 1);
delete values[field];
});
};

const nodes = obsGroups.map((question, index) => {
const deleteControl =
obsGroups.length > 1 ? (
obsGroups.length > 1 && encounterContext.sessionMode !== 'view' ? (
<div>
<div className={styles.removeButton}>
<Button
Expand Down Expand Up @@ -138,7 +141,7 @@ export const OHRIRepeat: React.FC<OHRIFormFieldProps> = ({ question, onChange })
kind="tertiary"
onClick={() => {
const nextCount = counter + 1;
handleAdd(nextCount);
handleAdd(counter);
setCounter(nextCount);
}}>
<span>{question.questionOptions.repeatOptions?.addText || `${t('add', 'Add')}`}</span>
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useInitialValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export function useInitialValues(
initialValues[`${field.id}-unspecified`] = !!!existingVal;
}
});
repeatableFields.forEach((field) => {
let initializedRepeatField = formFields.find((initField) => initField.id === field.id);
if (initializedRepeatField) {
initializedRepeatField.uuid = initializedRepeatField.questions[0]?.value?.obsGroup?.uuid;
}
});
const flatenedFields = repeatableFields.flatMap((field) => {
let counter = 1;
const unMappedGroups = encounter.obs.filter(
Expand Down
Loading

0 comments on commit cfe5cca

Please sign in to comment.