From 0b706c8568ca7c7ac338bd37757e05188a15a2ba Mon Sep 17 00:00:00 2001 From: Felix Evers Date: Thu, 8 Jan 2026 16:17:39 +0100 Subject: [PATCH 1/5] update websocket authentication --- backend/auth.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/auth.py b/backend/auth.py index ab15ac10..56568aa6 100644 --- a/backend/auth.py +++ b/backend/auth.py @@ -135,6 +135,21 @@ def get_token_source(connection: HTTPConnection) -> str | None: if len(parts) == 2 and parts[0].lower() == "bearer": return parts[1] + try: + if hasattr(connection, "query_params"): + token_param = connection.query_params.get("token") + if token_param: + return token_param + except (AttributeError, KeyError): + try: + from urllib.parse import urlparse, parse_qs + parsed_url = urlparse(str(connection.url)) + query_params = parse_qs(parsed_url.query) + if "token" in query_params and query_params["token"]: + return query_params["token"][0] + except Exception: + pass + return connection.cookies.get(AUTH_COOKIE_NAME) From c0bdf3ef8d68920ce04f81bc9fa545064545bf28 Mon Sep 17 00:00:00 2001 From: Felix Evers Date: Thu, 8 Jan 2026 16:29:19 +0100 Subject: [PATCH 2/5] add show all patients button --- web/components/patients/PatientList.tsx | 47 ++++++++++++++++++++++--- web/i18n/translations.ts | 3 ++ web/locales/de-DE.arb | 1 + web/locales/en-US.arb | 1 + 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/web/components/patients/PatientList.tsx b/web/components/patients/PatientList.tsx index 9dfb271a..a1875ba3 100644 --- a/web/components/patients/PatientList.tsx +++ b/web/components/patients/PatientList.tsx @@ -1,7 +1,7 @@ import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } from 'react' -import { Table, Chip, FillerRowElement, Button, SearchBar, ProgressIndicator, Tooltip } from '@helpwave/hightide' +import { Table, Chip, FillerRowElement, Button, SearchBar, ProgressIndicator, Tooltip, Checkbox } from '@helpwave/hightide' import { PlusIcon, Table as TableIcon, LayoutGrid, Printer } from 'lucide-react' -import { GetPatientsDocument, Sex, type GetPatientsQuery, type TaskType, type PatientState } from '@/api/gql/generated' +import { GetPatientsDocument, Sex, PatientState, type GetPatientsQuery, type TaskType } from '@/api/gql/generated' import { usePaginatedGraphQLQuery } from '@/hooks/usePaginatedQuery' import { SidePanel } from '@/components/layout/SidePanel' import { PatientDetailView } from '@/components/patients/PatientDetailView' @@ -28,6 +28,8 @@ export type PatientViewModel = { tasks: TaskType[], } +const STORAGE_KEY_SHOW_ALL_PATIENTS = 'patient-show-all-states' + export type PatientListRef = { openCreate: () => void, openPatient: (patientId: string) => void, @@ -47,15 +49,45 @@ export const PatientList = forwardRef(({ initi const [selectedPatient, setSelectedPatient] = useState(undefined) const [searchQuery, setSearchQuery] = useState('') const [openedPatientId, setOpenedPatientId] = useState(null) + const [showAllPatients, setShowAllPatients] = useState(() => { + if (typeof window !== 'undefined') { + const stored = localStorage.getItem(STORAGE_KEY_SHOW_ALL_PATIENTS) + if (stored === 'true') { + return true + } + if (stored === 'false') { + return false + } + } + return false + }) const [isPrinting, setIsPrinting] = useState(false) + const handleShowAllPatientsChange = (checked: boolean) => { + setShowAllPatients(() => { + if (typeof window !== 'undefined') { + localStorage.setItem(STORAGE_KEY_SHOW_ALL_PATIENTS, String(checked)) + } + return checked + }) + } + + const allPatientStates: PatientState[] = [ + PatientState.Admitted, + PatientState.Discharged, + PatientState.Dead, + PatientState.Wait, + ] + + const patientStates = showAllPatients ? allPatientStates : (acceptedStates ?? [PatientState.Admitted]) + const { data: patientsData, refetch } = usePaginatedGraphQLQuery({ - queryKey: ['GetPatients'], + queryKey: ['GetPatients', { rootLocationIds: selectedRootLocationIds, states: patientStates }], document: GetPatientsDocument, baseVariables: { rootLocationIds: selectedRootLocationIds && selectedRootLocationIds.length > 0 ? selectedRootLocationIds : undefined, - states: acceptedStates + states: patientStates }, pageSize: 50, extractItems: (result) => result.patients, @@ -278,6 +310,13 @@ export const PatientList = forwardRef(({ initi />
+
+ + {translation('showAllPatients') || 'Show all patients'} +
{viewType === 'table' && (
{isLoading && (
- +
)} {!isLoading && ( From 1162dbea6ade689aedec9dfea35e313b748ff0c3 Mon Sep 17 00:00:00 2001 From: Felix Evers Date: Thu, 8 Jan 2026 16:38:46 +0100 Subject: [PATCH 4/5] fix location scoping --- web/components/patients/PatientList.tsx | 8 +++++--- web/pages/location/[id].tsx | 6 ++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/web/components/patients/PatientList.tsx b/web/components/patients/PatientList.tsx index a1875ba3..7508eb48 100644 --- a/web/components/patients/PatientList.tsx +++ b/web/components/patients/PatientList.tsx @@ -39,11 +39,13 @@ type PatientListProps = { initialPatientId?: string, onInitialPatientOpened?: () => void, acceptedStates?: PatientState[], + rootLocationIds?: string[], } -export const PatientList = forwardRef(({ initialPatientId, onInitialPatientOpened, acceptedStates }, ref) => { +export const PatientList = forwardRef(({ initialPatientId, onInitialPatientOpened, acceptedStates, rootLocationIds }, ref) => { const translation = useTasksTranslation() const { selectedRootLocationIds } = useTasksContext() + const effectiveRootLocationIds = rootLocationIds ?? selectedRootLocationIds const { viewType, toggleView } = usePatientViewToggle() const [isPanelOpen, setIsPanelOpen] = useState(false) const [selectedPatient, setSelectedPatient] = useState(undefined) @@ -83,10 +85,10 @@ export const PatientList = forwardRef(({ initi const patientStates = showAllPatients ? allPatientStates : (acceptedStates ?? [PatientState.Admitted]) const { data: patientsData, refetch } = usePaginatedGraphQLQuery({ - queryKey: ['GetPatients', { rootLocationIds: selectedRootLocationIds, states: patientStates }], + queryKey: ['GetPatients', { rootLocationIds: effectiveRootLocationIds, states: patientStates }], document: GetPatientsDocument, baseVariables: { - rootLocationIds: selectedRootLocationIds && selectedRootLocationIds.length > 0 ? selectedRootLocationIds : undefined, + rootLocationIds: effectiveRootLocationIds && effectiveRootLocationIds.length > 0 ? effectiveRootLocationIds : undefined, states: patientStates }, pageSize: 50, diff --git a/web/pages/location/[id].tsx b/web/pages/location/[id].tsx index 745ee193..77863e84 100644 --- a/web/pages/location/[id].tsx +++ b/web/pages/location/[id].tsx @@ -9,7 +9,6 @@ import { TaskList, type TaskViewModel } from '@/components/tasks/TaskList' import { useGetLocationNodeQuery, useGetPatientsQuery, useGetTasksQuery, type LocationType } from '@/api/gql/generated' import { useMemo, useState } from 'react' import { useRouter } from 'next/router' -import { useTasksContext } from '@/hooks/useTasksContext' import { LocationChips } from '@/components/patients/LocationChips' import { LOCATION_PATH_SEPARATOR } from '@/utils/location' @@ -28,7 +27,6 @@ const getKindStyles = (kind: string) => { const LocationPage: NextPage = () => { const translation = useTasksTranslation() const router = useRouter() - const { selectedRootLocationIds } = useTasksContext() const id = Array.isArray(router.query['id']) ? router.query['id'][0] : router.query['id'] const [showAllTasks, setShowAllTasks] = useState(false) @@ -43,7 +41,7 @@ const LocationPage: NextPage = () => { const isTeamLocation = locationData?.locationNode?.kind === 'TEAM' const { data: patientsData, refetch: refetchPatients, isLoading: isLoadingPatients } = useGetPatientsQuery( - { locationId: id, rootLocationIds: selectedRootLocationIds && selectedRootLocationIds.length > 0 ? selectedRootLocationIds : undefined }, + { rootLocationIds: id ? [id] : undefined }, { enabled: !!id && !isTeamLocation, refetchOnWindowFocus: true, @@ -201,7 +199,7 @@ const LocationPage: NextPage = () => { {!isLoading && !isError && ( - + Date: Thu, 8 Jan 2026 16:47:11 +0100 Subject: [PATCH 5/5] colorize checkboxes --- web/components/tasks/TaskCardView.tsx | 18 +++++++++++++++++- web/components/tasks/TaskDetailView.tsx | 19 ++++++++++++++++++- web/components/tasks/TaskList.tsx | 18 +++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/web/components/tasks/TaskCardView.tsx b/web/components/tasks/TaskCardView.tsx index 4b3e6e36..3636f521 100644 --- a/web/components/tasks/TaskCardView.tsx +++ b/web/components/tasks/TaskCardView.tsx @@ -84,6 +84,22 @@ const getPriorityColor = (priority: string | null | undefined): string => { } } +const getPriorityCheckboxColor = (priority: string | null | undefined): string => { + if (!priority) return '' + switch (priority) { + case 'P1': + return 'border-green-500 text-green-500 checked:bg-green-500' + case 'P2': + return 'border-blue-500 text-blue-500 checked:bg-blue-500' + case 'P3': + return 'border-orange-500 text-orange-500 checked:bg-orange-500' + case 'P4': + return 'border-red-500 text-red-500 checked:bg-red-500' + default: + return '' + } +} + const toDate = (date: Date | string | null | undefined): Date | undefined => { if (!date) return undefined if (date instanceof Date) return date @@ -233,7 +249,7 @@ export const TaskCardView = ({ task, onToggleDone: _onToggleDone, onClick, showA
diff --git a/web/components/tasks/TaskDetailView.tsx b/web/components/tasks/TaskDetailView.tsx index 61afe10d..9b066b17 100644 --- a/web/components/tasks/TaskDetailView.tsx +++ b/web/components/tasks/TaskDetailView.tsx @@ -49,6 +49,7 @@ import { ErrorDialog } from '@/components/ErrorDialog' import { useAtomicMutation } from '@/hooks/useAtomicMutation' import { fetcher } from '@/api/gql/fetcher' import { UserInfoPopup } from '@/components/UserInfoPopup' +import clsx from 'clsx' interface TaskDetailViewProps { taskId: string | null, @@ -57,6 +58,22 @@ interface TaskDetailViewProps { initialPatientId?: string, } +const getPriorityCheckboxColor = (priority: TaskPriority | null | undefined): string => { + if (!priority) return '' + switch (priority) { + case 'P1': + return 'border-green-500 text-green-500 checked:bg-green-500' + case 'P2': + return 'border-blue-500 text-blue-500 checked:bg-blue-500' + case 'P3': + return 'border-orange-500 text-orange-500 checked:bg-orange-500' + case 'P4': + return 'border-red-500 text-red-500 checked:bg-red-500' + default: + return '' + } +} + export const TaskDetailView = ({ taskId, onClose, onSuccess, initialPatientId }: TaskDetailViewProps) => { const translation = useTasksTranslation() const queryClient = useQueryClient() @@ -455,7 +472,7 @@ export const TaskDetailView = ({ taskId, onClose, onSuccess, initialPatientId }: reopenTask({ id: taskId }) } }} - className="rounded-full scale-125" + className={clsx('rounded-full scale-125', getPriorityCheckboxColor(formData.priority as TaskPriority | null | undefined))} /> )}
diff --git a/web/components/tasks/TaskList.tsx b/web/components/tasks/TaskList.tsx index c196398c..7f6dbad0 100644 --- a/web/components/tasks/TaskList.tsx +++ b/web/components/tasks/TaskList.tsx @@ -104,6 +104,22 @@ const getPriorityDotColor = (priority: string | null | undefined): string => { } } +const getPriorityCheckboxColor = (priority: string | null | undefined): string => { + if (!priority) return '' + switch (priority) { + case 'P1': + return 'border-green-500 text-green-500 checked:bg-green-500' + case 'P2': + return 'border-blue-500 text-blue-500 checked:bg-blue-500' + case 'P3': + return 'border-orange-500 text-orange-500 checked:bg-orange-500' + case 'P4': + return 'border-red-500 text-red-500 checked:bg-red-500' + default: + return '' + } +} + const STORAGE_KEY_SHOW_DONE = 'task-show-done' export const TaskList = forwardRef(({ tasks: initialTasks, onRefetch, showAssignee = false, initialTaskId, onInitialTaskOpened, headerActions }, ref) => { @@ -446,7 +462,7 @@ export const TaskList = forwardRef(({ tasks: initial reopenTask({ id: task.id }) } }} - className={clsx('rounded-full')} + className={clsx('rounded-full', getPriorityCheckboxColor(task.priority))} />
)