From a65b7687c203b79e2b6dcc27078ce8474273bfdb Mon Sep 17 00:00:00 2001 From: p-netm Date: Thu, 16 Feb 2023 17:08:47 +0300 Subject: [PATCH] Fix Replication of participants every time care team is edited. (#1175) * Fix Bug where practitioner names were rendered as objects * Style tweak on view details page * Fix bug that replicates practitioners on every careteam edit * Remove surplus form.resetFields useEffect * Fix bug where removed practitioners and orgs still show up * Deprecate and remove care team util test file --- .../components/CreateEditCareTeam/Form.tsx | 10 +- .../components/CreateEditCareTeam/index.tsx | 2 + .../CreateEditCareTeam/tests/fixtures.ts | 182 +++++++++++- .../tests/form.unmocked.test.tsx | 122 +++++++- .../CreateEditCareTeam/tests/utils.test.ts | 174 ----------- .../components/CreateEditCareTeam/utils.tsx | 110 ++++--- .../src/components/ViewDetails/index.tsx | 37 ++- .../tests/__snapshots__/index.test.tsx.snap | 209 ++++++++++++++ .../components/ViewDetails/tests/fixtures.ts | 269 ++++++++++++++++++ .../ViewDetails/tests/index.test.tsx | 26 +- 10 files changed, 901 insertions(+), 240 deletions(-) delete mode 100644 packages/fhir-care-team/src/components/CreateEditCareTeam/tests/utils.test.ts diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/Form.tsx b/packages/fhir-care-team/src/components/CreateEditCareTeam/Form.tsx index 0b7cf198f..fb8a30ea4 100644 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/Form.tsx +++ b/packages/fhir-care-team/src/components/CreateEditCareTeam/Form.tsx @@ -68,15 +68,7 @@ const CareTeamForm: React.FC = (props: CareTeamFormProps) => initialValues={initialValues} onFinish={(values: FormFields) => { setIsSubmitting(true); - submitForm( - { ...initialValues, ...values }, - fhirBaseURL, - organizations, - practitioners, - t, - initialValues.id, - initialValues.uuid - ) + submitForm(values, initialValues, fhirBaseURL, organizations, practitioners, t) .catch(() => sendErrorNotification(t('An error occurred'))) .finally(() => setIsSubmitting(false)); }} diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/index.tsx b/packages/fhir-care-team/src/components/CreateEditCareTeam/index.tsx index f788cb1cd..884e53251 100644 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/index.tsx +++ b/packages/fhir-care-team/src/components/CreateEditCareTeam/index.tsx @@ -55,6 +55,8 @@ const CreateEditCareTeam: React.FC = (props: CreateEdit onError: () => sendErrorNotification(t('An error occurred')), select: (res) => res, enabled: !!careTeamId, + cacheTime: 0, + staleTime: 0, } ); diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/fixtures.ts b/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/fixtures.ts index 6a5c003d2..f8d47d26b 100644 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/fixtures.ts +++ b/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/fixtures.ts @@ -1,3 +1,5 @@ +import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; + export const careTeam1 = { resourceType: 'CareTeam', id: '308', @@ -114,7 +116,7 @@ export const careTeam4201 = { reference: 'Organization/4190', }, ], -}; +} as unknown as ICareTeam; export const careTeam4201Edited = { resourceType: 'CareTeam', @@ -146,6 +148,155 @@ export const careTeam4201Edited = { identifier: [{ use: 'official', value: '0b3a3311-6f5a-40dd-95e5-008001acebe1' }], }; +export const careTeam4201alternative = { + resourceType: 'CareTeam', + id: '4201', + meta: { + versionId: '2', + lastUpdated: '2021-11-01T10:33:47.409+00:00', + source: '#3671f89bf6e8bf20', + }, + text: { + status: 'generated', + div: '
Care Team
', + }, + contained: [ + { + resourceType: 'Practitioner', + id: '4193', + name: [ + { + family: 'Careful', + given: ['Adam'], + prefix: ['Dr'], + }, + ], + }, + ], + status: 'active', + category: [ + { + coding: [ + { + system: 'http://loinc.org', + code: 'LA27976-2', + display: 'Encounter-focused care team', + }, + ], + }, + ], + name: 'Peter Charlmers Care team', + subject: { + reference: 'Patient/4195', + display: 'Peter James Chalmers', + }, + encounter: { + reference: 'Encounter/4197', + }, + period: { + end: '2013-01-01', + }, + participant: [ + { + role: [ + { + text: 'responsiblePerson', + }, + ], + member: { + reference: 'Patient/4195', + display: 'Peter James Chalmers', + }, + }, + { + role: [ + { + text: 'responsiblePerson', + }, + ], + member: { + reference: '#pr1', + display: 'Dorothy Dietition', + }, + onBehalfOf: { + reference: 'Organization/f001', + }, + period: { + end: '2013-01-01', + }, + }, + { member: { reference: 'Practitioner/102', display: 'Ward N 2 Williams MD' } }, + { + role: [ + { + coding: [ + { + system: 'http://snomed.info/sct', + code: '394730007', + display: 'Healthcare related organization', + }, + ], + }, + ], + member: { display: 'Test Team 70', reference: 'Organization/368' }, + }, + ], + managingOrganization: [{ display: 'Test Team 70', reference: 'Organization/368' }], +} as unknown as ICareTeam; + +export const careTeam4201alternativeEdited = { + resourceType: 'CareTeam', + id: '4201', + contained: [ + { + resourceType: 'Practitioner', + id: '4193', + name: [{ family: 'Careful', given: ['Adam'], prefix: ['Dr'] }], + }, + ], + status: 'inactive', + category: [ + { + coding: [ + { system: 'http://loinc.org', code: 'LA27976-2', display: 'Encounter-focused care team' }, + ], + }, + ], + name: 'Peter Charlmers Care teamcare team', + subject: { reference: 'Patient/4195', display: 'Peter James Chalmers' }, + encounter: { reference: 'Encounter/4197' }, + period: { end: '2013-01-01' }, + managingOrganization: [{ display: 'testing ash123', reference: 'Organization/319' }], + identifier: [{ use: 'official', value: '9b782015-8392-4847-b48c-50c11638656b' }], + participant: [ + { + role: [{ text: 'responsiblePerson' }], + member: { reference: 'Patient/4195', display: 'Peter James Chalmers' }, + }, + { + role: [{ text: 'responsiblePerson' }], + member: { reference: '#pr1', display: 'Dorothy Dietition' }, + onBehalfOf: { reference: 'Organization/f001' }, + period: { end: '2013-01-01' }, + }, + { member: { reference: 'Practitioner/103', display: 'Ward N 1 Williams MD' } }, + { + role: [ + { + coding: [ + { + system: 'http://snomed.info/sct', + code: '394730007', + display: 'Healthcare related organization', + }, + ], + }, + ], + member: { display: 'testing ash123', reference: 'Organization/319' }, + }, + ], +}; + export const practitioners = { resourceType: 'Bundle', id: '5db4aa7e-eca6-432d-a544-d0a8279d8103', @@ -1513,6 +1664,7 @@ export const createdCareTeam = { name: 'Care team 1', status: 'active', participant: [ + { member: { reference: 'Practitioner/102', display: 'Ward N 2 Williams MD' } }, { role: [ { @@ -1525,8 +1677,34 @@ export const createdCareTeam = { ], }, ], - member: { reference: 'Organization/368', display: 'Test Team 70' }, + member: { display: 'Test Team 70', reference: 'Organization/368' }, }, + ], + managingOrganization: [{ display: 'Test Team 70', reference: 'Organization/368' }], +}; + +export const createdCareTeam2 = { + resourceType: 'CareTeam', + identifier: [{ use: 'official', value: '9b782015-8392-4847-b48c-50c11638656b' }], + id: '9b782015-8392-4847-b48c-50c11638656b', + name: 'care team', + status: 'inactive', + participant: [ { member: { reference: 'Practitioner/102', display: 'Ward N 2 Williams MD' } }, + { + role: [ + { + coding: [ + { + system: 'http://snomed.info/sct', + code: '394730007', + display: 'Healthcare related organization', + }, + ], + }, + ], + member: { reference: 'Organization/368', display: 'Test Team 70' }, + }, ], + managingOrganization: [{ reference: 'Organization/368', display: 'Test Team 70' }], }; diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/form.unmocked.test.tsx b/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/form.unmocked.test.tsx index d3e97a29f..db1394999 100644 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/form.unmocked.test.tsx +++ b/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/form.unmocked.test.tsx @@ -1,17 +1,23 @@ import React from 'react'; import { mount } from 'enzyme'; import flushPromises from 'flush-promises'; -import * as fixtures from './fixtures'; import { act } from 'react-dom/test-utils'; import { CareTeamForm } from '../Form'; -import { defaultInitialValues } from '../utils'; +import { defaultInitialValues, getCareTeamFormFields } from '../utils'; import { getResourcesFromBundle } from '@opensrp/react-utils'; -import { cleanup, fireEvent, waitFor } from '@testing-library/react'; +import { cleanup, fireEvent, waitFor, render, screen } from '@testing-library/react'; import userEvents from '@testing-library/user-event'; import * as notifications from '@opensrp/notifications'; import nock from 'nock'; import { careTeamResourceType } from '../../../constants'; -import { createdCareTeam } from './fixtures'; +import { + createdCareTeam, + createdCareTeam2, + careTeam4201alternativeEdited, + organizations, + practitioners, + careTeam4201alternative, +} from './fixtures'; import { store } from '@opensrp/store'; import { authenticateUser } from '@onaio/session-reducer'; @@ -60,8 +66,8 @@ afterEach(() => { const props = { initialValues: defaultInitialValues, fhirBaseURL: 'https://r4.smarthealthit.org/', - practitioners: getResourcesFromBundle(fixtures.practitioners), - organizations: getResourcesFromBundle(fixtures.organizations), + practitioners: getResourcesFromBundle(practitioners), + organizations: getResourcesFromBundle(organizations), }; test('filter select by text able to create new careteam', async () => { @@ -193,9 +199,111 @@ test('filter select by text able to create new careteam', async () => { wrapper.find('form').simulate('submit'); await waitFor(() => { - expect(successNoticeMock.mock.calls).toEqual([['Successfully Added Care Teams']]); + expect(successNoticeMock.mock.calls).toEqual([['Successfully added CareTeams']]); }); expect(nock.isDone()).toBeTruthy(); wrapper.unmount(); }); + +test('1157 - Create care team works corectly', async () => { + const successNoticeMock = jest + .spyOn(notifications, 'sendSuccessNotification') + .mockImplementation(() => undefined); + + nock(props.fhirBaseURL) + .put(`/${careTeamResourceType}/${createdCareTeam2.id}`, createdCareTeam2) + .reply(200) + .persist(); + + render(); + + await waitFor(() => { + expect(screen.getByText(/Create Care Team/)).toBeInTheDocument(); + }); + + const nameInput = screen.getByLabelText('Name') as Element; + userEvents.type(nameInput, 'care team'); + + const activeStatusRadio = screen.getByLabelText('Active'); + expect(activeStatusRadio).toBeChecked(); + + const inactiveStatusRadio = screen.getByLabelText('Inactive'); + expect(inactiveStatusRadio).not.toBeChecked(); + userEvents.click(inactiveStatusRadio); + + const practitionersInput = screen.getByLabelText('Practitioner Participant'); + userEvents.click(practitionersInput); + fireEvent.click(screen.getByTitle('Ward N 2 Williams MD')); + + const managingOrgsSelect = screen.getByLabelText('Managing organizations'); + userEvents.click(managingOrgsSelect); + fireEvent.click(screen.getByTitle('Test Team 70')); + + const saveBtn = screen.getByRole('button', { name: 'Save' }); + userEvents.click(saveBtn); + + await waitFor(() => { + expect(successNoticeMock.mock.calls).toEqual([['Successfully added CareTeams']]); + }); + + expect(nock.isDone()).toBeTruthy(); +}); + +test('1157 - editing care team works corectly', async () => { + const thisProps = { + ...props, + initialValues: getCareTeamFormFields(careTeam4201alternative), + }; + const successNoticeMock = jest + .spyOn(notifications, 'sendSuccessNotification') + .mockImplementation(() => undefined); + + nock(props.fhirBaseURL) + .put( + `/${careTeamResourceType}/${careTeam4201alternativeEdited.id}`, + careTeam4201alternativeEdited + ) + .reply(200) + .persist(); + + render(); + + await waitFor(() => { + expect(screen.getByText(/Edit Care Team /)).toBeInTheDocument(); + }); + + const nameInput = screen.getByLabelText('Name') as Element; + userEvents.type(nameInput, 'care team'); + + const activeStatusRadio = screen.getByLabelText('Active'); + expect(activeStatusRadio).toBeChecked(); + + const inactiveStatusRadio = screen.getByLabelText('Inactive'); + expect(inactiveStatusRadio).not.toBeChecked(); + userEvents.click(inactiveStatusRadio); + + // remove assigned + const selectClear = [...document.querySelectorAll('.ant-select-selection-item-remove')]; + expect(selectClear).toHaveLength(2); + selectClear.forEach((clear) => { + fireEvent.click(clear); + }); + + const practitionersInput = screen.getByLabelText('Practitioner Participant'); + userEvents.click(practitionersInput); + fireEvent.click(screen.getByTitle('Ward N 1 Williams MD')); + + const managingOrgsSelect = screen.getByLabelText('Managing organizations'); + userEvents.click(managingOrgsSelect); + fireEvent.click(screen.getByTitle('testing ash123')); + + const saveBtn = screen.getByRole('button', { name: 'Save' }); + userEvents.click(saveBtn); + + await waitFor(() => { + expect(successNoticeMock.mock.calls).toEqual([['Successfully updated CareTeams']]); + }); + + expect(nock.isDone()).toBeTruthy(); +}); diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/utils.test.ts b/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/utils.test.ts deleted file mode 100644 index bfdc9f7cd..000000000 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/tests/utils.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { getPatientName, submitForm } from '../utils'; -import { act } from 'react-dom/test-utils'; -import { store } from '@opensrp/store'; -import { authenticateUser } from '@onaio/session-reducer'; -import flushPromises from 'flush-promises'; -import * as fhirCient from 'fhirclient'; -import { history } from '@onaio/connected-reducer-registry'; -import * as notifications from '@opensrp/notifications'; -import { - practitioners as fixturePractitioners, - organizations, - formValues, - careTeam1, -} from './fixtures'; - -jest.mock('@opensrp/notifications', () => ({ - __esModule: true, - ...jest.requireActual('@opensrp/notifications'), -})); - -const mockV4 = '0b3a3311-6f5a-40dd-95e5-008001acebe1'; - -jest.mock('uuid', () => { - const v4 = () => mockV4; - return { __esModule: true, ...jest.requireActual('uuid'), v4 }; -}); - -describe('forms/utils/submitForm', () => { - const practitioners = fixturePractitioners.entry.map((prac) => ({ - name: getPatientName(prac.resource), - id: prac.resource.id, - })); - - beforeAll(() => { - store.dispatch( - authenticateUser( - true, - { - email: 'bob@example.com', - name: 'Bobbie', - username: 'RobertBaratheon', - }, - // eslint-disable-next-line @typescript-eslint/naming-convention - { api_token: 'hunter2', oAuth2Data: { access_token: 'sometoken', state: 'abcde' } } - ) - ); - }); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - const fhirBaseURL = 'https://r4.smarthealthit.org/'; - const id = 'cab07278-c77b-4bc7-b154-bcbf01b7d35b'; - - it('submits user creation correctly', async () => { - const notificationSuccessMock = jest.spyOn(notifications, 'sendSuccessNotification'); - const historyPushMock = jest.spyOn(history, 'push'); - - const fhir = jest.spyOn(fhirCient, 'client'); - fhir.mockImplementation( - jest.fn().mockImplementation(() => { - return { - update: jest.fn().mockResolvedValue(careTeam1), - }; - }) - ); - - submitForm(formValues, fhirBaseURL, organizations, practitioners, (t) => t, '', '').catch( - jest.fn() - ); - - await act(async () => { - await flushPromises(); - }); - - expect(notificationSuccessMock.mock.calls).toMatchObject([['Successfully Added Care Teams']]); - expect(historyPushMock).toHaveBeenCalledWith(`/admin/CareTeams`); - }); - - it('ensures error notification is not thrown when creating new care team', async () => { - const mockErrorCallback = jest.fn(); - - const fhir = jest.spyOn(fhirCient, 'client'); - fhir.mockImplementation( - jest.fn().mockImplementation(() => { - return { - create: jest.fn().mockResolvedValue(careTeam1), - }; - }) - ); - - submitForm(formValues, fhirBaseURL, organizations, practitioners, (t) => t, '', '').catch( - mockErrorCallback - ); - - await act(async () => { - await flushPromises(); - }); - expect(mockErrorCallback).not.toHaveBeenCalled(); - }); - - it('submits care team edit correctly', async () => { - const notificationSuccessMock = jest.spyOn(notifications, 'sendSuccessNotification'); - const historyPushMock = jest.spyOn(history, 'push'); - - const fhir = jest.spyOn(fhirCient, 'client'); - fhir.mockImplementation( - jest.fn().mockImplementation(() => { - return { - update: jest.fn().mockResolvedValue(careTeam1), - }; - }) - ); - - submitForm(formValues, fhirBaseURL, organizations, practitioners, (t) => t, '308', id).catch( - jest.fn() - ); - - await act(async () => { - await flushPromises(); - }); - - expect(notificationSuccessMock.mock.calls).toMatchObject([['Successfully Updated Care Teams']]); - expect(notificationSuccessMock).toHaveBeenCalledWith('Successfully Updated Care Teams'); - expect(historyPushMock).toHaveBeenCalledWith('/admin/CareTeams'); - }); - - it('handles error when user creation fails', async () => { - const notificationErrorMock = jest.spyOn(notifications, 'sendErrorNotification'); - - const fhir = jest.spyOn(fhirCient, 'client'); - fhir.mockImplementation( - jest.fn().mockImplementation(() => { - return { - create: jest.fn().mockRejectedValue('API Failed'), - }; - }) - ); - - submitForm(formValues, fhirBaseURL, organizations, practitioners, (t) => t, '', '').catch( - jest.fn() - ); - - await act(async () => { - await flushPromises(); - }); - - expect(notificationErrorMock.mock.calls).toMatchObject([['An error occurred']]); - }); - - it('handles error when user edit fails', async () => { - const notificationErrorMock = jest.spyOn(notifications, 'sendErrorNotification'); - - const fhir = jest.spyOn(fhirCient, 'client'); - fhir.mockImplementation( - jest.fn().mockImplementation(() => { - return { - update: jest.fn().mockRejectedValue('API Failed'), - }; - }) - ); - - submitForm(formValues, fhirBaseURL, organizations, practitioners, (t) => t, '308', id).catch( - jest.fn() - ); - - await act(async () => { - await flushPromises(); - }); - - expect(notificationErrorMock.mock.calls).toMatchObject([['An error occurred']]); - }); -}); diff --git a/packages/fhir-care-team/src/components/CreateEditCareTeam/utils.tsx b/packages/fhir-care-team/src/components/CreateEditCareTeam/utils.tsx index 9b54b81d3..de8b1fc7d 100644 --- a/packages/fhir-care-team/src/components/CreateEditCareTeam/utils.tsx +++ b/packages/fhir-care-team/src/components/CreateEditCareTeam/utils.tsx @@ -27,42 +27,77 @@ import { get, keyBy } from 'lodash'; import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; import { HumanName } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/humanName'; import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; +import { CareTeamParticipant } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/careTeamParticipant'; +import { Reference } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/reference'; + +/** + * computes participants that should be carried over when generating the payload + * + * @param values - current form values + * @param initialValues - initial form values + */ +const getCarriedOverParticipants = (values: FormFields, initialValues: FormFields) => { + const { participant } = initialValues.initialCareTeam ?? {}; + const participantByKey = (participant ?? []).reduce((ac, val) => { + const reference = val.member?.reference as string; + return { ...ac, [reference]: val }; + }, {}) as Record; + const currentManagingOrgValuesyId = values[managingOrganizations].reduce((ac, val) => { + return { + ...ac, + [val]: val, + }; + }, {}); + const currentpractitionersById = values[practitionerParticipants].reduce((ac, val) => { + return { + ...ac, + [val]: val, + }; + }, {}); + const cleanParticipants = (refs: string[], lookup: Record) => { + refs.forEach((ref) => { + if (!lookup[ref]) { + delete participantByKey[ref]; + } + }); + }; + cleanParticipants(initialValues.practitionerParticipants, currentpractitionersById); + cleanParticipants(initialValues.managingOrganizations, currentManagingOrgValuesyId); + return participantByKey; +}; export const submitForm = async ( values: FormFields, + initialValues: FormFields, fhirBaseURL: string, organizations: IOrganization[], practitioners: IPractitioner[], - t: TFunction, - id?: string, - uuid?: string + t: TFunction ): Promise => { - const { initialCareTeam } = values; + const { initialCareTeam, id, uuid } = initialValues; const { meta, text, participant, ...nonMetaFields } = initialCareTeam ?? {}; - const carriedOverParticipants = (participant ?? []).filter( - (participant) => - !participant.member?.reference?.startsWith(organizationResourceType) || - !participant.member.reference.startsWith(practitionerResourceType) - ); - const careTeamId = uuid ? uuid : v4(); - const practitionerParticipantPayload = values[practitionerParticipants].map((id) => { - return { + const allPractitionersById = keyBy( + practitioners, + (practitioner) => `${practitionerResourceType}/${practitioner.id}` + ); + const practitionerParticipantsById: Record = {}; + values[practitionerParticipants].forEach((id) => { + const fullPractitionerObj = allPractitionersById[id]; + practitionerParticipantsById[id] = { member: { reference: id, - display: getPatientName( - practitioners.find( - (practitioner) => `${practitionerResourceType}/${practitioner.id}` === id - ) - ), + display: getPatientName(fullPractitionerObj), }, }; }); const organizationsById = keyBy(organizations, (org) => `${organizationResourceType}/${org.id}`); - - const managingOrgsPayload = values[managingOrganizations].map((id) => { - return { + const managingOrgsById: Record = {}; + values[managingOrganizations].forEach((id) => { + const orgName = (organizationsById[id] as IOrganization | undefined)?.name; + const orgDisplay = orgName ? { display: organizationsById[id].name } : {}; + managingOrgsById[id] = { role: [ { coding: [ @@ -75,12 +110,24 @@ export const submitForm = async ( }, ], member: { + ...orgDisplay, reference: id, - display: organizationsById[id].name, }, }; }); + const carriedOverParticipantsById = getCarriedOverParticipants(values, initialValues); + const finalParticipantsById = { + ...carriedOverParticipantsById, + ...practitionerParticipantsById, + ...managingOrgsById, + }; + + const managingOrgsPayload = Object.values(managingOrgsById).map( + (obj) => obj.member + ) as Reference[]; + + const careTeamId = uuid ? uuid : v4(); const payload: Omit = { ...nonMetaFields, resourceType: FHIR_CARE_TEAM, @@ -93,17 +140,14 @@ export const submitForm = async ( id: id ? id : careTeamId, name: values.name, status: values.status as IfhirR4.CareTeam.StatusEnum, - participant: [ - ...carriedOverParticipants, - ...managingOrgsPayload, - ...practitionerParticipantPayload, - ], + participant: Object.values(finalParticipantsById), + managingOrganization: managingOrgsPayload, }; const serve = new FHIRServiceClass(fhirBaseURL, FHIR_CARE_TEAM); - let successNotificationMessage = t('Successfully Added Care Teams'); + let successNotificationMessage = t('Successfully added CareTeams'); if (id) { - successNotificationMessage = t('Successfully Updated Care Teams'); + successNotificationMessage = t('Successfully updated CareTeams'); } await serve .update(payload) @@ -146,7 +190,7 @@ export const defaultInitialValues: FormFields = { [uuid]: '', [id]: '', [name]: '', - [status]: '', + [status]: 'active', initialCareTeam: undefined, [managingOrganizations]: [], [organizationParticipants]: [], @@ -168,6 +212,10 @@ export const getCareTeamFormFields = (careTeam?: ICareTeam): FormFields => { const organizationRefs = participantRefs.filter((ref) => { return ref.startsWith(organizationResourceType); }); + const managingOrgsRefs = (careTeam.managingOrganization ?? []) + .map((ref) => ref.reference) + .filter((ref) => !!ref) as string[]; + return { uuid: get(officialIdentifier, '0.value', undefined), id, @@ -176,9 +224,7 @@ export const getCareTeamFormFields = (careTeam?: ICareTeam): FormFields => { initialCareTeam: careTeam, practitionerParticipants: practitionerRefs, organizationParticipants: organizationRefs, - managingOrganizations: (careTeam.managingOrganization ?? []) - .map((ref) => ref.reference) - .filter((item) => item !== undefined) as string[], + managingOrganizations: managingOrgsRefs, }; }; diff --git a/packages/fhir-care-team/src/components/ViewDetails/index.tsx b/packages/fhir-care-team/src/components/ViewDetails/index.tsx index b0f2e2bac..78688c978 100644 --- a/packages/fhir-care-team/src/components/ViewDetails/index.tsx +++ b/packages/fhir-care-team/src/components/ViewDetails/index.tsx @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; -import { Col, Space, Button, Alert } from 'antd'; -import { CloseOutlined } from '@ant-design/icons'; +import { Col, Button, Alert } from 'antd'; +import { CloseOutlined, SyncOutlined } from '@ant-design/icons'; import { useHistory } from 'react-router'; import { useQuery } from 'react-query'; import { @@ -11,7 +11,7 @@ import { getResourcesFromBundle, parseFhirHumanName, } from '@opensrp/react-utils'; -import { careTeamResourceType, practitionerResourceType, URL_CARE_TEAM } from '../../constants'; +import { careTeamResourceType, URL_CARE_TEAM } from '../../constants'; import { useTranslation } from '../../mls'; import { renderObjectAsKeyvalue } from '@opensrp/react-utils'; import { get, groupBy, keyBy } from 'lodash'; @@ -41,21 +41,23 @@ function categorizeIncludedResources(resources: Resource[], careTeamId: string) const subjectRef = thisCareTeam.subject?.reference; const subjectResource = subjectRef ? resByIds[subjectRef] : undefined; - const participantResources: Resource[] = []; + const participants: Resource[] = []; const managingOrganizations: IOrganization[] = []; thisCareTeam.participant?.forEach((participant) => { const ref = participant.member?.reference; - if (ref) { - participantResources.push(resByIds[ref]); + const referencedResource = resByIds[ref ?? ''] as unknown as Resource | undefined; + if (referencedResource) { + participants.push(referencedResource); } }); thisCareTeam.managingOrganization?.forEach((organization) => { const ref = organization.reference; - if (ref) { - managingOrganizations.push(resByIds[ref] as unknown as IOrganization); + const referencedResource = resByIds[ref ?? ''] as unknown as IOrganization | undefined; + if (referencedResource) { + managingOrganizations.push(referencedResource); } }); - const participantByResourceType = groupBy(participantResources, 'resourceType'); + const participantByResourceType = groupBy(participants, 'resourceType'); return { subjectResource, participantByResourceType, thisCareTeam, managingOrganizations }; } @@ -114,9 +116,9 @@ const ViewDetails = (props: ViewDetailsProps) => { )[0]; return (
  • - {resourceType === practitionerResourceType - ? parseFhirHumanName(practitionerName) - : res.name} + {typeof res.name === 'string' + ? res.name + : parseFhirHumanName(practitionerName)}
  • ); })} @@ -159,15 +161,20 @@ const ViewDetails = (props: ViewDetailsProps) => { {error && !data ? ( ) : ( - + <> {isLoading ? ( - + } + > ) : careTeam ? ( renderObjectAsKeyvalue(careTeamKeyValues) ) : ( )} - + )} ); diff --git a/packages/fhir-care-team/src/components/ViewDetails/tests/__snapshots__/index.test.tsx.snap b/packages/fhir-care-team/src/components/ViewDetails/tests/__snapshots__/index.test.tsx.snap index 61d2844e7..215e14daf 100644 --- a/packages/fhir-care-team/src/components/ViewDetails/tests/__snapshots__/index.test.tsx.snap +++ b/packages/fhir-care-team/src/components/ViewDetails/tests/__snapshots__/index.test.tsx.snap @@ -1,5 +1,214 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`1157 - view details errors out for careTeam 3500 1`] = ` +
    +
    + + CareTeam ID + +
    +
    + + 3500 + +
    +
    +`; + +exports[`1157 - view details errors out for careTeam 3500 2`] = ` +
    +
    + + Name + +
    +
    + + Peter James Charlmers Care team + +
    +
    +`; + +exports[`1157 - view details errors out for careTeam 3500 3`] = ` +
    +
    + + status + +
    +
    + + active + +
    +
    +`; + +exports[`1157 - view details errors out for careTeam 3500 4`] = ` +
    +
    + + Participants + +
    +
    + +
      +
    • +
      +
      +
      +
      + + Patient + +
      +
      + +
        +
      • + Peter James Chalmers +
      • +
      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    +`; + +exports[`1157 - view details errors out for careTeam 3500 5`] = ` +
    +
    + + Patient + +
    +
    + +
      +
    • + Peter James Chalmers +
    • +
    +
    +
    +
    +`; + +exports[`1157 - view details errors out for careTeam 3500 6`] = ` +
    +
    + + Managing organizations + +
    +
    + + + +
    +
    +`; + exports[`Closes on clicking cancel (X) 1`] = `
    Care Team
    ', + }, + contained: [ + { + resourceType: 'Practitioner', + id: '3457', + name: [ + { + family: 'Careful', + given: ['Adam'], + prefix: ['Dr'], + }, + ], + }, + ], + status: 'active', + category: [ + { + coding: [ + { + system: 'http://loinc.org', + code: 'LA27976-2', + display: 'Encounter-focused care team', + }, + ], + }, + ], + name: 'Peter James Charlmers Care team', + subject: { + reference: 'Patient/3455', + display: 'Peter James Chalmers', + }, + encounter: { + reference: 'Encounter/3458', + }, + period: { + end: '2013-01-01', + }, + participant: [ + { + role: [ + { + text: 'responsiblePerson', + }, + ], + member: { + reference: 'Patient/3455', + display: 'Peter James Chalmers', + }, + }, + { + role: [ + { + text: 'adviser', + }, + ], + member: { + reference: '#pr1', + display: 'Dorothy Dietition', + }, + onBehalfOf: { + reference: 'Organization/0000', + }, + period: { + end: '2013-01-01', + }, + }, + ], + managingOrganization: [ + { + reference: 'Organization/3461', + }, + ], + }, + search: { + mode: 'match', + }, + }, + { + fullUrl: 'https://fhir.labs.smartregister.org/fhir/Encounter/3458', + resource: { + resourceType: 'Encounter', + id: '3458', + meta: { + versionId: '2', + lastUpdated: '2021-10-25T06:53:54.230+00:00', + source: '#1350520584a57b46', + }, + text: { + status: 'generated', + div: '
    Encounter with patient @example
    ', + }, + status: 'finished', + class: { + system: 'http://terminology.hl7.org/CodeSystem/v3-ActCode', + code: 'IMP', + display: 'inpatient encounter to check on Obsesity', + }, + subject: { + reference: 'Patient/3455', + }, + }, + search: { + mode: 'include', + }, + }, + { + fullUrl: 'https://fhir.labs.smartregister.org/fhir/Patient/3455', + resource: { + resourceType: 'Patient', + id: '3455', + meta: { + versionId: '3', + lastUpdated: '2021-10-22T13:49:19.121+00:00', + source: '#70532eaf6e0ba7df', + }, + text: { + status: 'generated', + div: '
    Peter James CHALMERS
    Address534 Erewhon St
    PleasantVille Vic
    Date of birth25 December 1974
    ', + }, + active: true, + name: [ + { + use: 'official', + family: 'Chalmers', + given: ['Peter', 'James'], + }, + { + use: 'usual', + given: ['Jim'], + }, + { + use: 'maiden', + family: 'Windsor', + given: ['Peter', 'James'], + period: { + end: '2002', + }, + }, + ], + telecom: [ + { + use: 'home', + }, + { + system: 'phone', + value: '(03) 5555 6473', + use: 'work', + rank: 1, + }, + { + system: 'phone', + value: '(03) 3410 5613', + use: 'mobile', + rank: 2, + }, + { + system: 'phone', + value: '(03) 5555 8834', + use: 'old', + period: { + end: '2014', + }, + }, + ], + gender: 'male', + birthDate: '1974-12-25', + deceasedBoolean: false, + address: [ + { + use: 'home', + type: 'both', + text: '534 Erewhon St PeasantVille, Rainbow, Vic 3999', + line: ['534 Erewhon St'], + city: 'PleasantVille', + district: 'Rainbow', + state: 'Vic', + postalCode: '3999', + period: { + start: '1974-12-25', + }, + }, + ], + contact: [ + { + relationship: [ + { + coding: [ + { + system: 'http://terminology.hl7.org/CodeSystem/v2-0131', + code: 'N', + }, + ], + }, + ], + name: { + family: 'du Marché', + _family: { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/humanname-own-prefix', + valueString: 'VV', + }, + ], + }, + given: ['Bénédicte'], + }, + telecom: [ + { + system: 'phone', + value: '+33 (237) 998327', + }, + ], + address: { + use: 'home', + type: 'both', + line: ['534 Erewhon St'], + city: 'PleasantVille', + district: 'Rainbow', + state: 'Vic', + postalCode: '3999', + period: { + start: '1974-12-25', + }, + }, + gender: 'female', + period: { + start: '2012', + }, + }, + ], + managingOrganization: { + reference: 'Organization/3454', + }, + }, + search: { + mode: 'include', + }, + }, + ], +}; diff --git a/packages/fhir-care-team/src/components/ViewDetails/tests/index.test.tsx b/packages/fhir-care-team/src/components/ViewDetails/tests/index.test.tsx index a690c150a..58703bf86 100644 --- a/packages/fhir-care-team/src/components/ViewDetails/tests/index.test.tsx +++ b/packages/fhir-care-team/src/components/ViewDetails/tests/index.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Router } from 'react-router'; import { QueryClientProvider } from 'react-query'; import { ViewDetails } from '..'; -import { careTeam2, careTeamWithIncluded } from './fixtures'; +import { careTeam2, careTeam3500, careTeamWithIncluded } from './fixtures'; import { createBrowserHistory } from 'history'; import { createTestQueryClient } from '../../ListView/tests/utils'; import nock from 'nock'; @@ -125,3 +125,27 @@ test('shows broken page if fhir api is down', async () => { expect(getByText(/coughid/)).toBeInTheDocument(); }); + +test('1157 - view details errors out for careTeam 3500', async () => { + const thisProps = { + ...props, + careTeamId: '3500', + }; + nock(props.fhirBaseURL) + .get(`/${careTeamResourceType}/_search`) + .query({ _id: '3500', _include: 'CareTeam:*' }) + .reply(200, careTeam3500); + + const { queryByText } = render(); + await waitForElementToBeRemoved(queryByText(/Fetching Care team/i)); + + // see view details contents + const keyValuePairs = document.querySelectorAll( + 'div[data-testid="key-value"] .singleKeyValue-pair' + ); + keyValuePairs.forEach((pair) => { + expect(pair).toMatchSnapshot(); + }); + + expect(nock.pendingMocks()).toEqual([]); +});