Skip to content

Commit

Permalink
Arjun | A - 1205463333089465 | Edit complex forms in new display cont…
Browse files Browse the repository at this point in the history
…rol (#741)

* Sowmya | WIP - edit complex forms

* add. half baked state of edit forms flow

* add. bring form controls panel as popup

* add. changes to save updated form

* add. tweaks to handle edit and errors through modal

* add. tests and refactor Edit Form

* add. custom error notification for edit form

* add. translations for error message

* add. tests to fix coverage

---------

Co-authored-by: sowmya198 <sowmya.ayilam.s@gmail.com>
  • Loading branch information
Arjun-Go and sowmya-AS authored Oct 17, 2023
1 parent 5dbdd8a commit 243c8b2
Show file tree
Hide file tree
Showing 17 changed files with 1,106 additions and 30 deletions.
1 change: 1 addition & 0 deletions micro-frontends/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@babel/core": "^7.15.5",
"@babel/plugin-transform-runtime": "^7.22.4",
"@babel/preset-env": "^7.22.4",
"bahmni-form-controls": "^0.93.12",
"@babel/preset-react": "^7.22.3",
"@testing-library/react": "12.1.5",
"axios": "1.4.0",
Expand Down
3 changes: 2 additions & 1 deletion micro-frontends/public/i18n/locale_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"REACTIONS": "Reaction(s)",
"SEARCH_REACTION": "Search Reaction",
"COMMON_REACTIONS": "Common Reactions",
"ALLERGEN": "Allergen"
"ALLERGEN": "Allergen",
"EDIT_FORM_ERROR_MESSAGE": "Please enter a value in the mandatory fields or correct the value in the highlighted fields to proceed"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import React, { useEffect, useState } from 'react';
import PropTypes from "prop-types";
import { getLocale } from "../i18n/utils";
import { getAllForms, getFormByFormName ,getFormDetail, getFormTranslations, setEditableObservations } from "./EditObservationFormUtils";
import { findByEncounterUuid } from '../../utils/FormDisplayControl/FormView';
import { Modal, Loading } from 'carbon-components-react';
import { FormattedMessage } from "react-intl";
import { I18nProvider } from '../i18n/I18nProvider';
import ErrorNotification from '../ErrorNotification/ErrorNotification';

import "./EditObservationForm.scss";

const EditObservationForm = (props) => {
const saveButtonText = (
<FormattedMessage
id={"SAVE"}
defaultMessage={"SAVE"}
/>
);

const {
formName,
closeEditObservationForm,
isEditFormLoading,
setEditFormLoading,
patient,
formData,
encounterUuid,
consultationMapper,
handleEditSave,
editErrorMessage
} = props;

const [loadedFormDetails, setLoadedFormDetails] = useState({});
const [loadedFormTranslations, setLoadedFormTranslations] = useState({});
const [updatedObservations, setUpdatedObservations] = useState(null);
const [editError, setEditError] = useState(false);
const [encounter, setEncounter] = useState(null);
const nodeId = "form-renderer";

const handleSave = () => {
const editedObservations = updatedObservations.getValue();
if(editedObservations.errors && editedObservations.errors.length > 0) {
setEditError(true);
return;
}
encounter.observations = editedObservations.observations;
handleEditSave(encounter);
};

useEffect(() => {
const fetchFormDetails = async () => {
if( formData.length > 0 && encounterUuid !== null) {
const encounterTransaction = await findByEncounterUuid(encounterUuid);
setEncounter(consultationMapper.map(encounterTransaction));

const formVersion = "1";
const allForms = await getAllForms();
const observationForm = getFormByFormName(allForms, formName, formVersion);
const formUuid = observationForm.uuid;
const locale = getLocale();
const validateForm = false;
const collapse = false;

if (!loadedFormDetails[formUuid]) {
var formDetails = await getFormDetail(formUuid);
const formDetailsAsString = formDetails.resources[0].value;
formDetails = JSON.parse(formDetailsAsString);
formDetails.version = formVersion;

setLoadedFormDetails((prevDetails) => ({ ...prevDetails, [formUuid]: formDetails }));

const formParams = { formName: formName, formVersion: formVersion, locale: locale, formUuid: formUuid };
const formTranslations = await getFormTranslations(formDetails.translationsUrl, formParams);
setLoadedFormTranslations((prevTranslations) => ({ ...prevTranslations, [formUuid]: formTranslations }));

var editableObservations = [];
formData.forEach(function (observation) {
setEditableObservations(observation, formName, formVersion, editableObservations);
});
setEditFormLoading(false);
setUpdatedObservations(window.renderWithControls(formDetails, editableObservations, nodeId, collapse, patient, validateForm, locale, formTranslations));
}
}
};

fetchFormDetails();
}, [formData, formName, loadedFormDetails, loadedFormTranslations, patient, encounterUuid]);

return (
<>
<I18nProvider>
<Modal
open
passiveModal
className="edit-observation-form-modal"
onRequestClose={closeEditObservationForm}
>
{
isEditFormLoading ? (<Loading />) :
<div>
<div className='section-title-wrapper'>
<h2 className="section-title">{formName}</h2>
<button className='confirm' onClick={handleSave}>{saveButtonText}</button>
</div>
<section className="content-body">
<section className='section-body'>
<div id={nodeId}></div>
</section>
</section>
</div>
}
</Modal>
{ editError && <ErrorNotification setEditError={setEditError} errorMessage={editErrorMessage}/> }
</I18nProvider>
</>
);
};

EditObservationForm.propTypes = {
formName: PropTypes.string.isRequired,
closeEditObservationForm: PropTypes.func.isRequired,
isEditFormLoading: PropTypes.bool.isRequired,
patient: PropTypes.object.isRequired,
formData: PropTypes.object.isRequired,
encounterUuid: PropTypes.string.isRequired,
consultationMapper: PropTypes.object.isRequired,
handleSave: PropTypes.func.isRequired,
handleSaveError: PropTypes.func.isRequired
};

export default EditObservationForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.edit-observation-form-modal {
.bx--modal-container {
background: #f0f0f0;
border-radius: 5px;
width: 80%;
height: 84%;
max-width: 1180px;
}

.content-body {
border: 1px solid #669999;
box-shadow: 0px 4px 4px 0px #0000004f;
}

.section-title-wrapper {
background: #669999;
padding-right: 10px;
display: flex;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 1;
}

.section-title {
font-family: OpenSans, Arial, sans-serif;
padding: 10px;
margin-top: 0;
margin-bottom: 0;
color: #ffffff;
font-size: 16px;
clear: both;
background: #669999;
position: sticky;
border-radius: 0;
top: 0;
}

.confirm {
border-radius: 3px;
background: linear-gradient(to bottom, #A1D030, #88af28);
background-color: #88af28;
border: #88af28 1px solid;
padding: 6px 20px 7px;
display: inline-block;
line-height: 1.2em;
color: white;
cursor: pointer;
min-width: 0;
max-width: 300px;
text-decoration: none;
max-width: 250px;
min-width: 0;
margin-right: 10px;
margin-bottom: 6px;
}

.section-body {
padding: 20px 10px;
background-color: #ffffff;
color: #525252;
}
}

.bx--modal-content {
padding-top: 0;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import EditObservationForm from './EditObservationForm';

describe('EditObservationForm', () => {
const mockProps = {
"isEditFormLoading": false,
"formName": "Radiology Study Report",
"patient": {
"uuid": "3c05ad1e-573e-43c5-81d2-ac18bfe3efd2",
"givenName": "new",
"familyName": "patient",
"name": "new patient",
"age": 22,
"ageText": "22 <span> years </span>",
"gender": "F",
"genderText": "<span>Female</span>",
"address": {},
"birthdateEstimated": false,
"birthtime": null,
"identifier": "BAH203002",
"birthdate": "2001-10-14T18:30:00.000Z",
"image": "/openmrs/ws/rest/v1/patientImage?patientUuid=3c05ad1e-573e-43c5-81d2-ac18bfe3efd2",
"registrationLocation": {
"label": "Registration Location",
"value": {
"uuid": "0fbbeaf4-f3ea-11ed-a05b-0242ac123210",
"display": "G Mobile Clinic",
"links": [
{
"rel": "self",
"uri": "http://localhost/openmrs/ws/rest/v1/location/0fbbeaf4-f3ea-11ed-a05b-0242ac123210",
"resourceAlias": "location"
}
]
},
"isDateField": false
},
"confirmedPatient": {
"label": "confirmedPatient",
"value": true,
"isDateField": false
}
},
"formData": {
"encounterDateTime": 1697378779000,
"visitStartDateTime": null,
"targetObsRelation": null,
"groupMembers": [],
"providers": [
{
"uuid": "c1c26908-3f10-11e4-adec-0800271c1b75",
"name": "Bailly RURANGIRWA",
"encounterRoleUuid": "a0b03050-c99b-11e0-9572-0800200c9a66"
}
],
"isAbnormal": null,
"duration": null,
"type": "Text",
"encounterUuid": "847d26db-d1db-4176-b704-f906dee15fad",
"obsGroupUuid": null,
"creatorName": "Bailly RURANGIRWA",
"conceptSortWeight": 1,
"parentConceptUuid": null,
"hiNormal": null,
"lowNormal": null,
"formNamespace": "Bahmni",
"formFieldPath": "Radiology Study Report.1/15-0",
"interpretation": null,
"status": "FINAL",
"encounterTypeName": null,
"complexData": null,
"abnormal": null,
"unknown": false,
"orderUuid": null,
"observationDateTime": 1697378779000,
"conceptNameToDisplay": "Study Site, Projections and Comment",
"voided": false,
"voidReason": null,
"valueAsString": "Done",
"concept": {
"uuid": "38bdca1d-a351-4c64-84ae-e3390891f637",
"name": "Study Site, Projections and Comment",
"dataType": "Text",
"shortName": "Study Site, Projections and Comment",
"units": null,
"conceptClass": "Misc",
"hiNormal": null,
"lowNormal": null,
"set": false,
"mappings": []
},
"uuid": "f030fecd-6b48-416e-a6f6-1192ae8c18cc",
"conceptUuid": "38bdca1d-a351-4c64-84ae-e3390891f637",
"comment": null,
"value": "Done"
},
"encounterUuid": "847d26db-d1db-4176-b704-f906dee15fad",
"consultationMapper": {},
"handleEditSave": jest.fn(),
"closeEditObservationForm": jest.fn(),
"handleSave": jest.fn()
};

it('should render the component', async () => {
render(<EditObservationForm {...mockProps} />);
await waitFor(() => {
expect(screen.getByText('Radiology Study Report')).toBeTruthy();
});
});

it('should show Loading screen if form is loading', async () => {
var updatedProps = mockProps;
updatedProps.isEditFormLoading = true;

render(<EditObservationForm {...updatedProps} />);
await waitFor(() => {
expect(screen.queryAllByText("Active loading indicator")).toHaveLength(2);
});
});

});
Loading

0 comments on commit 243c8b2

Please sign in to comment.