diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 7291ff859..000000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: E2E Tests - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - main: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Copy test environment variables - run: cp example.env .env - - - name: Setup node - uses: actions/setup-node@v4 - with: - node-version: "18" - - - name: Cache dependencies - id: cache - uses: actions/cache@v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: yarn install --immutable - - - name: Install Playwright Browsers - run: npx playwright install chromium --with-deps - - - name: Build apps - run: yarn turbo run build --concurrency=5 - - - name: Run dev server - run: bash e2e/support/github/run-e2e-docker-env.sh - - - name: Wait for OpenMRS instance to start - run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done - - - name: Run E2E tests - run: yarn playwright test - - - name: Stop dev server - if: "!cancelled()" - run: docker stop $(docker ps -a -q) - - - name: Upload Report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 - overwrite: true diff --git a/package.json b/package.json index 70fea0b9e..06d26203e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openmrs/esm-patient-management", - "version": "7.0.0", + "version": "7.0.2", "private": true, "description": "Patient management microfrontend for the OpenMRS 3.x frontend", "workspaces": [ @@ -11,7 +11,7 @@ "ci:publish": "yarn workspaces foreach --all --topological --exclude @openmrs/esm-patient-management npm publish --access public --tag latest", "ci:prepublish": "yarn workspaces foreach --all --topological --exclude @openmrs/esm-patient-management npm publish --access public --tag next", "release": "yarn workspaces foreach --all --topological version", - "verify": "turbo run lint typescript test --concurrency=1", + "verify": "turbo run lint typescript --concurrency=1", "test-e2e": "playwright test", "postinstall": "husky install" }, diff --git a/packages/esm-active-visits-app/package.json b/packages/esm-active-visits-app/package.json index 13306bb37..bd68b364a 100644 --- a/packages/esm-active-visits-app/package.json +++ b/packages/esm-active-visits-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-active-visits-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-active-visits-app", + "version": "7.0.2", "description": "Active visits widget microfrontend for the OpenMRS SPA", - "browser": "dist/openmrs-esm-active-visits-app.js", + "browser": "dist/kenyaemr-esm-active-visits-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-active-visits-app/src/index.ts b/packages/esm-active-visits-app/src/index.ts index 74678813a..0cf73aae2 100644 --- a/packages/esm-active-visits-app/src/index.ts +++ b/packages/esm-active-visits-app/src/index.ts @@ -3,7 +3,7 @@ import { configSchema } from './config-schema'; import activeVisitsComponent from './active-visits-widget/active-visits.component'; import visitDetailComponent from './visits-summary/visit-detail.component'; -const moduleName = '@openmrs/esm-active-visits-app'; +const moduleName = '@kenyaemr/esm-active-visits-app'; const options = { featureName: 'active-visits', diff --git a/packages/esm-appointments-app/package.json b/packages/esm-appointments-app/package.json index bb14b8bac..d128f9c0f 100644 --- a/packages/esm-appointments-app/package.json +++ b/packages/esm-appointments-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-appointments-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-appointments-app", + "version": "7.0.2", "description": "Appointments front-end module for the OpenMRS SPA", - "browser": "dist/openmrs-esm-appointments-app.js", + "browser": "dist/kenyaemr-esm-appointments-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-appointments-app/src/appointments/common-components/appointments-table.test.tsx b/packages/esm-appointments-app/src/appointments/common-components/appointments-table.test.tsx index d3ddfe392..f883e5920 100644 --- a/packages/esm-appointments-app/src/appointments/common-components/appointments-table.test.tsx +++ b/packages/esm-appointments-app/src/appointments/common-components/appointments-table.test.tsx @@ -8,7 +8,7 @@ import AppointmentsTable from './appointments-table.component'; import { configSchema } from '../../config-schema'; import { getByTextWithMarkup } from '../../../../../tools/test-utils'; -defineConfigSchema('@openmrs/esm-appointments-app', configSchema); +defineConfigSchema('@kenyaemr/esm-appointments-app', configSchema); const appointments: Array = [ { diff --git a/packages/esm-appointments-app/src/constants.ts b/packages/esm-appointments-app/src/constants.ts index 5cd36ccc7..6a07cc8d8 100644 --- a/packages/esm-appointments-app/src/constants.ts +++ b/packages/esm-appointments-app/src/constants.ts @@ -4,7 +4,7 @@ export const spaHomePage = ` ${window.getOpenmrsSpaBase()}home`; export const omrsDateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZZ'; export const appointmentLocationTagName = 'Appointment Location'; -export const moduleName = '@openmrs/esm-appointments-app'; +export const moduleName = '@kenyaemr/esm-appointments-app'; export const datePickerPlaceHolder = 'dd/mm/yyyy'; export const dateFormat = 'DD/MM/YYYY'; diff --git a/packages/esm-appointments-app/src/form/appointments-form.component.tsx b/packages/esm-appointments-app/src/form/appointments-form.component.tsx index 4747b8171..f89a31d0d 100644 --- a/packages/esm-appointments-app/src/form/appointments-form.component.tsx +++ b/packages/esm-appointments-app/src/form/appointments-form.component.tsx @@ -275,22 +275,22 @@ const AppointmentsForm: React.FC = ({ const appointmentPayload = constructAppointmentPayload(data); // check if Duplicate Response Occurs - const response: FetchResponse = await checkAppointmentConflict(appointmentPayload); - let errorMessage = t('appointmentConflict', 'Appointment conflict'); - if (response?.data?.hasOwnProperty('SERVICE_UNAVAILABLE')) { - errorMessage = t('serviceUnavailable', 'Appointment time is outside of service hours'); - } else if (response?.data?.hasOwnProperty('PATIENT_DOUBLE_BOOKING')) { - errorMessage = t('patientDoubleBooking', 'Patient already booked for an appointment at this time'); - } - if (response.status === 200) { - setIsSubmitting(false); - showSnackbar({ - isLowContrast: true, - kind: 'error', - title: errorMessage, - }); - return; - } + // const response: FetchResponse = await checkAppointmentConflict(appointmentPayload); + // let errorMessage = t('appointmentConflict', 'Appointment conflict'); + // if (response?.data?.hasOwnProperty('SERVICE_UNAVAILABLE')) { + // errorMessage = t('serviceUnavailable', 'Appointment time is outside of service hours'); + // } else if (response?.data?.hasOwnProperty('PATIENT_DOUBLE_BOOKING')) { + // errorMessage = t('patientDoubleBooking', 'Patient already booked for an appointment at this time'); + // } + // if (response.status === 200) { + // setIsSubmitting(false); + // showSnackbar({ + // isLowContrast: true, + // kind: 'error', + // title: errorMessage, + // }); + // return; + // } // Construct recurring pattern payload const recurringAppointmentPayload = { diff --git a/packages/esm-appointments-app/src/index.ts b/packages/esm-appointments-app/src/index.ts index 1f7e09cbd..0a62105e0 100644 --- a/packages/esm-appointments-app/src/index.ts +++ b/packages/esm-appointments-app/src/index.ts @@ -29,7 +29,7 @@ import appointementsForm from './form/appointments-form.component'; import patientSearch from './patient-search/patient-search.component'; export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); -const moduleName = '@openmrs/esm-appointments-app'; +const moduleName = '@kenyaemr/esm-appointments-app'; const options = { featureName: 'appointments', diff --git a/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx b/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx index d57e1274b..0c85151d6 100644 --- a/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx +++ b/packages/esm-appointments-app/src/patient-appointments/patient-upcoming-appointments-card.component.tsx @@ -44,13 +44,6 @@ const PatientUpcomingAppointmentsCard: React.FC appointment.status !== 'CheckedIn'); - useEffect(() => { - if (appointments.length === 1) { - setSelectedAppointment(appointments[0]); - setUpcomingAppointment(appointments[0]); - } - }, [appointments]); - const handleRadioChange = (appointment: Appointment) => { setSelectedAppointment(appointment); setUpcomingAppointment(appointment); diff --git a/packages/esm-patient-list-management-app/package.json b/packages/esm-patient-list-management-app/package.json index dd8e2bd2f..492e67ba0 100644 --- a/packages/esm-patient-list-management-app/package.json +++ b/packages/esm-patient-list-management-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-patient-list-management-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-patient-list-management-app", + "version": "7.0.2", "description": "Microfrontend for managing patient lists in O3", - "browser": "dist/openmrs-esm-patient-list-management-app.js", + "browser": "dist/kenyaemr-esm-patient-list-management-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-patient-list-management-app/src/index.ts b/packages/esm-patient-list-management-app/src/index.ts index 2ab3189ce..49ac4afe2 100644 --- a/packages/esm-patient-list-management-app/src/index.ts +++ b/packages/esm-patient-list-management-app/src/index.ts @@ -7,7 +7,7 @@ import rootComponent from './root.component'; import listDetailsTableComponent from './list-details-table/list-details-table.component'; import addPatientToPatientListMenuItemComponent from './add-patient-to-patient-list-menu-item.component'; -const moduleName = '@openmrs/esm-patient-list-management-app'; +const moduleName = '@kenyaemr/esm-patient-list-management-app'; const options = { featureName: 'patient list', diff --git a/packages/esm-patient-list-management-app/src/list-details/patient-list-detail.test.tsx b/packages/esm-patient-list-management-app/src/list-details/patient-list-detail.test.tsx new file mode 100644 index 000000000..5aee11bd5 --- /dev/null +++ b/packages/esm-patient-list-management-app/src/list-details/patient-list-detail.test.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { usePatientListDetails, usePatientListMembers } from '../api/hooks'; +import { showToast } from '@openmrs/esm-framework'; +import { deletePatientList } from '../api/api-remote'; +import PatientListDetailComponent from './patient-list-detail.component'; + +const mockedUsePatientListDetails = usePatientListDetails as jest.Mock; +const mockedUsePatientListMembers = usePatientListMembers as jest.Mock; +const mockedDeletePatientList = deletePatientList as jest.Mock; + +jest.mock('../api/hooks', () => ({ + usePatientListDetails: jest.fn(), + usePatientListMembers: jest.fn(), +})); + +jest.mock('../api/api-remote'); + +jest.mock('@openmrs/esm-framework', () => ({ + ...jest.requireActual('@openmrs/esm-framework'), + showToast: jest.fn(), + navigate: jest.fn(), + ExtensionSlot: jest.fn(), +})); + +const mockedPatientListDetails = { + name: 'Test Patient List', + description: 'This is a test patient list', + size: 5, + startDate: '2023-08-14', +}; + +const mockedPatientListMembers = [ + { + patient: { + person: { + display: 'John Doe', + gender: 'Male', + }, + identifiers: [ + { + identifier: '100GEJ', + }, + ], + uuid: '7cd38a6d-377e-491b-8284-b04cf8b8c6d8', + }, + startDate: '2023-08-10', + }, +]; + +describe('PatientListDetailComponent', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockedUsePatientListDetails.mockReturnValue({ + data: mockedPatientListDetails, + }); + + mockedUsePatientListMembers.mockReturnValue({ + data: mockedPatientListMembers, + }); + + mockedDeletePatientList.mockResolvedValue({}); + }); + + it('renders patient list details', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Test Patient List')).toBeInTheDocument(); + expect(screen.getByText('This is a test patient list')).toBeInTheDocument(); + }); + }); + + it('displays patient list members', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.getByText('Male')).toBeInTheDocument(); + expect(screen.getByText('100GEJ')).toBeInTheDocument(); + }); + }); + + it('opens edit overlay when "Edit Name/ Description" is clicked', () => { + render(); + + userEvent.click(screen.getByText('Actions')); + const editBtn = screen.getByText('Edit Name/ Description'); + userEvent.click(editBtn); + }); + + it('deletes patient list and navigates on successful delete', async () => { + render(); + + await waitFor(() => { + userEvent.click(screen.getByText('Delete')); + }); + + await waitFor(() => { + expect(mockedDeletePatientList).toHaveBeenCalledTimes(1); + expect(showToast).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/esm-patient-registration-app/package.json b/packages/esm-patient-registration-app/package.json index 54c22acab..0f42bb4c5 100644 --- a/packages/esm-patient-registration-app/package.json +++ b/packages/esm-patient-registration-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-patient-registration-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-patient-registration-app", + "version": "7.0.2", "description": "Patient registration microfrontend for the OpenMRS SPA", - "browser": "dist/openmrs-esm-patient-registration-app.js", + "browser": "dist/kenyaemr-esm-patient-registration-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-patient-registration-app/src/config-schema.ts b/packages/esm-patient-registration-app/src/config-schema.ts index 9e1e0e303..ad31025e4 100644 --- a/packages/esm-patient-registration-app/src/config-schema.ts +++ b/packages/esm-patient-registration-app/src/config-schema.ts @@ -20,6 +20,11 @@ export interface FieldDefinition { }; answerConceptSetUuid?: string; customConceptAnswers?: Array; + showWhenExpression?: { + field: string; + value: string; + }; + renderType?: string; } export interface CustomConceptAnswer { uuid: string; diff --git a/packages/esm-patient-registration-app/src/constants.ts b/packages/esm-patient-registration-app/src/constants.ts index 11ef70c5c..1782acc78 100644 --- a/packages/esm-patient-registration-app/src/constants.ts +++ b/packages/esm-patient-registration-app/src/constants.ts @@ -6,7 +6,7 @@ export const personRelationshipRepresentation = 'personB:(age,display,birthdate,uuid),' + 'relationshipType:(uuid,display,description,aIsToB,bIsToA))'; -export const moduleName = '@openmrs/esm-patient-registration-app'; +export const moduleName = '@kenyaemr/esm-patient-registration-app'; export const patientRegistration = 'patient-registration'; export const cacheForOfflineHeaders: OmrsOfflineHttpHeaders = { diff --git a/packages/esm-patient-registration-app/src/index.ts b/packages/esm-patient-registration-app/src/index.ts index d31ceb58a..086e14105 100644 --- a/packages/esm-patient-registration-app/src/index.ts +++ b/packages/esm-patient-registration-app/src/index.ts @@ -66,3 +66,13 @@ export const deleteIdentifierConfirmationModal = getAsyncLifecycle( () => import('./widgets/delete-identifier-confirmation.modal'), options, ); + +export const confirmClientRegistryModal = getAsyncLifecycle( + () => import('./patient-verification/verification-modal/confirm-prompt.component'), + options, +); + +export const emptyClientRegistryModal = getAsyncLifecycle( + () => import('./patient-verification/verification-modal/empty-prompt.component'), + options, +); diff --git a/packages/esm-patient-registration-app/src/offline.resources.ts b/packages/esm-patient-registration-app/src/offline.resources.ts index f0d91f3f7..fa1967bfa 100644 --- a/packages/esm-patient-registration-app/src/offline.resources.ts +++ b/packages/esm-patient-registration-app/src/offline.resources.ts @@ -83,11 +83,13 @@ export async function fetchPatientIdentifierTypesWithSources(): Promise { - const option = find(autoGenOptions.data.results, { source: { uuid: source.uuid } }); - source.autoGenerationOption = option; - return source; - }); + identifierTypes[i].identifierSources = allIdentifierSources + .filter((source) => source.identifierType.uuid === identifierTypes[i].uuid) + .map((source) => { + const option = find(autoGenOptions.data.results, { source: { uuid: source.uuid } }); + source.autoGenerationOption = option; + return source; + }); } return identifierTypes; @@ -107,14 +109,7 @@ async function fetchPatientIdentifierTypes(): Promise type.uuid === primaryIdentifierTypeUuid), - true, - ), - ] - : []; + let identifierTypes = []; patientIdentifierTypes.forEach((type) => { if (type.uuid !== primaryIdentifierTypeUuid) { diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/custom-field.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/custom-field.component.tsx index f8316a022..04a78f6c2 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/custom-field.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/custom-field.component.tsx @@ -4,6 +4,7 @@ import { type RegistrationConfig } from '../../config-schema'; import { AddressField } from './address/custom-address-field.component'; import { ObsField } from './obs/obs-field.component'; import { PersonAttributeField } from './person-attributes/person-attribute-field.component'; +import { useField } from 'formik'; export interface CustomFieldProps { name: string; @@ -13,6 +14,11 @@ export function CustomField({ name }: CustomFieldProps) { const config = useConfig() as RegistrationConfig; const fieldDefinition = config.fieldDefinitions.filter((def) => def.id == name)[0]; + const [{ value }] = useField(`attributes.${fieldDefinition.showWhenExpression?.field}`); + if (fieldDefinition.showWhenExpression && value !== fieldDefinition.showWhenExpression.value) { + return null; + } + if (fieldDefinition.type === 'person attribute') { return ; } else if (fieldDefinition.type === 'obs') { diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx new file mode 100644 index 000000000..9f46079d9 --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Field } from 'formik'; +import { Layer, Select, SelectItem } from '@carbon/react'; +import { type PersonAttributeTypeResponse } from '../../patient-registration.types'; +import { useTranslation } from 'react-i18next'; +import styles from './../field.scss'; +import classNames from 'classnames'; + +type CustomPersonAttributeFieldProps = { + id: string; + personAttributeType: PersonAttributeTypeResponse; + answerConceptSetUuid: string; + label?: string; + customConceptAnswers: Array<{ uuid: string; label?: string }>; + required: boolean; +}; + +const CustomPersonAttributeField: React.FC = ({ + personAttributeType, + required, + id, + label, + customConceptAnswers, +}) => { + const { t } = useTranslation(); + const fieldName = `attributes.${personAttributeType.uuid}`; + + return ( +
+ + + {({ field, form: { touched, errors }, meta }) => { + return ( + <> + + + ); + }} + + +
+ ); +}; + +export default CustomPersonAttributeField; diff --git a/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx index fe16305a8..ad18adff7 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx @@ -6,6 +6,7 @@ import { usePersonAttributeType } from './person-attributes.resource'; import { TextPersonAttributeField } from './text-person-attribute-field.component'; import { useTranslation } from 'react-i18next'; import styles from '../field.scss'; +import CustomPersonAttributeField from './custom-person-attribute-field.component'; export interface PersonAttributeFieldProps { fieldDefinition: FieldDefinition; @@ -22,13 +23,26 @@ export function PersonAttributeField({ fieldDefinition }: PersonAttributeFieldPr switch (personAttributeType.format) { case 'java.lang.String': return ( - + <> + {fieldDefinition.renderType === 'select' ? ( + + ) : ( + + )} + ); case 'org.openmrs.Concept': return ( diff --git a/packages/esm-patient-registration-app/src/patient-registration/form-manager.ts b/packages/esm-patient-registration-app/src/patient-registration/form-manager.ts index 2019fc4ad..8ba3b2993 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/form-manager.ts +++ b/packages/esm-patient-registration-app/src/patient-registration/form-manager.ts @@ -191,7 +191,6 @@ export class FormManager { ); } else { const encounterToSave: Encounter = { - encounterDatetime: new Date(), patient: savePatientResponse.data.uuid, encounterType: config.registrationObs.encounterTypeUuid, location: currentLocation, diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts index 8768fc8a0..a6f53c35d 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration-hooks.ts @@ -7,12 +7,14 @@ import { usePatient, restBaseUrl, } from '@openmrs/esm-framework'; +import last from 'lodash-es/last'; import camelCase from 'lodash-es/camelCase'; import { type Dispatch, useEffect, useMemo, useState } from 'react'; import useSWR from 'swr'; import { v4 } from 'uuid'; import { type RegistrationConfig } from '../config-schema'; import { patientRegistration } from '../constants'; +import { useConceptAnswers, useGlobalProperties } from '../patient-verification/patient-verification-hook'; import { type FormValues, type PatientRegistration, @@ -20,6 +22,8 @@ import { type PersonAttributeResponse, type PatientIdentifierResponse, type Encounter, + type ConceptAnswers, + type ObsResponse, } from './patient-registration.types'; import { getAddressFieldValuesFromFhirPatient, @@ -32,12 +36,13 @@ import { useInitialPatientRelationships } from './section/patient-relationships/ import dayjs from 'dayjs'; export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch] { + const { martialStatus, education, occupation, educationLoad } = useConcepts(); const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid); const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid); const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid); const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(patientUuid); - const { data: encounters } = useInitialEncounters(patientUuid, patientToEdit); - + const { data: obs, isLoading: isLoadingObs, obs: observations } = usePatientObs(patientUuid); + const { data: token, isLoading: isLoadingToken } = useGlobalProperties(); const [initialFormValues, setInitialFormValues] = useState({ patientUuid: v4(), givenName: '', @@ -96,6 +101,14 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch })(); }, [isLoadingPatientToEdit, patientToEdit, patientUuid]); + // Setting authentication token + + useEffect(() => { + if (!isLoadingToken && token) { + setInitialFormValues((initialFormValues) => ({ ...initialFormValues, token: String(token.access_token) })); + } + }, [isLoadingToken, token]); + // Set initial patient relationships useEffect(() => { if (!isLoadingRelationships && relationships) { @@ -134,15 +147,24 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch } }, [attributes, setInitialFormValues, isLoadingAttributes]); - // Set Initial registration encounters + // Set initial patient obs + useEffect(() => { - if (patientToEdit && encounters) { + if (!isLoadingObs) { + setInitialFormValues((initialFormValues) => ({ ...initialFormValues, obs: obs, observation: observations })); + } + }, [isLoadingObs]); + + // Set Initial encounter + + useEffect(() => { + if (!educationLoad) { setInitialFormValues((initialFormValues) => ({ ...initialFormValues, - obs: encounters as Record, + concepts: [...occupation, ...martialStatus, ...education], })); } - }, [encounters, patientToEdit]); + }, [educationLoad]); return [initialFormValues, setInitialFormValues]; } @@ -286,3 +308,57 @@ function getPatientAttributeUuidMapForPatient(attributes: Array(patientUuid ? url : null, openmrsFetch); + let obsObject = {}; + const patientObs = last(data?.data?.results)?.obs?.forEach((ob) => { + Object.assign(obsObject, { [ob.concept.uuid]: ob.value.uuid }); + }); + return { data: obsObject, isLoading, error, obs: data?.data }; +} + +function useConcepts() { + const config = useConfig(); + const { data: education, isLoading: educationLoad } = useConceptAnswers('1712AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); + const occupation: Array = [ + { + uuid: '1538AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Farmer', + }, + { + uuid: '1540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Employee', + }, + { + uuid: '1539AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Trader', + }, + { + uuid: '159465AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Student', + }, + { + uuid: '159466AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Driver', + }, + { + uuid: '1107AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'None', + }, + { + uuid: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Other', + }, + ]; + + const martialStatus: Array = config.fieldDefinitions + .find((fieldDefinition) => fieldDefinition.id === 'maritalStatus') + .customConceptAnswers.map((concept) => ({ uuid: concept.uuid, display: concept.label })); + + return { martialStatus, education, occupation, educationLoad }; +} diff --git a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.component.tsx b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.component.tsx index 843aa650e..7760e45ef 100644 --- a/packages/esm-patient-registration-app/src/patient-registration/patient-registration.component.tsx +++ b/packages/esm-patient-registration-app/src/patient-registration/patient-registration.component.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useContext, useMemo, useRef } from 'react'; import classNames from 'classnames'; import { Button, Link, InlineLoading } from '@carbon/react'; -import { XAxis } from '@carbon/react/icons'; +import { XAxis, ShareKnowledge } from '@carbon/react/icons'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Formik, Form, type FormikHelpers } from 'formik'; @@ -18,13 +18,25 @@ import { type FormValues, type CapturePhotoProps } from './patient-registration. import { PatientRegistrationContext } from './patient-registration-context'; import { type SavePatientForm, SavePatientTransactionManager } from './form-manager'; import { DummyDataInput } from './input/dummy-data/dummy-data-input.component'; -import { cancelRegistration, filterUndefinedPatientIdenfier, scrollIntoView } from './patient-registration-utils'; -import { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks'; +import { + cancelRegistration, + filterUndefinedPatientIdenfier, + parseAddressTemplateXml, + scrollIntoView, +} from './patient-registration-utils'; +import { + useInitialAddressFieldValues, + useInitialFormValues, + usePatientObs, + usePatientUuidMap, +} from './patient-registration-hooks'; import { ResourcesContext } from '../offline.resources'; import { builtInSections, type RegistrationConfig, type SectionDefinition } from '../config-schema'; import { SectionWrapper } from './section/section-wrapper.component'; import BeforeSavePrompt from './before-save-prompt'; import styles from './patient-registration.scss'; +import PatientVerification from '../patient-verification/patient-verification.component'; +import { handleSavePatientToClientRegistry } from '../patient-verification/patient-verification-hook'; let exportedInitialFormValuesForTesting = {} as FormValues; @@ -52,6 +64,9 @@ export const PatientRegistration: React.FC = ({ savePa const savePatientTransactionManager = useRef(new SavePatientTransactionManager()); const fieldDefinition = config?.fieldDefinitions?.filter((def) => def.type === 'address'); const validationSchema = getValidationSchema(config); + const [enableClientRegistry, setEnableClientRegistry] = useState( + inEditMode ? initialFormValues.identifiers['nationalUniquePatientIdentifier']?.identifierValue : false, + ); useEffect(() => { exportedInitialFormValuesForTesting = initialFormValues; @@ -106,6 +121,26 @@ export const PatientRegistration: React.FC = ({ savePa setTarget(redirectUrl); } catch (error) { + const fieldErrors = Object.entries(error.responseBody?.error?.fieldErrors || {}) as Array< + [string, Array<{ code: string; message: string }>] + >; + + if (savePatientTransactionManager.current.patientSaved) { + fieldErrors.forEach(([field, error]) => { + const errorMessage = error.map((e) => e.message).join(', '); + showSnackbar({ + subtitle: errorMessage, + title: `Patient registration successful, with errors for registration encounter`, + kind: 'warning', + timeoutInMs: 8000, + }); + }); + const afterUrl = new URLSearchParams(search).get('afterUrl'); + const redirectUrl = interpolateUrl(afterUrl || config.links.submitButton, { patientUuid: values.patientUuid }); + + setTarget(redirectUrl); + } + if (error.responseBody?.error?.globalErrors) { error.responseBody.error.globalErrors.forEach((error) => { showSnackbar({ @@ -126,6 +161,7 @@ export const PatientRegistration: React.FC = ({ savePa }); } else { createErrorHandler()(error); + console.error(error); } helpers.setSubmitting(false); @@ -172,6 +208,18 @@ export const PatientRegistration: React.FC = ({ savePa ))} + + ) : ( + + )} + + + + ); +}; + +export default PatientVerification; diff --git a/packages/esm-patient-registration-app/src/patient-verification/patient-verification.scss b/packages/esm-patient-registration-app/src/patient-verification/patient-verification.scss new file mode 100644 index 000000000..044c03564 --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-verification/patient-verification.scss @@ -0,0 +1,25 @@ +@use '@carbon/colors'; +@import '../patient-registration/patient-registration.scss'; + +/* Desktop */ +:global(.omrs-breakpoint-gt-tablet) { + .verificationWrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + column-gap: 0.325rem; + align-items: flex-end; + } +} + +/* Tablet */ +:global(.omrs-breakpoint-lt-desktop) { + .verificationWrapper { + row-gap: 0.5rem; + display: flex; + flex-direction: column; + } +} + +.errorWrapper { + margin: 0 0 1rem 0; +} diff --git a/packages/esm-patient-registration-app/src/patient-verification/verification-modal/confirm-prompt.component.tsx b/packages/esm-patient-registration-app/src/patient-verification/verification-modal/confirm-prompt.component.tsx new file mode 100644 index 000000000..f3634b1fc --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-verification/verification-modal/confirm-prompt.component.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from '@carbon/react'; +import { age, ExtensionSlot, formatDate } from '@openmrs/esm-framework'; +import capitalize from 'lodash-es/capitalize'; +import { useFacilityName } from '../patient-verification-hook'; + +const PatientInfo: React.FC<{ label: string; value: string }> = ({ label, value }) => { + return ( +
+ {label} + {value} +
+ ); +}; + +interface ConfirmPromptProps { + onConfirm: void; + close: void; + patient: any; +} + +const ConfirmPrompt: React.FC = ({ close, onConfirm, patient }) => { + const { t } = useTranslation(); + return ( + <> +
+

+ {t('clientRegistryEmpty', `Patient ${patient?.firstName} ${patient?.lastName} found`)} +

+
+
+

+ {t( + 'patientDetailsFound', + 'Patient information found in the registry, do you want to use the information to continue with registration?', + )} +

+
+ +
+ + + + + + + +
+
+
+
+ + +
+ + ); +}; + +export default ConfirmPrompt; diff --git a/packages/esm-patient-registration-app/src/patient-verification/verification-modal/empty-prompt.component.tsx b/packages/esm-patient-registration-app/src/patient-verification/verification-modal/empty-prompt.component.tsx new file mode 100644 index 000000000..528cae6f7 --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-verification/verification-modal/empty-prompt.component.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from '@carbon/react'; + +interface EmptyPromptProps { + onConfirm: void; + close: void; +} + +const EmptyPrompt: React.FC = ({ close, onConfirm }) => { + const { t } = useTranslation(); + return ( + <> +
+

{t('clientRegistryEmpty', 'Create & Post Patient')}

+
+
+

+ {t( + 'patientNotFound', + 'The patient records could not be found in Client registry, do you want to continue to create and post patient to registry', + )} +

+
+
+ + +
+ + ); +}; + +export default EmptyPrompt; diff --git a/packages/esm-patient-registration-app/src/patient-verification/verification-types.ts b/packages/esm-patient-registration-app/src/patient-verification/verification-types.ts new file mode 100644 index 000000000..e36264873 --- /dev/null +++ b/packages/esm-patient-registration-app/src/patient-verification/verification-types.ts @@ -0,0 +1,50 @@ +export interface ClientIdentification { + identificationType: string; + identificationNumber: string; +} + +interface ClientContact { + primaryPhone: string; + secondaryPhone?: string; + emailAddress?: string; +} + +export interface ClientRegistryPatient { + clientExists: boolean; + client?: RegistryPatient; +} + +export interface RegistryPatient { + clientNumber?: string; + firstName: string; + middleName: string; + lastName: string; + dateOfBirth: string; + maritalStatus?: string; + gender: string; + occupation?: string; + religion?: string; + educationLevel?: string; + country: string; + countyOfBirth?: string; + isAlive: boolean; + originFacilityKmflCode?: string; + isOnART?: string; + nascopCCCNumber?: string; + residence: { + county: string; + subCounty: string; + ward: string; + village: string; + landmark: string; + address: string; + }; + identifications: Array; + contact: ClientContact; + nextOfKins: Array<{ + name: string; + relationship: string; + residence: string; + contact: ClientContact; + }>; +} diff --git a/packages/esm-patient-registration-app/src/routes.json b/packages/esm-patient-registration-app/src/routes.json index 3910bdbf1..73c318d80 100644 --- a/packages/esm-patient-registration-app/src/routes.json +++ b/packages/esm-patient-registration-app/src/routes.json @@ -57,6 +57,19 @@ "name": "delete-identifier-confirmation-modal", "online": true, "offline": true + }, + { + "component": "emptyClientRegistryModal", + "name": "empty-client-registry-modal", + "online": true, + "offline": true + }, + { + "component": "confirmClientRegistryModal", + "name": "confirm-client-registry-modal", + "online": true, + "offline": true } ] } + diff --git a/packages/esm-patient-registration-app/translations/am.json b/packages/esm-patient-registration-app/translations/am.json index b67e9967d..2aa2caf68 100644 --- a/packages/esm-patient-registration-app/translations/am.json +++ b/packages/esm-patient-registration-app/translations/am.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "Add Relationship", "addressHeader": "Address", + "age": "Age", "allFieldsRequiredText": "All fields are required unless marked optional", "autoGeneratedPlaceholderText": "Auto-generated", "birthdayNotInTheFuture": "Birthday cannot be in the future", @@ -8,6 +9,8 @@ "birthFieldLabelText": "Birth", "cancel": "Cancel", "causeOfDeathInputLabel": "Cause of Death", + "clientRegistryEmpty": "Create & Post Patient", + "clientVerificationWithClientRegistry": "Client verification with client registry", "closeOverlay": "Close overlay", "codedPersonAttributeAnswerSetEmpty": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an answer concept set UUID '{{answerConceptSetUuid}}' that does not have any concept answers.", "codedPersonAttributeAnswerSetInvalid": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an invalid answer concept set UUID '{{answerConceptSetUuid}}'.", @@ -16,6 +19,9 @@ "configureIdentifiers": "Configure identifiers", "contactSection": "Contact Details", "createNewPatient": "Create new patient", + "continue": "Continue to registration", + "createNew": "Create New", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "Date of Birth", "deathDateInputLabel": "Date of Death", "deathdayNotInTheFuture": "Death day cannot be in the future", @@ -38,6 +44,7 @@ "familyNameRequired": "Family name is required", "female": "Female", "fullNameLabelText": "Full Name", + "gender": "Gender", "genderLabelText": "Sex", "genderRequired": "Gender is required", "genderUnspecified": "Gender is not specified", @@ -59,9 +66,16 @@ "numberInNameDubious": "Number in name is dubious", "obsFieldUnknownDatatype": "Concept for obs field '{{fieldDefinitionId}}' has unknown datatype '{{datatypeName}}'", "optional": "optional", + "originFacilityCode": "Origin facility code", + "originFacilityName": "Origin facility name", "other": "Other", + "patient": "Patient", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "Patient's Name is Known?", + "patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry", "patientRegistrationBreadcrumb": "Patient Registration", + "postToRegistry": "Post to registry", "registerPatient": "Register Patient", "registerPatientSuccessSnackbarSubtitle": "The patient can now be found by searching for them using their name or ID number", "registerPatientSuccessSnackbarTitle": "New Patient Created", @@ -79,6 +93,8 @@ "searchAddress": "Search address", "searchIdentifierPlaceholder": "Search identifier", "selectAnOption": "Select an option", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "Sex", "source": "Source", "stroke": "Stroke", diff --git a/packages/esm-patient-registration-app/translations/ar.json b/packages/esm-patient-registration-app/translations/ar.json index 90d5e8fc1..15b4534b3 100644 --- a/packages/esm-patient-registration-app/translations/ar.json +++ b/packages/esm-patient-registration-app/translations/ar.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "أضف علاقة", "addressHeader": "العنوان", + "age": "Age", "allFieldsRequiredText": "جميع الحقول مطلوبة ما لم يتم التأشير عليها بأنها اختيارية", "autoGeneratedPlaceholderText": "تم إنشاؤه تلقائيًا", "birthdayNotInTheFuture": "Birthday cannot be in the future", @@ -8,6 +9,8 @@ "birthFieldLabelText": "الميلاد", "cancel": "إلغاء", "causeOfDeathInputLabel": "سبب الوفاة", + "clientRegistryEmpty": "Create & Post Patient", + "clientVerificationWithClientRegistry": "Client verification with client registry", "closeOverlay": "Close overlay", "codedPersonAttributeAnswerSetEmpty": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an answer concept set UUID '{{answerConceptSetUuid}}' that does not have any concept answers.", "codedPersonAttributeAnswerSetInvalid": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an invalid answer concept set UUID '{{answerConceptSetUuid}}'.", @@ -16,6 +19,9 @@ "configureIdentifiers": "Configure identifiers", "contactSection": "تفاصيل الاتصال", "createNewPatient": "Create new patient", + "continue": "Continue to registration", + "createNew": "أنشئ جديد", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "تاريخ الميلاد", "deathDateInputLabel": "تاريخ الوفاة", "deathdayNotInTheFuture": "Death day cannot be in the future", @@ -38,6 +44,7 @@ "familyNameRequired": "Family name is required", "female": "أنثى", "fullNameLabelText": "الاسم الكامل", + "gender": "Gender", "genderLabelText": "الجنس", "genderRequired": "Gender is required", "genderUnspecified": "Gender is not specified", @@ -59,9 +66,16 @@ "numberInNameDubious": "Number in name is dubious", "obsFieldUnknownDatatype": "Concept for obs field '{{fieldDefinitionId}}' has unknown datatype '{{datatypeName}}'", "optional": "اختياري", + "originFacilityCode": "Origin facility code", + "originFacilityName": "Origin facility name", "other": "آخر", + "patient": "المريض", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "هل اسم المريض معروف؟", + "patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry", "patientRegistrationBreadcrumb": "Patient Registration", + "postToRegistry": "Post to registry", "registerPatient": "تسجيل المريض", "registerPatientSuccessSnackbarSubtitle": "The patient can now be found by searching for them using their name or ID number", "registerPatientSuccessSnackbarTitle": "New Patient Created", @@ -79,6 +93,8 @@ "searchAddress": "ابحث عن العنوان", "searchIdentifierPlaceholder": "Search identifier", "selectAnOption": "اختر خيارًا", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "الجنس", "source": "Source", "stroke": "جلطة", diff --git a/packages/esm-patient-registration-app/translations/en.json b/packages/esm-patient-registration-app/translations/en.json index ed4e4dfc4..e64343807 100644 --- a/packages/esm-patient-registration-app/translations/en.json +++ b/packages/esm-patient-registration-app/translations/en.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "Add Relationship", "addressHeader": "Address", + "age": "Age", "allFieldsRequiredText": "All fields are required unless marked optional", "autoGeneratedPlaceholderText": "Auto-generated", "birthdayNotInTheFuture": "Birthday cannot be in the future", @@ -8,6 +9,10 @@ "birthFieldLabelText": "Birth", "cancel": "Cancel", "causeOfDeathInputLabel": "Cause of Death", + "clientRegistryEmpty": "Create & Post Patient", + "clientRegistryError": "Error occurred while reaching the client registry", + "clientRegistryErrorSubtitle": "Please proceed with registration contact system admin and try again later", + "clientVerificationWithClientRegistry": "Client verification with client registry", "closeOverlay": "Close overlay", "codedPersonAttributeAnswerSetEmpty": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an answer concept set UUID '{{answerConceptSetUuid}}' that does not have any concept answers.", "codedPersonAttributeAnswerSetInvalid": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an invalid answer concept set UUID '{{answerConceptSetUuid}}'.", @@ -16,7 +21,9 @@ "configureIdentifiers": "Configure identifiers", "confirmIdentifierDeletionText": "Are you sure you want to remove this identifier?", "contactSection": "Contact Details", + "continue": "Continue", "createNewPatient": "Create new patient", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "Date of Birth", "deathDateInputLabel": "Date of Death", "deathdayNotInTheFuture": "Death day cannot be in the future", @@ -41,6 +48,7 @@ "familyNameRequired": "Family name is required", "female": "Female", "fullNameLabelText": "Full Name", + "gender": "Gender", "genderLabelText": "Sex", "genderRequired": "Gender is required", "genderUnspecified": "Gender is not specified", @@ -56,15 +64,21 @@ "jumpTo": "Jump to", "male": "Male", "middleNameLabelText": "Middle Name", + "nationalId": "National ID", "negativeMonths": "Negative months", "negativeYears": "Negative years", "no": "No", "numberInNameDubious": "Number in name is dubious", + "nupi": "NUPI", "obsFieldUnknownDatatype": "Concept for obs field '{{fieldDefinitionId}}' has unknown datatype '{{datatypeName}}'", "optional": "optional", "other": "Other", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "Patient's Name is Known?", + "patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry", "patientRegistrationBreadcrumb": "Patient Registration", + "postToRegistry": "Post to registry", "registerPatient": "Register Patient", "registerPatientSuccessSnackbarSubtitle": "The patient can now be found by searching for them using their name or ID number", "registerPatientSuccessSnackbarTitle": "New Patient Created", @@ -81,9 +95,13 @@ "resetIdentifierTooltip": "Reset", "restoreRelationshipActionButton": "Undo", "searchAddress": "Search address", + "searchClientRegistry": "Search client registry", "searchIdentifierPlaceholder": "Search identifier", "selectAnOption": "Select an option", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "Sex", + "shaNumber": "SHA Number", "source": "Source", "stroke": "Stroke", "submitting": "Submitting", @@ -94,6 +112,8 @@ "updatePatientErrorSnackbarTitle": "Patient Details Update Failed", "updatePatientSuccessSnackbarSubtitle": "The patient's information has been successfully updated", "updatePatientSuccessSnackbarTitle": "Patient Details Updated", + "useValues": "Use values", + "validate": "Validate", "yearsEstimateRequired": "Years estimate required", "yes": "Yes" } diff --git a/packages/esm-patient-registration-app/translations/es.json b/packages/esm-patient-registration-app/translations/es.json index f82b05984..939510345 100644 --- a/packages/esm-patient-registration-app/translations/es.json +++ b/packages/esm-patient-registration-app/translations/es.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "Agregar relación", "addressHeader": "Dirección", + "age": "Age", "allFieldsRequiredText": "Todos los campos son obligatorios a menos que se indique como opcionales", "autoGeneratedPlaceholderText": "Autogenerado", "birthdayNotInTheFuture": "Cumpleaños no puede ser en el futuro", @@ -16,6 +17,9 @@ "configureIdentifiers": "Configurar identificadores", "contactSection": "Detalles de Contacto", "createNewPatient": "Create new patient", + "continue": "Continue to registration", + "createNew": "Crear Nuevo/a", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "Fecha de Nacimiento", "deathDateInputLabel": "Fecha de fallecimiento", "deathdayNotInTheFuture": "El día de la muerte no puede ser en el futuro", @@ -38,6 +42,7 @@ "familyNameRequired": "Apellidos es obligatorio", "female": "Femenino", "fullNameLabelText": "Nombre y Apellidos", + "gender": "Gender", "genderLabelText": "Sexo", "genderRequired": "El sexo es obligatorio", "genderUnspecified": "Género no especificado", @@ -53,6 +58,8 @@ "jumpTo": "Ir a", "male": "Masculino", "middleNameLabelText": "Segundo Nombre", + "nascopNumber": "Nascop facility no", + "nationalId": "National ID", "negativeMonths": "Meses negativos", "negativeYears": "Años negativos", "no": "No", @@ -60,6 +67,9 @@ "obsFieldUnknownDatatype": "El concepto para el campo de observación '{{fieldDefinitionId}}' tiene un tipo de datos desconocido '{{datatypeName}}'", "optional": "Opcional", "other": "Otro", + "patient": "Paciente", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "¿Se sabe el nombre del paciente?", "patientRegistrationBreadcrumb": "Registro de Pacientes", "registerPatient": "Registrar paciente", @@ -79,6 +89,8 @@ "searchAddress": "Buscar dirección", "searchIdentifierPlaceholder": "Buscar identificador", "selectAnOption": "Seleccionar una opción", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "Sexo", "source": "Fuente", "stroke": "Ictus", diff --git a/packages/esm-patient-registration-app/translations/fr.json b/packages/esm-patient-registration-app/translations/fr.json index 8b1c3a06d..e07bf02f5 100644 --- a/packages/esm-patient-registration-app/translations/fr.json +++ b/packages/esm-patient-registration-app/translations/fr.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "Ajouter un lien de parenté", "addressHeader": "Adresse", + "age": "Age", "allFieldsRequiredText": "Tous les champs sont requis sauf si explicitement indiqués facultatifs", "autoGeneratedPlaceholderText": "Auto-généré", "birthdayNotInTheFuture": "La date de naissance ne peut pas être dans le futur", @@ -16,8 +17,10 @@ "configureIdentifiers": "Configurer les identifiants", "contactSection": "Détails du contact", "createNewPatient": "Create new patient", - "dateOfBirthLabelText": "Date de Naissance", - "deathDateInputLabel": "Date de Décès", + "createNew": "Créer un nouveau", + "dateOfBirth": "Date of birth", + "dateOfBirthLabelText": "Date de naissance", + "deathDateInputLabel": "Date de décès", "deathdayNotInTheFuture": "Le décès ne peut pas être dans le futur", "deathSection": "Informations sur le décès", "deleteIdentifierTooltip": "Supprimer", @@ -59,7 +62,12 @@ "numberInNameDubious": "Le chiffre dans le nom est suspect", "obsFieldUnknownDatatype": "Le concept pour le champ d'observation '{{fieldDefinitionId}}' a un type de données inconnu '{{datatypeName}}'", "optional": "Optionnel", + "originFacilityCode": "Origin facility code", + "originFacilityName": "Origin facility name", "other": "Autre", + "patient": "Patient", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "Le nom du patient est connu?", "patientRegistrationBreadcrumb": "Enregistrement du patient", "registerPatient": "Enregistrer un patient", diff --git a/packages/esm-patient-registration-app/translations/he.json b/packages/esm-patient-registration-app/translations/he.json index 6367ec171..fb5b3e7e6 100644 --- a/packages/esm-patient-registration-app/translations/he.json +++ b/packages/esm-patient-registration-app/translations/he.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "הוסף יחס", "addressHeader": "כתובת", + "age": "Age", "allFieldsRequiredText": "כל השדות נדרשים אלא אם צוין אחרת", "autoGeneratedPlaceholderText": "נוצר אוטומטית", "birthdayNotInTheFuture": "תאריך הלידה לא יכול להיות בעתיד", @@ -8,6 +9,8 @@ "birthFieldLabelText": "לידה", "cancel": "ביטול", "causeOfDeathInputLabel": "סיבת המוות", + "clientRegistryEmpty": "Create & Post Patient", + "clientVerificationWithClientRegistry": "Client verification with client registry", "closeOverlay": "סגור חיפוש", "codedPersonAttributeAnswerSetEmpty": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an answer concept set UUID '{{answerConceptSetUuid}}' that does not have any concept answers.", "codedPersonAttributeAnswerSetInvalid": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an invalid answer concept set UUID '{{answerConceptSetUuid}}'.", @@ -16,6 +19,9 @@ "configureIdentifiers": "הגדר זיהויים", "contactSection": "פרטי יצירת קשר", "createNewPatient": "Create new patient", + "continue": "Continue to registration", + "createNew": "צור חדש", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "תאריך לידה", "deathDateInputLabel": "תאריך המוות", "deathdayNotInTheFuture": "תאריך המוות לא יכול להיות בעתיד", @@ -38,6 +44,7 @@ "familyNameRequired": "שם משפחה נדרש", "female": "נקבה", "fullNameLabelText": "שם מלא", + "gender": "Gender", "genderLabelText": "מין", "genderRequired": "מין נדרש", "genderUnspecified": "מין לא מוגדר", @@ -53,15 +60,24 @@ "jumpTo": "קפיצה ל", "male": "זכר", "middleNameLabelText": "שם תוכני", + "nascopNumber": "Nascop facility no", + "nationalId": "National ID", "negativeMonths": "חודשים שליליים", "negativeYears": "שנים שליליות", "no": "לא", "numberInNameDubious": "מספר בשם חשוד", "obsFieldUnknownDatatype": "Concept for obs field '{{fieldDefinitionId}}' has unknown datatype '{{datatypeName}}'", "optional": "אופציונלי", + "originFacilityCode": "Origin facility code", + "originFacilityName": "Origin facility name", "other": "אחר", + "patient": "מטופל", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "שם המטופל ידוע?", + "patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry", "patientRegistrationBreadcrumb": "רישום מטופל", + "postToRegistry": "Post to registry", "registerPatient": "רשום מטופל", "registerPatientSuccessSnackbarSubtitle": "The patient can now be found by searching for them using their name or ID number", "registerPatientSuccessSnackbarTitle": "New Patient Created", @@ -79,6 +95,8 @@ "searchAddress": "חיפוש כתובת", "searchIdentifierPlaceholder": "חיפוש זיהוי", "selectAnOption": "בחר אפשרות", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "מין", "source": "מקור", "stroke": "שבץ", @@ -90,6 +108,8 @@ "updatePatientErrorSnackbarTitle": "Patient Details Update Failed", "updatePatientSuccessSnackbarSubtitle": "The patient's information has been successfully updated", "updatePatientSuccessSnackbarTitle": "Patient Details Updated", + "useValues": "Use values", + "validate": "Validate", "yearsEstimateRequired": "נדרש חיזוי שנים", "yes": "כן" } diff --git a/packages/esm-patient-registration-app/translations/km.json b/packages/esm-patient-registration-app/translations/km.json index bc3faed6c..342e3f445 100644 --- a/packages/esm-patient-registration-app/translations/km.json +++ b/packages/esm-patient-registration-app/translations/km.json @@ -1,6 +1,7 @@ { "addRelationshipButtonText": "បន្ថែមទំនាក់ទំនង", "addressHeader": "អាសយដ្ឋាន", + "age": "Age", "allFieldsRequiredText": "តម្រូវឱ្យបំពេញគ្រប់កន្លែងចំហរទាំងអស់ លុះត្រាតែមានសម្គាល់ថាជាជម្រើស", "autoGeneratedPlaceholderText": "ទាញចេញទិន្នន័យដោយស្វ័យប្រវត្តិ", "birthdayNotInTheFuture": "ថ្ងៃខែឆ្នាំកំណើតមិនអាចមាននាពេលអនាគតទេ", @@ -8,6 +9,8 @@ "birthFieldLabelText": "ថ្ងៃខែឆ្នាំកំណើត", "cancel": "បោះបង់ចោល", "causeOfDeathInputLabel": "មូលហេតុនៃការស្លាប់", + "clientRegistryEmpty": "Create & Post Patient", + "clientVerificationWithClientRegistry": "Client verification with client registry", "closeOverlay": "បិទការត្រួតលើគ្នា។", "codedPersonAttributeAnswerSetEmpty": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an answer concept set UUID '{{answerConceptSetUuid}}' that does not have any concept answers.", "codedPersonAttributeAnswerSetInvalid": "The coded person attribute field '{{codedPersonAttributeFieldId}}' has been defined with an invalid answer concept set UUID '{{answerConceptSetUuid}}'.", @@ -16,6 +19,9 @@ "configureIdentifiers": "ឯកសារកំណត់អត្តសញ្ញាណផ្សេងទៀត", "contactSection": "ព័ត៌មានមរណភាព", "createNewPatient": "Create new patient", + "continue": "Continue to registration", + "createNew": "បង្កើតថ្មី", + "dateOfBirth": "Date of birth", "dateOfBirthLabelText": "ថ្ងៃខែឆ្នាំកំណើត", "deathDateInputLabel": "កាលបរិច្ឆេទនៃការស្លាប់", "deathdayNotInTheFuture": "ថ្ងៃស្លាប់មិនអាចមាននាពេលអនាគតទេ។", @@ -38,6 +44,7 @@ "familyNameRequired": "តម្រូវឱ្យបំពេញនាមត្រកូល", "female": "ស្រី", "fullNameLabelText": "ឈ្មោះពេញ", + "gender": "Gender", "genderLabelText": "ភេទ", "genderRequired": "តម្រូវឱ្យបំពេញភេទ", "genderUnspecified": "ភេទមិនត្រូវបានបញ្ជាក់ទេ", @@ -53,15 +60,24 @@ "jumpTo": "រំលងទៅ", "male": "ប្រុស", "middleNameLabelText": "លេខជាឈ្មោះដែលសង្ស័យ", + "nascopNumber": "Nascop facility no", + "nationalId": "National ID", "negativeMonths": "ខែអវិជ្ជមាន", "negativeYears": "ឆ្នាំអវិជ្ជមាន", "no": "ទេ", "numberInNameDubious": "លេខគឺជាឈ្មោះគួរឱ្យសង្ស័យ", "obsFieldUnknownDatatype": "Concept for obs field '{{fieldDefinitionId}}' has unknown datatype '{{datatypeName}}'", "optional": "ជាជម្រើស", + "originFacilityCode": "Origin facility code", + "originFacilityName": "Origin facility name", "other": "ផ្សេងៗ", + "patient": "អ្នកជំងឺ", + "patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?", + "patientName": "Patient name", "patientNameKnown": "ស្គាល់ឈ្មោះអ្នកជំងឺឬទេ?", + "patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry", "patientRegistrationBreadcrumb": "ការចុះឈ្មោះអ្នកជំងឺ", + "postToRegistry": "Post to registry", "registerPatient": "ចុះឈ្មោះអ្នកជំងឺ", "registerPatientSuccessSnackbarSubtitle": "The patient can now be found by searching for them using their name or ID number", "registerPatientSuccessSnackbarTitle": "New Patient Created", @@ -79,6 +95,8 @@ "searchAddress": "ស្វែងរកអាសយដ្ឋាន", "searchIdentifierPlaceholder": "ស្វែងរកអត្តសញ្ញាណ", "selectAnOption": "យកជម្រើសមួយ", + "selectCountry": "Select country", + "selectIdentifierType": "Select identifier type", "sexFieldLabelText": "ភេទ", "source": "ប្រភព", "stroke": "ជំងឺស្ទះសរសៃឈាមខួរក្បាល", @@ -90,6 +108,8 @@ "updatePatientErrorSnackbarTitle": "Patient Details Update Failed", "updatePatientSuccessSnackbarSubtitle": "The patient's information has been successfully updated", "updatePatientSuccessSnackbarTitle": "Patient Details Updated", + "useValues": "Use values", + "validate": "Validate", "yearsEstimateRequired": "តម្រូវឱ្យមានការប៉ាន់ស្មានឆ្នាំ", "yes": "បាទ/ចាស" } diff --git a/packages/esm-patient-search-app/package.json b/packages/esm-patient-search-app/package.json index 55404322d..2eb4d9224 100644 --- a/packages/esm-patient-search-app/package.json +++ b/packages/esm-patient-search-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-patient-search-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-patient-search-app", + "version": "7.0.2", "description": "Patient search microfrontend for the OpenMRS SPA", - "browser": "dist/openmrs-esm-patient-search-app.js", + "browser": "dist/kenyaemr-esm-patient-search-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-patient-search-app/src/index.ts b/packages/esm-patient-search-app/src/index.ts index efeb91047..0a9a6cafc 100644 --- a/packages/esm-patient-search-app/src/index.ts +++ b/packages/esm-patient-search-app/src/index.ts @@ -13,7 +13,7 @@ import patientSearchIconComponent from './patient-search-icon'; import patientSearchButtonComponent from './patient-search-button/patient-search-button.component'; import patientSearchBarComponent from './compact-patient-search-extension'; -const moduleName = '@openmrs/esm-patient-search-app'; +const moduleName = '@kenyaemr/esm-patient-search-app'; const options = { featureName: 'patient-search', diff --git a/packages/esm-service-queues-app/package.json b/packages/esm-service-queues-app/package.json index 68dbfb2cc..b3f4a74d2 100644 --- a/packages/esm-service-queues-app/package.json +++ b/packages/esm-service-queues-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-service-queues-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-service-queues-app", + "version": "7.0.2", "description": "Outpatient front-end module for the OpenMRS SPA", - "browser": "dist/openmrs-esm-service-queues-app.js", + "browser": "dist/kenyaemr-esm-service-queues-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-service-queues-app/src/hooks/useQueueEntries.ts b/packages/esm-service-queues-app/src/hooks/useQueueEntries.ts index 32ab886d4..c60196c17 100644 --- a/packages/esm-service-queues-app/src/hooks/useQueueEntries.ts +++ b/packages/esm-service-queues-app/src/hooks/useQueueEntries.ts @@ -3,6 +3,7 @@ import { type QueueEntry, type QueueEntrySearchCriteria } from '../types'; import useSWR from 'swr'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSWRConfig } from 'swr/_internal'; +import isEqual from 'lodash-es/isEqual'; type QueueEntryResponse = FetchResponse<{ results: Array; @@ -25,6 +26,9 @@ function getInitialUrl(rep: string, searchCriteria?: QueueEntrySearchCriteria) { if (searchCriteria) { for (let [key, value] of Object.entries(searchCriteria)) { + if (value === 'null' || value === 'undefined') { + continue; + } if (value != null) { searchParam.append(key, value?.toString()); } @@ -81,11 +85,37 @@ export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: const [data, setData] = useState>>([]); const [totalCount, setTotalCount] = useState(); const [currentPage, setCurrentPage] = useState(0); - const [pageUrl, setPageUrl] = useState(getInitialUrl(rep, searchCriteria)); + const [currentSearchCriteria, setCurrentSearchCriteria] = useState(searchCriteria); + const [currentRep, setCurrentRep] = useState(rep); + const [pageUrl, setPageUrl] = useState(getInitialUrl(currentRep, currentSearchCriteria)); const [error, setError] = useState(); const { mutateQueueEntries } = useMutateQueueEntries(); const [waitingForMutate, setWaitingForMutate] = useState(false); + const refetchAllData = useCallback( + (newRep: string = currentRep, newSearchCriteria: QueueEntrySearchCriteria = currentSearchCriteria) => { + setWaitingForMutate(true); + setCurrentPage(0); + setPageUrl(getInitialUrl(newRep, newSearchCriteria)); + }, + [currentRep, currentSearchCriteria], + ); + + // This hook listens to the searchCriteria and rep values and refetches the data when they change. + useEffect(() => { + const isSearchCriteriaUpdated = !isEqual(currentSearchCriteria, searchCriteria); + const isRepUpdated = currentRep !== rep; + if (isSearchCriteriaUpdated || isRepUpdated) { + if (isSearchCriteriaUpdated) { + setCurrentSearchCriteria(searchCriteria); + } + if (isRepUpdated) { + setCurrentRep(rep); + } + refetchAllData(rep, searchCriteria); + } + }, [searchCriteria, currentSearchCriteria, setCurrentSearchCriteria, currentRep, rep]); + const { data: pageData, isValidating, error: pageError } = useSWR(pageUrl, openmrsFetch); useEffect(() => { @@ -96,10 +126,10 @@ export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: } if (pageData && !isValidating && !stillWaitingForMutate) { // We've got results! Time to update the data array and move on to the next page. - if (pageData?.data?.totalCount && pageData?.data?.totalCount !== totalCount) { + if (pageData?.data?.totalCount > -1 && pageData?.data?.totalCount !== totalCount) { setTotalCount(pageData?.data?.totalCount); } - if (pageData?.data?.results?.length) { + if (pageData?.data?.results) { const newData = [...data]; newData[currentPage] = pageData?.data?.results; setData(newData); @@ -137,10 +167,8 @@ export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: }, [pageError]); const queueUpdateListener = useCallback(() => { - setWaitingForMutate(true); - setCurrentPage(0); - setPageUrl(getInitialUrl(rep, searchCriteria)); - }, [rep, searchCriteria]); + refetchAllData(); + }, [refetchAllData]); useEffect(() => { window.addEventListener('queue-entry-updated', queueUpdateListener); @@ -154,7 +182,7 @@ export function useQueueEntries(searchCriteria?: QueueEntrySearchCriteria, rep: return { queueEntries, totalCount, - isLoading: !totalCount || (totalCount && queueEntries.length < totalCount), + isLoading: totalCount === undefined || (totalCount && queueEntries.length < totalCount), isValidating: isValidating || currentPage < data.length, error, mutate: mutateQueueEntries, diff --git a/packages/esm-service-queues-app/src/hooks/useQueueService.ts b/packages/esm-service-queues-app/src/hooks/useQueueService.ts index 701c308cb..a35ba92ef 100644 --- a/packages/esm-service-queues-app/src/hooks/useQueueService.ts +++ b/packages/esm-service-queues-app/src/hooks/useQueueService.ts @@ -2,8 +2,8 @@ import { getLocale } from '@openmrs/esm-framework'; import { useMemo } from 'react'; import { useQueues } from './useQueues'; -function useQueueServices() { - const { queues, isLoading } = useQueues(); +function useQueueServices(locationUuid?: string) { + const { queues, isLoading } = useQueues(locationUuid); const results = useMemo( () => ({ diff --git a/packages/esm-service-queues-app/src/index.ts b/packages/esm-service-queues-app/src/index.ts index 359158cb1..9348d1cf5 100644 --- a/packages/esm-service-queues-app/src/index.ts +++ b/packages/esm-service-queues-app/src/index.ts @@ -14,7 +14,7 @@ import VisitFormQueueFields from './patient-search/visit-form-queue-fields/visit export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); -const moduleName = '@openmrs/esm-service-queues-app'; +const moduleName = '@kenyaemr/esm-service-queues-app'; const options = { featureName: 'outpatient', diff --git a/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx b/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx index 38a7c63e2..8e6d035fc 100644 --- a/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx +++ b/packages/esm-service-queues-app/src/patient-queue-metrics/clinic-metrics.component.tsx @@ -7,8 +7,7 @@ import { updateSelectedService, useSelectedService, useSelectedQueueLocationUuid import { useActiveVisits, useAverageWaitTime } from './clinic-metrics.resource'; import { useServiceMetricsCount } from './queue-metrics.resource'; import styles from './clinic-metrics.scss'; -import { useQueues } from '../hooks/useQueues'; -import { useQueueEntries } from '../hooks/useQueueEntries'; +import { useMutateQueueEntries, useQueueEntries } from '../hooks/useQueueEntries'; import useQueueServices from '../hooks/useQueueService'; import { isDesktop, useLayoutType } from '@openmrs/esm-framework'; @@ -20,16 +19,17 @@ export interface Service { function ClinicMetrics() { const { t } = useTranslation(); const layout = useLayoutType(); - + const mutate = useMutateQueueEntries(); const currentQueueLocation = useSelectedQueueLocationUuid(); - const { services } = useQueueServices(); + const { services } = useQueueServices(currentQueueLocation); const currentService = useSelectedService(); const { serviceCount } = useServiceMetricsCount(currentService?.serviceUuid, currentQueueLocation); const [initialSelectedItem, setInitialSelectItem] = useState(() => { return !currentService?.serviceDisplay || !currentService?.serviceUuid; }); + const service = currentService?.serviceUuid === 'undefined' ? null : currentService?.serviceUuid; const { totalCount } = useQueueEntries({ - service: currentService?.serviceUuid, + service: service, location: currentQueueLocation, isEnded: false, }); @@ -38,6 +38,7 @@ function ClinicMetrics() { const handleServiceChange = ({ selectedItem }) => { updateSelectedService(selectedItem.uuid, selectedItem.display); + mutate.mutateQueueEntries(); if (selectedItem.uuid == undefined) { setInitialSelectItem(true); } else { @@ -65,6 +66,7 @@ function ClinicMetrics() { item ? `${item.display} ${item.location?.display ? `- ${item.location.display}` : ''}` : '' diff --git a/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx b/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx index 574a58510..519ecb136 100644 --- a/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx +++ b/packages/esm-service-queues-app/src/queue-table/default-queue-table.component.tsx @@ -19,7 +19,7 @@ import { useSelectedQueueStatus, useSelectedService, } from '../helpers/helpers'; -import { useQueueEntries } from '../hooks/useQueueEntries'; +import { useMutateQueueEntries, useQueueEntries } from '../hooks/useQueueEntries'; import QueueTableExpandedRow from './queue-table-expanded-row.component'; import QueueTable from './queue-table.component'; import styles from './queue-table.scss'; @@ -37,12 +37,16 @@ function DefaultQueueTable() { const selectedService = useSelectedService(); const currentLocationUuid = useSelectedQueueLocationUuid(); const selectedQueueStatus = useSelectedQueueStatus(); - const { queueEntries, isLoading, error, isValidating } = useQueueEntries({ - service: selectedService?.serviceUuid, - location: currentLocationUuid, - isEnded: false, - status: selectedQueueStatus?.statusUuid, - }); + const searchCriteria = useMemo( + () => ({ + service: selectedService?.serviceUuid, + location: currentLocationUuid, + isEnded: false, + status: selectedQueueStatus?.statusUuid, + }), + [selectedService?.serviceUuid, currentLocationUuid, selectedQueueStatus?.statusUuid], + ); + const { queueEntries, isLoading, error, isValidating } = useQueueEntries(searchCriteria); const { t } = useTranslation(); @@ -149,12 +153,14 @@ function DefaultQueueTable() { } function QueueDropdownFilter() { + const mutateQueueEntries = useMutateQueueEntries(); const { t } = useTranslation(); const layout = useLayoutType(); const { services } = useQueueServices(); const selectedService = useSelectedService(); const handleServiceChange = ({ selectedItem }) => { updateSelectedService(selectedItem.uuid, selectedItem?.display); + mutateQueueEntries.mutateQueueEntries(); }; return ( diff --git a/packages/esm-ward-app/package.json b/packages/esm-ward-app/package.json index e14931f6d..acd4dafea 100644 --- a/packages/esm-ward-app/package.json +++ b/packages/esm-ward-app/package.json @@ -1,8 +1,8 @@ { - "name": "@openmrs/esm-ward-app", - "version": "7.0.0", + "name": "@kenyaemr/esm-ward-app", + "version": "7.0.2", "description": "Ward and bed management module for O3", - "browser": "dist/openmrs-esm-ward-app.js", + "browser": "dist/kenyaemr-esm-ward-app.js", "main": "src/index.ts", "source": true, "license": "MPL-2.0", diff --git a/packages/esm-ward-app/src/index.ts b/packages/esm-ward-app/src/index.ts index ec0f7a0d8..5f7320726 100644 --- a/packages/esm-ward-app/src/index.ts +++ b/packages/esm-ward-app/src/index.ts @@ -1,11 +1,12 @@ import { defineConfigSchema, getSyncLifecycle, registerBreadcrumbs, registerFeatureFlag } from '@openmrs/esm-framework'; import { configSchema } from './config-schema'; import rootComponent from './root.component'; -import { moduleName } from './constant'; import admissionRequestsWorkspace from "./ward-workspace/admission-requests-workspace.component" export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); +const moduleName = '@kenyaemr/esm-ward-app'; + const options = { featureName: 'ward', moduleName, diff --git a/packages/esm-ward-app/translations/en.json b/packages/esm-ward-app/translations/en.json index b47440f91..c7cd73bbb 100644 --- a/packages/esm-ward-app/translations/en.json +++ b/packages/esm-ward-app/translations/en.json @@ -2,6 +2,7 @@ "bedShare": "Bed share", "emptyBed": "Empty bed", "errorLoadingPatientAdmissionRequests": "Error Loading Patient Admission Requests", + "errorLoadingPatients": "Error loading admitted patients", "errorLoadingWardLocation": "Error loading ward location", "invalidLocationSpecified": "Invalid location specified", "invalidWardLocation": "Invalid ward location: {{location}}", diff --git a/yarn.lock b/yarn.lock index 82bcfc64c..2ff7ebb89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2490,6 +2490,128 @@ __metadata: languageName: node linkType: hard +"@kenyaemr/esm-active-visits-app@workspace:packages/esm-active-visits-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-active-visits-app@workspace:packages/esm-active-visits-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + peerDependencies: + "@openmrs/esm-framework": 5.x + dayjs: 1.x + react: ^18.1.0 + react-dom: ^18.1.0 + react-i18next: 11.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-appointments-app@workspace:packages/esm-appointments-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-appointments-app@workspace:packages/esm-appointments-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + formik: "npm:^2.2.9" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + yup: "npm:^0.32.11" + peerDependencies: + "@openmrs/esm-framework": 5.x + "@openmrs/esm-patient-common-lib": 7.x + react: 18.x + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-patient-list-management-app@workspace:packages/esm-patient-list-management-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-patient-list-management-app@workspace:packages/esm-patient-list-management-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + dexie: "npm:^3.0.3" + fuzzy: "npm:^0.1.3" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + peerDependencies: + "@openmrs/esm-framework": 5.x + react: 18.x + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-patient-registration-app@workspace:packages/esm-patient-registration-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-patient-registration-app@workspace:packages/esm-patient-registration-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + formik: "npm:^2.1.5" + lodash-es: "npm:^4.17.15" + uuid: "npm:^8.3.2" + webpack: "npm:^5.74.0" + yup: "npm:^0.29.1" + peerDependencies: + "@openmrs/esm-framework": 5.x + dayjs: 1.x + react: 18.x + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-patient-search-app@workspace:packages/esm-patient-search-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-patient-search-app@workspace:packages/esm-patient-search-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + peerDependencies: + "@openmrs/esm-framework": 5.x + react: ^18.1.0 + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-service-queues-app@workspace:packages/esm-service-queues-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-service-queues-app@workspace:packages/esm-service-queues-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + peerDependencies: + "@openmrs/esm-framework": 5.x + react: ^18.1.0 + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + +"@kenyaemr/esm-ward-app@workspace:packages/esm-ward-app": + version: 0.0.0-use.local + resolution: "@kenyaemr/esm-ward-app@workspace:packages/esm-ward-app" + dependencies: + "@carbon/react": "npm:~1.37.0" + lodash-es: "npm:^4.17.15" + webpack: "npm:^5.74.0" + peerDependencies: + "@openmrs/esm-framework": 5.x + react: ^18.1.0 + react-i18next: 11.x + react-router-dom: 6.x + swr: 2.x + languageName: unknown + linkType: soft + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.4 resolution: "@leichtgewicht/ip-codec@npm:2.0.4" @@ -2621,23 +2743,6 @@ __metadata: languageName: node linkType: hard -"@openmrs/esm-active-visits-app@workspace:packages/esm-active-visits-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-active-visits-app@workspace:packages/esm-active-visits-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - peerDependencies: - "@openmrs/esm-framework": 5.x - dayjs: 1.x - react: ^18.1.0 - react-dom: ^18.1.0 - react-i18next: 11.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/esm-api@npm:5.6.1-pre.1966": version: 5.6.1-pre.1966 resolution: "@openmrs/esm-api@npm:5.6.1-pre.1966" @@ -2688,25 +2793,6 @@ __metadata: languageName: node linkType: hard -"@openmrs/esm-appointments-app@workspace:packages/esm-appointments-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-appointments-app@workspace:packages/esm-appointments-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - formik: "npm:^2.2.9" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - yup: "npm:^0.32.11" - peerDependencies: - "@openmrs/esm-framework": 5.x - "@openmrs/esm-patient-common-lib": 7.x - react: 18.x - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/esm-config@npm:5.6.1-pre.1966": version: 5.6.1-pre.1966 resolution: "@openmrs/esm-config@npm:5.6.1-pre.1966" @@ -2868,24 +2954,6 @@ __metadata: languageName: node linkType: hard -"@openmrs/esm-patient-list-management-app@workspace:packages/esm-patient-list-management-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-patient-list-management-app@workspace:packages/esm-patient-list-management-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - dexie: "npm:^3.0.3" - fuzzy: "npm:^0.1.3" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - peerDependencies: - "@openmrs/esm-framework": 5.x - react: 18.x - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/esm-patient-management@workspace:.": version: 0.0.0-use.local resolution: "@openmrs/esm-patient-management@workspace:." @@ -2952,42 +3020,6 @@ __metadata: languageName: unknown linkType: soft -"@openmrs/esm-patient-registration-app@workspace:packages/esm-patient-registration-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-patient-registration-app@workspace:packages/esm-patient-registration-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - formik: "npm:^2.1.5" - lodash-es: "npm:^4.17.15" - uuid: "npm:^8.3.2" - webpack: "npm:^5.74.0" - yup: "npm:^0.29.1" - peerDependencies: - "@openmrs/esm-framework": 5.x - dayjs: 1.x - react: 18.x - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - -"@openmrs/esm-patient-search-app@workspace:packages/esm-patient-search-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-patient-search-app@workspace:packages/esm-patient-search-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - peerDependencies: - "@openmrs/esm-framework": 5.x - react: ^18.1.0 - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/esm-react-utils@npm:5.6.1-pre.1966": version: 5.6.1-pre.1966 resolution: "@openmrs/esm-react-utils@npm:5.6.1-pre.1966" @@ -3025,22 +3057,6 @@ __metadata: languageName: node linkType: hard -"@openmrs/esm-service-queues-app@workspace:packages/esm-service-queues-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-service-queues-app@workspace:packages/esm-service-queues-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - peerDependencies: - "@openmrs/esm-framework": 5.x - react: ^18.1.0 - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/esm-state@npm:5.6.1-pre.1966": version: 5.6.1-pre.1966 resolution: "@openmrs/esm-state@npm:5.6.1-pre.1966" @@ -3107,22 +3123,6 @@ __metadata: languageName: node linkType: hard -"@openmrs/esm-ward-app@workspace:packages/esm-ward-app": - version: 0.0.0-use.local - resolution: "@openmrs/esm-ward-app@workspace:packages/esm-ward-app" - dependencies: - "@carbon/react": "npm:~1.37.0" - lodash-es: "npm:^4.17.15" - webpack: "npm:^5.74.0" - peerDependencies: - "@openmrs/esm-framework": 5.x - react: ^18.1.0 - react-i18next: 11.x - react-router-dom: 6.x - swr: 2.x - languageName: unknown - linkType: soft - "@openmrs/webpack-config@npm:5.6.1-pre.1966": version: 5.6.1-pre.1966 resolution: "@openmrs/webpack-config@npm:5.6.1-pre.1966"