From 6703172762c07e834a030f8457bdf2f9ebb5a14c Mon Sep 17 00:00:00 2001 From: Jabar Jeremy <24471994+jabahum@users.noreply.github.com> Date: Tue, 26 Sep 2023 14:50:27 +0300 Subject: [PATCH] ft: add worklist dialog setup (#5) * fix : rename list component * ft: add worklist set up * ft : set worklist creation * fix: add tests ordered empty state * fix : empty state message --- src/config-schema.ts | 6 + src/index.ts | 5 + .../add-to-worklist-dialog.component.tsx | 232 ++++++++++++++++++ .../add-to-worklist-dialog.resource.ts | 119 +++++++++ .../lab-dialogs/add-to-worklist-dialog.scss | 20 ++ .../lab-tests/lab-tests.component.tsx | 7 + .../lab-tests/lab-tests.resource.ts | 6 + src/queue-list/lab-tests/lab-tests.scss | 12 + .../laboratory-patient-list.component.tsx | 40 ++- .../laboratory-patient-list.resource.ts | 23 +- src/queue-list/laboratory-queue.scss | 19 ++ src/queue-list/laboratory-tabs.component.tsx | 6 +- .../pick-lab-request-menu.component.tsx | 38 +++ src/routes.json | 4 + .../laboratory-summary.resource.tsx | 27 ++ 15 files changed, 558 insertions(+), 6 deletions(-) create mode 100644 src/queue-list/lab-dialogs/add-to-worklist-dialog.component.tsx create mode 100644 src/queue-list/lab-dialogs/add-to-worklist-dialog.resource.ts create mode 100644 src/queue-list/lab-dialogs/add-to-worklist-dialog.scss create mode 100644 src/queue-list/lab-tests/lab-tests.component.tsx create mode 100644 src/queue-list/lab-tests/lab-tests.resource.ts create mode 100644 src/queue-list/lab-tests/lab-tests.scss create mode 100644 src/queue-list/pick-lab-request-menu.component.tsx diff --git a/src/config-schema.ts b/src/config-schema.ts index 982819b..822732e 100644 --- a/src/config-schema.ts +++ b/src/config-schema.ts @@ -11,9 +11,15 @@ export const configSchema = { _default: "Laboratory", _description: "Location tag for laboratory locations.", }, + laboratorySpecimenTypeConcept: { + _type: Type.ConceptUuid, + _default: "159959AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + _description: "Concept UUID for laboratory specimen types", + }, }; export type Config = { laboratoryQueueConcept: string; laboratoryLocationTag: string; + laboratorySpecimenTypeConcept: string; }; diff --git a/src/index.ts b/src/index.ts index 7a6c295..fb36512 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,11 @@ export const laboratoryOrderComponent = getAsyncLifecycle( options ); +export const addToWorklistDialog = getAsyncLifecycle( + () => import("./queue-list/lab-dialogs/add-to-worklist-dialog.component"), + options +); + export function startupApp() { defineConfigSchema(moduleName, configSchema); } diff --git a/src/queue-list/lab-dialogs/add-to-worklist-dialog.component.tsx b/src/queue-list/lab-dialogs/add-to-worklist-dialog.component.tsx new file mode 100644 index 0000000..5f8cb30 --- /dev/null +++ b/src/queue-list/lab-dialogs/add-to-worklist-dialog.component.tsx @@ -0,0 +1,232 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { MappedQueueEntry } from "../../types"; +import { + Button, + ContentSwitcher, + Form, + ModalBody, + ModalFooter, + ModalHeader, + Select, + SelectItem, + Switch, + TextArea, + Grid, + Checkbox, + TextInput, + IconButton, +} from "@carbon/react"; +import { useTranslation } from "react-i18next"; +import { MappedPatientQueueEntry } from "../laboratory-patient-list.resource"; +import styles from "./add-to-worklist-dialog.scss"; +import { + navigate, + showNotification, + showToast, + useLocations, + useSession, +} from "@openmrs/esm-framework"; +import { Renew } from "@carbon/react/icons"; +import { + useQueueRoomLocations, + useSpecimenTypes, +} from "./add-to-worklist-dialog.resource"; + +interface AddToWorklistDialogProps { + queueEntry: MappedPatientQueueEntry; + closeModal: () => void; +} + +const AddToWorklistDialog: React.FC = ({ + queueEntry, + closeModal, +}) => { + const { t } = useTranslation(); + + const locations = useLocations(); + + const sessionUser = useSession(); + + const [selectedLocation, setSelectedLocation] = useState(""); + + const [preferred, setPreferred] = useState(false); + + const { specimenTypes } = useSpecimenTypes(); + + const { queueRoomLocations } = useQueueRoomLocations( + sessionUser?.sessionLocation?.uuid + ); + + const [specimenType, setSpecimenType] = useState(""); + const [selectedNextQueueLocation, setSelectedNextQueueLocation] = useState( + queueRoomLocations[0]?.uuid + ); + + const filteredlocations = queueRoomLocations?.filter( + (location) => location.uuid != selectedLocation + ); + + useEffect(() => { + if (locations?.length && sessionUser) { + setSelectedLocation(sessionUser?.sessionLocation?.uuid); + } + }, [locations, sessionUser]); + + const pickLabRequestQueue = useCallback((event) => { + event.preventDefault(); + }, []); + + const onChecked = () => { + setPreferred(!preferred); + }; + + const GenerateID = () => { + return ( + + + + ); + }; + if (queueEntry && Object.keys(queueEntry)?.length > 0) { + return ( +
+
+ + +
+

+ Currently Picked : {queueEntry.name} +

+
+
+
+ {t("specimenID", "Specimen ID")} +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ {t("specimenType", "Specimen Type")} +
+
+
+ +
+
+
+
+
+
+ +
+ {preferred && ( +
+
+ +
+
+ )} +
+
+
+ + + + + +
+ ); + } +}; + +export default AddToWorklistDialog; diff --git a/src/queue-list/lab-dialogs/add-to-worklist-dialog.resource.ts b/src/queue-list/lab-dialogs/add-to-worklist-dialog.resource.ts new file mode 100644 index 0000000..3b49504 --- /dev/null +++ b/src/queue-list/lab-dialogs/add-to-worklist-dialog.resource.ts @@ -0,0 +1,119 @@ +import { FetchResponse, openmrsFetch, useConfig } from "@openmrs/esm-framework"; +import { useMemo } from "react"; +import useSWR from "swr"; +import useSWRImmutable from "swr/immutable"; + +export interface QueueRoomsResponse { + uuid: string; + display: string; + name: string; + description: string; + address1: string; + address2: string; + cityvillage: string; + stateprovince: string; + country: string; + postalcode: string; + latitude: string; + longitude: string; + countydistrict: string; + address3: string; + address4: string; + address5: string; + address6: string; + parentLocation: ParentLocation; + childLocations: String[]; + retired: boolean; + attributes: String[]; + address7: string; + address8: string; + address9: string; + address10: string; + address11: string; + address12: string; + address13: string; + address14: string; + address15: string; + resourceVersion: string; +} + +export interface ParentLocation { + uuid: string; + display: string; + name: string; + description: string; + address1: string; + address2: string; + cityVillage: string; + stateProvince: string; + country: string; + postalcode: string; + latitude: string; + longitude: string; + countydistrict: string; + address3: string; + address4: string; + address5: string; + address6: string; + parentLocation: ParentLocation; + childLocations: ChildLocations[]; + retired: boolean; + attributes: String[]; + address7: string; + address8: string; + address9: string; + address10: string; + address11: string; + address12: string; + address13: string; + address14: string; + address15: string; + resourceversion: string; +} + +export interface ChildLocations { + uuid: string; + display: string; +} + +export interface ParentLocation { + uuid: string; + display: string; +} + +export function useQueueRoomLocations(currentQueueLocation: string) { + const apiUrl = `/ws/rest/v1/location/${currentQueueLocation}?v=full`; + const { data, error, isLoading } = useSWR<{ data: QueueRoomsResponse }>( + apiUrl, + openmrsFetch + ); + + const queueRoomLocations = useMemo( + () => + data?.data?.parentLocation?.childLocations?.map((response) => response) ?? + [], + [data?.data?.parentLocation?.childLocations] + ); + return { + queueRoomLocations: queueRoomLocations ? queueRoomLocations : [], + isLoading, + error, + }; +} + +// get specimen types +export function useSpecimenTypes() { + const config = useConfig(); + const { laboratorySpecimenTypeConcept } = config; + + const apiUrl = `/ws/rest/v1/concept/${laboratorySpecimenTypeConcept}`; + const { data, error, isLoading } = useSWRImmutable( + apiUrl, + openmrsFetch + ); + + return { + specimenTypes: data ? data?.data?.answers : [], + isLoading, + }; +} diff --git a/src/queue-list/lab-dialogs/add-to-worklist-dialog.scss b/src/queue-list/lab-dialogs/add-to-worklist-dialog.scss new file mode 100644 index 0000000..e5620f1 --- /dev/null +++ b/src/queue-list/lab-dialogs/add-to-worklist-dialog.scss @@ -0,0 +1,20 @@ +@use '@carbon/styles/scss/spacing'; +@use '@carbon/styles/scss/type'; + + +.radioButton { + padding: spacing.$spacing-02 spacing.$spacing-02; + margin: spacing.$spacing-03 0; +} + +section { + margin: spacing.$spacing-03; +} + +.sectionTitle { + margin-bottom: spacing.$spacing-04; +} + +.modalBody { + padding-bottom: spacing.$spacing-05; +} diff --git a/src/queue-list/lab-tests/lab-tests.component.tsx b/src/queue-list/lab-tests/lab-tests.component.tsx new file mode 100644 index 0000000..2177dfc --- /dev/null +++ b/src/queue-list/lab-tests/lab-tests.component.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const LabTests = () => { + return Lab Tests; +}; + +export default LabTests; diff --git a/src/queue-list/lab-tests/lab-tests.resource.ts b/src/queue-list/lab-tests/lab-tests.resource.ts new file mode 100644 index 0000000..a4a597c --- /dev/null +++ b/src/queue-list/lab-tests/lab-tests.resource.ts @@ -0,0 +1,6 @@ +export function useLabTests() { + return { + data: {}, + count: 0, + }; +} diff --git a/src/queue-list/lab-tests/lab-tests.scss b/src/queue-list/lab-tests/lab-tests.scss new file mode 100644 index 0000000..c698e19 --- /dev/null +++ b/src/queue-list/lab-tests/lab-tests.scss @@ -0,0 +1,12 @@ +@use '@carbon/styles/scss/spacing'; +@use '@carbon/styles/scss/type'; +@import "~@openmrs/esm-styleguide/src/vars"; + +.cardContainer { + background-color: $ui-02 ; + display: flex; + justify-content: space-between; + padding: 0 spacing.$spacing-05 spacing.$spacing-10 spacing.$spacing-03; + flex-flow: row wrap; + margin-top: - spacing.$spacing-03; +} diff --git a/src/queue-list/laboratory-patient-list.component.tsx b/src/queue-list/laboratory-patient-list.component.tsx index 894f7cc..03289c0 100644 --- a/src/queue-list/laboratory-patient-list.component.tsx +++ b/src/queue-list/laboratory-patient-list.component.tsx @@ -19,6 +19,7 @@ import { TableToolbarSearch, Layer, Tag, + TableExpandedRow, } from "@carbon/react"; import { useTranslation } from "react-i18next"; import { @@ -36,6 +37,10 @@ import { getTagColor, trimVisitNumber, } from "../utils/functions"; +import LabTests from "./lab-tests/lab-tests.component"; +import AddToWorklist from "./lab-dialogs/add-to-worklist-dialog.component"; +import PickLabRequestActionMenu from "./pick-lab-request-menu.component"; +import { EmptyState } from "@openmrs/esm-patient-common-lib"; // type FilterProps = { // rowIds: Array; @@ -73,6 +78,7 @@ const LaboratoryPatientList: React.FC = () => { { id: 2, header: t("age", "Age"), key: "age" }, { id: 3, header: t("orderedFrom", "Ordered from"), key: "orderedFrom" }, { id: 4, header: t("waitingTime", "Waiting time"), key: "waitingTime" }, + { id: 5, header: t("actions", "Actions"), key: "actions" }, ]; const tableRows = useMemo(() => { @@ -102,6 +108,14 @@ const LaboratoryPatientList: React.FC = () => { ), }, + actions: { + content: ( + true} + /> + ), + }, })); }, [paginatedQueueEntries, t]); @@ -195,13 +209,28 @@ const LaboratoryPatientList: React.FC = () => { {rows.map((row, index) => { return ( - + {row.cells.map((cell) => ( {cell.value?.content ?? cell.value} ))} - + + {row.isExpanded ? ( + + <> + + + + ) : ( + + )} ); })} @@ -230,6 +259,13 @@ const LaboratoryPatientList: React.FC = () => { ); } + + return ( +
+
+ +
+ ); }; export default LaboratoryPatientList; diff --git a/src/queue-list/laboratory-patient-list.resource.ts b/src/queue-list/laboratory-patient-list.resource.ts index 5adbfad..e16887a 100644 --- a/src/queue-list/laboratory-patient-list.resource.ts +++ b/src/queue-list/laboratory-patient-list.resource.ts @@ -1,9 +1,30 @@ -import { PatientQueue } from "../types/patient-queues"; +import { PatientQueue, UuidDisplay } from "../types/patient-queues"; import dayjs from "dayjs"; import useSWR from "swr"; import { formatDate, openmrsFetch, parseDate } from "@openmrs/esm-framework"; +export interface MappedPatientQueueEntry { + id: string; + name: string; + patientAge: number; + patientSex: string; + patientDob: string; + patientUuid: string; + priority: string; + priorityComment: string; + status: string; + waitTime: string; + locationFrom?: string; + locationToName?: string; + visitNumber: string; + identifiers: Array; + dateCreated: string; + creatorUuid: string; + creatorUsername: string; + creatorDisplay: string; +} + export function usePatientQueuesList( currentQueueRoomLocationUuid: string, status: string diff --git a/src/queue-list/laboratory-queue.scss b/src/queue-list/laboratory-queue.scss index 50eebb4..2d854c6 100644 --- a/src/queue-list/laboratory-queue.scss +++ b/src/queue-list/laboratory-queue.scss @@ -168,4 +168,23 @@ title { .container { background-color: $ui-01; +} + +.expandedLabQueueVisitRow { + :global(.cds--tab-content) { + padding: 0.5rem 0; + } + + td { + padding: 0.5rem; + + > div { + max-height: max-content !important; + background-color: $ui-02; + } + } + + th[colspan] td[colspan] > div:first-child { + padding: 0 1rem; + } } \ No newline at end of file diff --git a/src/queue-list/laboratory-tabs.component.tsx b/src/queue-list/laboratory-tabs.component.tsx index 20892a0..3b770da 100644 --- a/src/queue-list/laboratory-tabs.component.tsx +++ b/src/queue-list/laboratory-tabs.component.tsx @@ -41,7 +41,7 @@ const LaboratoryQueueTabs: React.FC = () => {
@@ -50,7 +50,7 @@ const LaboratoryQueueTabs: React.FC = () => {
@@ -59,7 +59,7 @@ const LaboratoryQueueTabs: React.FC = () => {
diff --git a/src/queue-list/pick-lab-request-menu.component.tsx b/src/queue-list/pick-lab-request-menu.component.tsx new file mode 100644 index 0000000..8061582 --- /dev/null +++ b/src/queue-list/pick-lab-request-menu.component.tsx @@ -0,0 +1,38 @@ +import { Button, Tooltip } from "@carbon/react"; +import { Notification } from "@carbon/react/icons"; + +import { showModal } from "@openmrs/esm-framework"; +import React, { useCallback } from "react"; +import { useTranslation } from "react-i18next"; +import { MappedPatientQueueEntry } from "./laboratory-patient-list.resource"; + +interface PickLabRequestActionMenuProps { + queueEntry: MappedPatientQueueEntry; + closeModal: () => void; +} + +const PickLabRequestActionMenu: React.FC = ({ + queueEntry, +}) => { + const { t } = useTranslation(); + + const launchPickLabRequestQueueModal = useCallback(() => { + const dispose = showModal("add-to-worklist-dialog", { + closeModal: () => dispose(), + queueEntry, + }); + }, [queueEntry]); + + return ( + + + + ); +}; + +export default PickLabRequestActionMenu; diff --git a/src/routes.json b/src/routes.json index dbace6e..0ea4f34 100644 --- a/src/routes.json +++ b/src/routes.json @@ -43,6 +43,10 @@ "name": "laboratory-orders-summary", "slot": "laboratory-orders-dashboard-slot", "component": "laboratoryOrderComponent" + }, + { + "name": "add-to-worklist-dialog", + "component": "addToWorklistDialog" } ] } diff --git a/src/summary-tiles/laboratory-summary.resource.tsx b/src/summary-tiles/laboratory-summary.resource.tsx index 768c470..f694224 100644 --- a/src/summary-tiles/laboratory-summary.resource.tsx +++ b/src/summary-tiles/laboratory-summary.resource.tsx @@ -32,3 +32,30 @@ export function useServices() { : [], }; } + +// test ordered +export function useOrderedTests() { + return { + count: 0, + }; +} +// worklist +export function useWorklistsStats() { + return { + count: 0, + }; +} + +// referred tests +export function useReferredTestsStats() { + return { + count: 0, + }; +} + +// results +export function useResultsStats() { + return { + count: 0, + }; +}