Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/components/patients/PatientDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ export const PatientDetailView = ({
return (
<FormElementWrapper label={translation('patientActions')}>
{() => (
<div className="flex gap-2 flex-wrap">
<div className="flex gap-4 flex-wrap">
{patient.state !== PatientState.Admitted && (
<Button
onClick={() => admitPatient({ id: patientId })}
Expand Down
48 changes: 25 additions & 23 deletions web/components/patients/PatientList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export const PatientList = forwardRef<PatientListRef, PatientListProps>(({ locat

return (
<div className="flex flex-col h-full gap-4">
<div className="flex flex-col sm:flex-row justify-between w-full gap-4 -mx-4 px-4 lg:mx-0 lg:pl-0 lg:pr-4">
<div className="flex flex-col sm:flex-row justify-between w-full gap-4">
<div className="w-full sm:max-w-md">
<SearchBar
placeholder={translation('search')}
Expand All @@ -244,34 +244,36 @@ export const PatientList = forwardRef<PatientListRef, PatientListProps>(({ locat
onSearch={() => null}
/>
</div>
<div className="flex items-center gap-2">
<Tooltip tooltip="Table View" position="top">
<Button
layout="icon"
color={viewType === 'table' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'table' ? undefined : 'text'}
onClick={() => toggleView('table')}
>
<TableIcon className="size-5" />
</Button>
</Tooltip>
<Tooltip tooltip="Card View" position="top">
<Button
layout="icon"
color={viewType === 'card' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'card' ? undefined : 'text'}
onClick={() => toggleView('card')}
>
<LayoutGrid className="size-5" />
</Button>
</Tooltip>
<div className="flex flex-col sm:flex-row items-center gap-4 w-full sm:w-auto sm:ml-auto lg:pr-4">
<div className="flex items-center gap-2 flex-shrink-0">
<Tooltip tooltip="Table View" position="top">
<Button
layout="icon"
color={viewType === 'table' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'table' ? undefined : 'text'}
onClick={() => toggleView('table')}
>
<TableIcon className="size-5" />
</Button>
</Tooltip>
<Tooltip tooltip="Card View" position="top">
<Button
layout="icon"
color={viewType === 'card' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'card' ? undefined : 'text'}
onClick={() => toggleView('card')}
>
<LayoutGrid className="size-5" />
</Button>
</Tooltip>
</div>
<Button
startIcon={<PlusIcon />}
onClick={() => {
setSelectedPatient(undefined)
setIsPanelOpen(true)
}}
className="w-full sm:w-auto min-w-[13rem]"
className="w-full sm:w-auto min-w-[13rem] flex-shrink-0"
>
{translation('addPatient')}
</Button>
Expand Down
147 changes: 117 additions & 30 deletions web/components/tasks/TaskList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } from 'react'
import { Avatar, Button, Checkbox, CheckboxUncontrolled, FillerRowElement, SearchBar, Table, Tooltip } from '@helpwave/hightide'
import { PlusIcon, Table as TableIcon, LayoutGrid } from 'lucide-react'
import { useCompleteTaskMutation, useReopenTaskMutation } from '@/api/gql/generated'
import { Avatar, Button, Checkbox, CheckboxUncontrolled, ConfirmDialog, Dialog, FillerRowElement, SearchBar, Select, SelectOption, Table, Tooltip } from '@helpwave/hightide'
import { PlusIcon, Table as TableIcon, LayoutGrid, UserCheck } from 'lucide-react'
import { useAssignTaskMutation, useCompleteTaskMutation, useGetUsersQuery, useReopenTaskMutation } from '@/api/gql/generated'
import clsx from 'clsx'
import { SmartDate } from '@/utils/date'
import { SidePanel } from '@/components/layout/SidePanel'
Expand Down Expand Up @@ -68,15 +68,23 @@ const STORAGE_KEY_SHOW_DONE = 'task-show-done'

export const TaskList = forwardRef<TaskListRef, TaskListProps>(({ tasks: initialTasks, onRefetch, showAssignee = false, initialTaskId, onInitialTaskOpened }, ref) => {
const translation = useTasksTranslation()
const { totalPatientsCount } = useTasksContext()
const { totalPatientsCount, user } = useTasksContext()
const { viewType, toggleView } = useTaskViewToggle()
const { mutate: completeTask } = useCompleteTaskMutation({ onSuccess: onRefetch })
const { mutate: reopenTask } = useReopenTaskMutation({ onSuccess: onRefetch })
const { mutate: assignTask } = useAssignTaskMutation({ onSuccess: onRefetch })
const { data: usersData } = useGetUsersQuery(undefined, {
refetchInterval: 15000,
refetchOnWindowFocus: true,
})

const [selectedPatientId, setSelectedPatientId] = useState<string | null>(null)
const [taskDialogState, setTaskDialogState] = useState<TaskDialogState>({ isOpen: false })
const [searchQuery, setSearchQuery] = useState('')
const [openedTaskId, setOpenedTaskId] = useState<string | null>(null)
const [isHandoverDialogOpen, setIsHandoverDialogOpen] = useState(false)
const [selectedUserId, setSelectedUserId] = useState<string | null>(null)
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false)
const [showDone, setShowDone] = useState<boolean>(() => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem(STORAGE_KEY_SHOW_DONE)
Expand Down Expand Up @@ -149,6 +157,40 @@ export const TaskList = forwardRef<TaskListRef, TaskListProps>(({ tasks: initial
})
}, [initialTasks, searchQuery, showDone])

const openTasks = useMemo(() => {
return initialTasks.filter(t => !t.done && t.assignee?.id === user?.id)
}, [initialTasks, user?.id])

const users = useMemo(() => {
return usersData?.users?.filter(u => u.id !== user?.id) || []
}, [usersData, user?.id])

const canHandover = openTasks.length > 0 && users.length > 0

const handleHandoverClick = () => {
if (!canHandover) {
return
}
setIsHandoverDialogOpen(true)
}

const handleUserSelect = (userId: string) => {
setSelectedUserId(userId)
setIsHandoverDialogOpen(false)
setIsConfirmDialogOpen(true)
}

const handleConfirmHandover = () => {
if (!selectedUserId) return

openTasks.forEach(task => {
assignTask({ id: task.id, userId: selectedUserId })
})

setIsConfirmDialogOpen(false)
setSelectedUserId(null)
}

const columns = useMemo<ColumnDef<TaskViewModel>[]>(
() => {
const cols: ColumnDef<TaskViewModel>[] = [
Expand Down Expand Up @@ -298,7 +340,7 @@ export const TaskList = forwardRef<TaskListRef, TaskListProps>(({ tasks: initial

return (
<div className="flex flex-col h-full gap-4">
<div className="flex flex-col sm:flex-row justify-between w-full gap-4 -mx-4 px-4 lg:mx-0 lg:pl-0 lg:pr-4">
<div className="flex flex-col sm:flex-row justify-between w-full gap-4">
<div className="w-full sm:max-w-md">
<SearchBar
placeholder={translation('search')}
Expand All @@ -307,38 +349,51 @@ export const TaskList = forwardRef<TaskListRef, TaskListProps>(({ tasks: initial
onSearch={() => null}
/>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2">
<Checkbox
checked={showDone}
onCheckedChange={handleShowDoneChange}
/>
<span className="text-sm text-description">{translation('showDone') || 'Show done'}</span>
<div className="flex flex-wrap items-center gap-4 w-full sm:w-auto sm:ml-auto lg:pr-4">
<div className="flex items-center justify-between gap-4 w-full sm:w-auto">
<div className="flex items-center gap-2">
<Checkbox
checked={showDone}
onCheckedChange={handleShowDoneChange}
/>
<span className="text-sm text-description whitespace-nowrap">{translation('showDone') || 'Show done'}</span>
</div>
<div className="flex items-center gap-2">
<Tooltip tooltip="Table View" position="top">
<Button
layout="icon"
color={viewType === 'table' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'table' ? undefined : 'text'}
onClick={() => toggleView('table')}
>
<TableIcon className="size-5" />
</Button>
</Tooltip>
<Tooltip tooltip="Card View" position="top">
<Button
layout="icon"
color={viewType === 'card' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'card' ? undefined : 'text'}
onClick={() => toggleView('card')}
>
<LayoutGrid className="size-5" />
</Button>
</Tooltip>
</div>
</div>
<Tooltip tooltip="Table View" position="top">
{canHandover && (
<Button
layout="icon"
color={viewType === 'table' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'table' ? undefined : 'text'}
onClick={() => toggleView('table')}
startIcon={<UserCheck className="size-5"/>}
onClick={handleHandoverClick}
className="w-full sm:w-auto flex-shrink-0"
>
<TableIcon className="size-5" />
{translation('shiftHandover') || 'Shift Handover'}
</Button>
</Tooltip>
<Tooltip tooltip="Card View" position="top">
<Button
layout="icon"
color={viewType === 'card' ? 'primary' : 'neutral'}
coloringStyle={viewType === 'card' ? undefined : 'text'}
onClick={() => toggleView('card')}
>
<LayoutGrid className="size-5" />
</Button>
</Tooltip>
)}
<Button
startIcon={<PlusIcon/>}
onClick={() => setTaskDialogState({ isOpen: true })}
className="w-full sm:w-auto min-w-[13rem]"
className="w-full sm:w-auto min-w-[13rem] flex-shrink-0"
disabled={!hasPatients}
>
{translation('addTask')}
Expand Down Expand Up @@ -410,6 +465,38 @@ export const TaskList = forwardRef<TaskListRef, TaskListProps>(({ tasks: initial
/>
)}
</SidePanel>
<Dialog
isOpen={isHandoverDialogOpen}
onClose={() => {
setIsHandoverDialogOpen(false)
setSelectedUserId(null)
}}
titleElement={translation('shiftHandover') || 'Shift Handover'}
description={translation('shiftHandoverDescription') || `Select a user to transfer all ${openTasks.length} open task${openTasks.length !== 1 ? 's' : ''} assigned to you.`}
>
<div className="flex flex-col gap-4">
<Select
value={selectedUserId || ''}
onValueChanged={handleUserSelect}
>
{users.map(u => (
<SelectOption key={u.id} value={u.id}>
{u.name}
</SelectOption>
))}
</Select>
</div>
</Dialog>
<ConfirmDialog
isOpen={isConfirmDialogOpen}
onCancel={() => {
setIsConfirmDialogOpen(false)
setSelectedUserId(null)
}}
onConfirm={handleConfirmHandover}
titleElement={translation('confirmShiftHandover') || 'Confirm Shift Handover'}
description={translation('confirmShiftHandoverDescription') || `Are you sure you want to transfer ${openTasks.length} open task${openTasks.length !== 1 ? 's' : ''} to ${users.find(u => u.id === selectedUserId)?.name || 'the selected user'}?`}
/>
</div>
)
})
Expand Down
12 changes: 12 additions & 0 deletions web/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export type TasksTranslationEntries = {
'collapseAll': string,
'confirm': string,
'confirmSelection': string,
'confirmShiftHandover': string,
'confirmShiftHandoverDescription': string,
'create': string,
'createdAt': string,
'createTask': string,
Expand Down Expand Up @@ -172,6 +174,8 @@ export type TasksTranslationEntries = {
'settings': string,
'settingsDescription': string,
'sex': string,
'shiftHandover': string,
'shiftHandoverDescription': string,
'showDone': string,
'sPropertySubjectType': (values: { subject: string }) => string,
'sPropertyType': (values: { type: string }) => string,
Expand Down Expand Up @@ -232,6 +236,8 @@ export const tasksTranslation: Translation<TasksTranslationLocales, Partial<Task
'collapseAll': `Alle einklappen`,
'confirm': `Bestätigen`,
'confirmSelection': `Auswahl bestätigen`,
'confirmShiftHandover': `Schichtübergabe bestätigen`,
'confirmShiftHandoverDescription': `Sind Sie sicher, dass Sie alle offenen Aufgaben an den ausgewählten Benutzer übertragen möchten?`,
'create': `Erstellen`,
'createdAt': `Erstellt am`,
'createTask': `Aufgabe erstellen`,
Expand Down Expand Up @@ -457,6 +463,8 @@ export const tasksTranslation: Translation<TasksTranslationLocales, Partial<Task
'settings': `Einstellungen`,
'settingsDescription': `Hier kannst du die App Konfiguration ändern.`,
'sex': `Geschlecht`,
'shiftHandover': `Schichtübergabe`,
'shiftHandoverDescription': `Wählen Sie einen Benutzer aus, um alle Ihnen zugewiesenen offenen Aufgaben zu übertragen.`,
'showDone': `Erledigte anzeigen`,
'sPropertySubjectType': ({ subject }): string => {
return TranslationGen.resolveSelect(subject, {
Expand Down Expand Up @@ -544,6 +552,8 @@ export const tasksTranslation: Translation<TasksTranslationLocales, Partial<Task
'collapseAll': `Collapse All`,
'confirm': `Confirm`,
'confirmSelection': `Confirm Selection`,
'confirmShiftHandover': `Confirm Shift Handover`,
'confirmShiftHandoverDescription': `Are you sure you want to transfer all open tasks to the selected user?`,
'conflictDetected': `Conflict Detected`,
'create': `Create`,
'createdAt': `Created at`,
Expand Down Expand Up @@ -770,6 +780,8 @@ export const tasksTranslation: Translation<TasksTranslationLocales, Partial<Task
'settings': `Settings`,
'settingsDescription': `Here you can change the app configuration.`,
'sex': `Sex`,
'shiftHandover': `Shift Handover`,
'shiftHandoverDescription': `Select a user to transfer all open tasks assigned to you.`,
'showDone': `Show done`,
'sPropertySubjectType': ({ subject }): string => {
return TranslationGen.resolveSelect(subject, {
Expand Down
6 changes: 5 additions & 1 deletion web/locales/de-DE.arb
Original file line number Diff line number Diff line change
Expand Up @@ -363,5 +363,9 @@
"noNotifications": "Keine aktuellen Updates",
"notifications": "Benachrichtigungen",
"dismissAll": "Alle verwerfen",
"security": "Sicherheit"
"security": "Sicherheit",
"shiftHandover": "Schichtübergabe",
"shiftHandoverDescription": "Wählen Sie einen Benutzer aus, um alle Ihnen zugewiesenen offenen Aufgaben zu übertragen.",
"confirmShiftHandover": "Schichtübergabe bestätigen",
"confirmShiftHandoverDescription": "Sind Sie sicher, dass Sie alle offenen Aufgaben an den ausgewählten Benutzer übertragen möchten?"
}
6 changes: 5 additions & 1 deletion web/locales/en-US.arb
Original file line number Diff line number Diff line change
Expand Up @@ -364,5 +364,9 @@
"noNotifications": "No recent updates",
"notifications": "Notifications",
"dismissAll": "Dismiss All",
"security": "Security"
"security": "Security",
"shiftHandover": "Shift Handover",
"shiftHandoverDescription": "Select a user to transfer all open tasks assigned to you.",
"confirmShiftHandover": "Confirm Shift Handover",
"confirmShiftHandoverDescription": "Are you sure you want to transfer all open tasks to the selected user?"
}
Loading