From d5a36d737b389b503cb03a4f790df4766a123ff7 Mon Sep 17 00:00:00 2001 From: Felix <67233923+DasProffi@users.noreply.github.com> Date: Sun, 13 Aug 2023 03:40:32 +0200 Subject: [PATCH] feat: add patientlist backend connection --- lib/components/HideableContentSection.tsx | 44 ++++++ pnpm-lock.yaml | 8 +- tasks/components/layout/PatientDetails.tsx | 2 +- tasks/components/layout/PatientList.tsx | 163 ++++++++++++--------- tasks/mutations/patient_mutations.ts | 79 +++++++--- tasks/package.json | 2 +- 6 files changed, 201 insertions(+), 97 deletions(-) create mode 100644 lib/components/HideableContentSection.tsx diff --git a/lib/components/HideableContentSection.tsx b/lib/components/HideableContentSection.tsx new file mode 100644 index 00000000..6fef9b8a --- /dev/null +++ b/lib/components/HideableContentSection.tsx @@ -0,0 +1,44 @@ +import { tw, tx } from '../twind' +import type { PropsWithChildren } from 'react' +import { ChevronDown, ChevronUp } from 'lucide-react' +import React, { useState } from 'react' + +export type HideableContentSectionProps = PropsWithChildren & { + initiallyOpen?: boolean, + disabled: boolean, + header: React.ReactNode +} + +/** + * A Component to hide and show + */ +export const HideableContentSection = ({ + initiallyOpen = true, + disabled, + children, + header +}: HideableContentSectionProps) => { + const [open, setOpen] = useState(initiallyOpen) + return ( +
+
{ + if (!disabled) { + setOpen(!open) + } + }} + > +
+ {header} +
+ {!disabled && (open ? : )} +
+ {open && ( +
+ {children} +
+ )} +
+ ) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc8f2364..c4bc9b64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -205,8 +205,8 @@ importers: specifier: workspace:* version: link:../lib '@helpwave/proto-ts': - specifier: 0.19.0-d9220d1.0 - version: 0.19.0-d9220d1.0 + specifier: 0.20.0-673c02c.0 + version: 0.20.0-673c02c.0 '@radix-ui/react-checkbox': specifier: 1.0.4 version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.18)(react-dom@18.2.0)(react@18.2.0) @@ -2167,8 +2167,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /@helpwave/proto-ts@0.19.0-d9220d1.0: - resolution: {integrity: sha512-BkXWu+IAFM2r+8rWs6pIA8L46PhvMj8pC3FVICDuABIxRLgZKWI+xnDc0a8o1i/EU7lXR5NvLVHIkZItUopDPw==} + /@helpwave/proto-ts@0.20.0-673c02c.0: + resolution: {integrity: sha512-HJQ3AbFe8Yle+TfjX+oxrREMhu1Rm9v3MscrRYejXbPRHfErf1qdtIw5W5qR4u2yhIVSlkkRmu0iHHF2TN0Cdg==} dev: false /@humanwhocodes/config-array@0.11.10: diff --git a/tasks/components/layout/PatientDetails.tsx b/tasks/components/layout/PatientDetails.tsx index 06a5df20..4ab0c90a 100644 --- a/tasks/components/layout/PatientDetails.tsx +++ b/tasks/components/layout/PatientDetails.tsx @@ -122,7 +122,7 @@ export const PatientDetail = ({ onBackgroundClick={() => setIsShowingDischargeDialog(false)} onConfirm={() => { setIsShowingDischargeDialog(false) - dischargeMutation.mutate(newPatient) + dischargeMutation.mutate(newPatient.id) }} confirmType="negative" /> diff --git a/tasks/components/layout/PatientList.tsx b/tasks/components/layout/PatientList.tsx index b264e319..17047187 100644 --- a/tasks/components/layout/PatientList.tsx +++ b/tasks/components/layout/PatientList.tsx @@ -2,15 +2,16 @@ import { tw } from '@helpwave/common/twind' import type { Languages } from '@helpwave/common/hooks/useLanguage' import type { PropsWithLanguage } from '@helpwave/common/hooks/useTranslation' import { useTranslation } from '@helpwave/common/hooks/useTranslation' -import React, { useContext, useState } from 'react' +import React, { useState } from 'react' import { Button } from '@helpwave/common/components/Button' import { Span } from '@helpwave/common/components/Span' import { Input } from '@helpwave/common/components/user_input/Input' import type { PatientDTO, PatientWithBedAndRoomDTO } from '../../mutations/patient_mutations' -import { usePatientListQuery } from '../../mutations/patient_mutations' +import { usePatientDischargeMutation, usePatientListQuery } from '../../mutations/patient_mutations' import { Label } from '../Label' import { MultiSearchWithMapping, SimpleSearchWithMapping } from '../../utils/simpleSearch' -import { WardOverviewContext } from '../../pages/ward/[uuid]' +import { LoadingAndErrorComponent } from '@helpwave/common/components/LoadingAndErrorComponent' +import { HideableContentSection } from '@helpwave/common/components/HideableContentSection' type PatientListTranslation = { patients: string, @@ -58,8 +59,21 @@ const defaultPatientListTranslations: Record } } +export type PatientListOpenedSectionsType = { + active: boolean, + unassigned: boolean, + discharged: boolean +} + +export const defaultPatientListOpenedSections: PatientListOpenedSectionsType = { + active: true, + unassigned: true, + discharged: false +} + export type PatientListProps = { onDischarge?: (patient: PatientDTO) => void, + initialOpenedSections?:PatientListOpenedSectionsType, width?: number } @@ -67,30 +81,23 @@ export type PatientListProps = { * The right side of the ward/[uuid].tsx page showing the detailed information about the patients in the ward */ export const PatientList = ({ - language + language, + initialOpenedSections = defaultPatientListOpenedSections }: PropsWithLanguage) => { const translation = useTranslation(language, defaultPatientListTranslations) const [search, setSearch] = useState('') - const context = useContext(WardOverviewContext) - const { data, isLoading, isError } = usePatientListQuery(context.state.wardID) - - if (isError) { - return

Error

- } - - if (isLoading || !data) { - return null - } + const { data, isLoading, isError } = usePatientListQuery() + const dischargeMutation = usePatientDischargeMutation() const activeLabelText = (patient: PatientWithBedAndRoomDTO) => `${patient.room.name} - ${patient.bed.name}` - const filteredActive = MultiSearchWithMapping(search, data.active, value => [value.name, activeLabelText(value)]) - const filteredUnassigned = SimpleSearchWithMapping(search, data.unassigned, value => value.name) - const filteredDischarged = SimpleSearchWithMapping(search, data.discharged, value => value.name) + const filteredActive = !data ? [] : MultiSearchWithMapping(search, data.active, value => [value.name, activeLabelText(value)]) + const filteredUnassigned = !data ? [] : SimpleSearchWithMapping(search, data.unassigned, value => value.name) + const filteredDischarged = !data ? [] : SimpleSearchWithMapping(search, data.discharged, value => value.name) return (
-
+
{translation.patients}
- {filteredActive.length >= 0 && ( -
- {`${translation.active} (${filteredActive.length})`} - {filteredActive.map(patient => ( -
- {patient.name} -
-
-
- ))} -
- )} - {filteredUnassigned.length >= 0 && ( -
- {`${translation.unassigned} (${filteredUnassigned.length})`} - {filteredUnassigned.map(patient => ( -
- {patient.name} -
-
-
- ))} -
- )} - {filteredDischarged.length >= 0 && ( -
- {`${translation.discharged} (${filteredDischarged.length})`} - {filteredDischarged.map(patient => ( -
- {patient.name} - -
- ))} + +
+ {filteredActive.length >= 0 && ( + {`${translation.active} (${filteredActive.length})`}} + > + {filteredActive.map(patient => ( +
+ {patient.name} +
+
+
+ ))} +
+ )} + {filteredUnassigned.length >= 0 && ( + {`${translation.unassigned} (${filteredUnassigned.length})`}} + > + {filteredUnassigned.map(patient => ( +
+ {patient.name} +
+
+
+ ))} +
+ )} + {filteredDischarged.length >= 0 && ( + {`${translation.discharged} (${filteredDischarged.length})`}} + > + {filteredDischarged.map(patient => ( +
+ {patient.name} + { /* TODO implement when backend endpoint exists + + */} +
+ ))} +
+ )}
- )} +
) } diff --git a/tasks/mutations/patient_mutations.ts b/tasks/mutations/patient_mutations.ts index 8a294bde..565bb1c1 100644 --- a/tasks/mutations/patient_mutations.ts +++ b/tasks/mutations/patient_mutations.ts @@ -10,7 +10,8 @@ import { AssignBedRequest, UnassignBedRequest, GetPatientDetailsResponse, - GetPatientAssignmentByWardRequest + GetPatientAssignmentByWardRequest, + GetPatientListRequest } from '@helpwave/proto-ts/proto/services/task_svc/v1/patient_svc_pb' import { patientService, getAuthenticatedGrpcMetadata } from '../utils/grpc' import type { BedWithPatientID } from './bed_mutations' @@ -185,24 +186,46 @@ export const usePatientAssignmentByWardQuery = (wardID: string) => { }) } -// TODO remove this comment -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const usePatientListQuery = (wardID: string) => { - const queryClient = useQueryClient() +export const usePatientListQuery = (organisationID?: string) => { return useQuery({ queryKey: [patientsQueryKey, 'patientList'], queryFn: async () => { - // TODO do grpc request + const req = new GetPatientListRequest() + if (organisationID) { + req.setOrganisationId(organisationID) + } + const res = await patientService.getPatientList(req, getAuthenticatedGrpcMetadata()) - return queryClient.getQueryData([patientsQueryKey, 'patientList']) - }, - initialData: { - active: [ - ], - unassigned: [ - ], - discharged: [ - ] + const patientList: PatientListDTO = { + active: res.getActiveList().map(value => { + const room = value.getRoom() + const bed = value.getBed() + + if (!room) { + console.error('no room for active patient in PatientList') + } + + if (!bed) { + console.error('no room for active patient in PatientList') + } + return ({ + id: value.getId(), + name: value.getHumanReadableIdentifier(), + bed: { id: bed?.getId() ?? '', name: bed?.getName() ?? '' }, + room: { id: room?.getId() ?? '', name: room?.getName() ?? '' } + }) + }), + discharged: res.getDischargedPatientsList().map(value => ({ + id: value.getId(), + name: value.getHumanReadableIdentifier(), + })), + unassigned: res.getUnassignedPatientsList().map(value => ({ + id: value.getId(), + name: value.getHumanReadableIdentifier(), + })) + } + + return patientList } }) } @@ -226,7 +249,8 @@ export const usePatientCreateMutation = (callback: (patient: PatientDTO) => void return patient }, onSuccess: () => { - queryClient.refetchQueries([roomsQueryKey]).catch(reason => console.log(reason)) + queryClient.refetchQueries([roomsQueryKey]).catch(reason => console.error(reason)) + queryClient.refetchQueries([patientsQueryKey]).catch(reason => console.error(reason)) } }) } @@ -252,26 +276,32 @@ export const usePatientUpdateMutation = (callback: (patient: PatientDTO) => void return patient }, onSuccess: () => { - queryClient.refetchQueries([roomsQueryKey]).catch(reason => console.log(reason)) + queryClient.refetchQueries([patientsQueryKey]).catch(reason => console.error(reason)) + queryClient.refetchQueries([roomsQueryKey]).catch(reason => console.error(reason)) } }) } -export const usePatientDischargeMutation = (callback: () => void) => { +export const usePatientDischargeMutation = (callback: () => void = noop) => { const queryClient = useQueryClient() return useMutation({ - mutationFn: async (patient: PatientDTO) => { + mutationFn: async (patientID: string) => { const req = new DischargePatientRequest() - req.setId(patient.id) + req.setId(patientID) const res = await patientService.dischargePatient(req, getAuthenticatedGrpcMetadata()) - // TODO some check whether request was successful + if (!res.toObject()) { + console.error('error in PatientDischarge') + } - queryClient.refetchQueries([roomsQueryKey, roomOverviewsQueryKey]).catch(reason => console.log(reason)) callback() return res.toObject() }, + onSuccess: () => { + queryClient.refetchQueries([roomsQueryKey, roomOverviewsQueryKey]).catch(reason => console.error(reason)) + queryClient.refetchQueries([patientsQueryKey]).catch(reason => console.error(reason)) + } }) } @@ -314,9 +344,12 @@ export const useUnassignMutation = (callback: () => void) => { console.error('assign bed request failed') } - queryClient.refetchQueries([roomsQueryKey, roomOverviewsQueryKey]).then() callback() return res.toObject() }, + onSuccess: () => { + queryClient.refetchQueries([roomsQueryKey, roomOverviewsQueryKey]).then() + queryClient.refetchQueries([patientsQueryKey]).then() + } }) } diff --git a/tasks/package.json b/tasks/package.json index 3ae886cd..d587a50c 100644 --- a/tasks/package.json +++ b/tasks/package.json @@ -13,7 +13,7 @@ "@dnd-kit/sortable": "7.0.2", "@headlessui/react": "1.7.16", "@helpwave/common": "workspace:*", - "@helpwave/proto-ts": "0.19.0-d9220d1.0", + "@helpwave/proto-ts": "0.20.0-673c02c.0", "@radix-ui/react-checkbox": "1.0.4", "@tanstack/react-query": "4.32.6", "@tanstack/react-table": "8.9.3",