Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Labs actions #29

Merged
merged 4 commits into from
Aug 19, 2024
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
175 changes: 175 additions & 0 deletions app/(dashboard)/patients/[id]/LabForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
'use client'
import { Dialog } from '@radix-ui/react-dialog'

Check warning on line 9 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L9

Added line #L9 was not covered by tests
import { type ComponentProps } from 'react'
import { z } from 'zod'
import {

Check warning on line 12 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L11-L12

Added lines #L11 - L12 were not covered by tests
getObservationTypeUnits,
getUnitOfObservationType,
} from '@/app/(dashboard)/patients/clientUtils'
import { type Observation } from '@/app/(dashboard)/patients/utils'
import { ObservationType } from '@/modules/firebase/utils'
import { Button } from '@/packages/design-system/src/components/Button'
import { DatePicker } from '@/packages/design-system/src/components/DatePicker'
import {

Check warning on line 20 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L17-L20

Added lines #L17 - L20 were not covered by tests
DialogContent,
DialogHeader,
DialogTitle,
} from '@/packages/design-system/src/components/Dialog'
import { Input } from '@/packages/design-system/src/components/Input'
import {

Check warning on line 26 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L25-L26

Added lines #L25 - L26 were not covered by tests
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/packages/design-system/src/components/Select'
import { Field } from '@/packages/design-system/src/forms/Field'
import { useForm } from '@/packages/design-system/src/forms/useForm'

Check warning on line 34 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L33-L34

Added lines #L33 - L34 were not covered by tests

export const labFormSchema = z.object({

Check warning on line 36 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L36

Added line #L36 was not covered by tests
type: z.nativeEnum(ObservationType),
effectiveDateTime: z.date(),
unit: z.string(),
value: z.number(),
})

export type LabFormSchema = z.infer<typeof labFormSchema>

interface LabFormProps {
observation?: Observation
onSubmit: (data: LabFormSchema) => Promise<void>
}

export const LabForm = ({ observation, onSubmit }: LabFormProps) => {
const isEdit = !!observation

Check warning on line 51 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L50-L51

Added lines #L50 - L51 were not covered by tests
const defaultType = observation?.type ?? ObservationType.potassium
const form = useForm({

Check warning on line 53 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L53

Added line #L53 was not covered by tests
formSchema: labFormSchema,
defaultValues: {
type: defaultType,
effectiveDateTime:
observation?.effectiveDateTime ?
new Date(observation.effectiveDateTime)
: new Date(),

Check warning on line 60 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L59-L60

Added lines #L59 - L60 were not covered by tests
unit: observation?.unit ?? getUnitOfObservationType(defaultType).unit,
value: observation?.value,
},
})

const [formType, formUnit] = form.watch(['type', 'unit'])
const units = getObservationTypeUnits(formType)

Check warning on line 67 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L66-L67

Added lines #L66 - L67 were not covered by tests

const handleSubmit = form.handleSubmit(async (data) => {
await onSubmit(data)

Check warning on line 70 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L69-L70

Added lines #L69 - L70 were not covered by tests
})

return (

Check warning on line 73 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L73

Added line #L73 was not covered by tests
<form onSubmit={handleSubmit}>
<Field
control={form.control}
name="type"
label="Type"
render={({ field }) => (
<Select
onValueChange={(type) => {
field.onChange(type)
form.setValue(

Check warning on line 83 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L80-L83

Added lines #L80 - L83 were not covered by tests
'unit',
getUnitOfObservationType(type as ObservationType, formUnit)
.unit,
)
}}
{...field}
>
<SelectTrigger>
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent>
{Object.values(ObservationType).map((type) => (
<SelectItem key={type} value={type}>

Check warning on line 96 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L96

Added line #L96 was not covered by tests
{type}
</SelectItem>
))}
</SelectContent>
</Select>
)}
/>
<Field
control={form.control}
name="unit"
label="Unit"
render={({ field }) => (
<Select {...field} key={formType}>

Check warning on line 109 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L109

Added line #L109 was not covered by tests
<SelectTrigger>
<SelectValue placeholder="Unit" />
</SelectTrigger>
<SelectContent>
{units.map((unit) => (
<SelectItem key={unit.unit} value={unit.unit}>

Check warning on line 115 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L115

Added line #L115 was not covered by tests
{unit.unit}
</SelectItem>
))}
</SelectContent>
</Select>
)}
/>
<Field
control={form.control}
name="value"
label="Value"
render={({ field }) => (
<Input

Check warning on line 128 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L128

Added line #L128 was not covered by tests
{...field}
type="number"
onChange={(event) =>
field.onChange(event.currentTarget.valueAsNumber)

Check warning on line 132 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L132

Added line #L132 was not covered by tests
}
/>
)}
/>
<Field
control={form.control}
name="effectiveDateTime"
label="Date"
render={({ field }) => (
<DatePicker

Check warning on line 142 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L142

Added line #L142 was not covered by tests
mode="single"
selected={field.value}
onSelect={(date) => field.onChange(date)}

Check warning on line 145 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L145

Added line #L145 was not covered by tests
defaultMonth={field.value}
toYear={new Date().getFullYear()}
/>
)}
/>
<Button type="submit" isPending={form.formState.isSubmitting}>
{isEdit ? 'Edit' : 'Create'} observation
</Button>
</form>
)
}

type LabFormDialogProps = LabFormProps &
Pick<ComponentProps<typeof Dialog>, 'open' | 'onOpenChange'>

export const LabFormDialog = ({

Check warning on line 161 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L161

Added line #L161 was not covered by tests
open,
onOpenChange,
observation,
...props
}: LabFormDialogProps) => (

Check warning on line 166 in app/(dashboard)/patients/[id]/LabForm.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabForm.tsx#L164-L166

Added lines #L164 - L166 were not covered by tests
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>{observation ? 'Edit' : 'Create'} observation</DialogTitle>
</DialogHeader>
<LabForm {...props} observation={observation} />
</DialogContent>
</Dialog>
)
80 changes: 80 additions & 0 deletions app/(dashboard)/patients/[id]/LabMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
'use client'
import { Pencil, Trash } from 'lucide-react'
import { LabFormDialog } from '@/app/(dashboard)/patients/[id]/LabForm'
import {

Check warning on line 11 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L9-L11

Added lines #L9 - L11 were not covered by tests
deleteObservation,
updateObservation,
} from '@/app/(dashboard)/patients/actions'
import { type Observation } from '@/app/(dashboard)/patients/utils'
import { type ResourceType } from '@/modules/firebase/utils'
import { RowDropdownMenu } from '@/packages/design-system/src/components/DataTable'
import { DropdownMenuItem } from '@/packages/design-system/src/components/DropdownMenu'
import { ConfirmDeleteDialog } from '@/packages/design-system/src/molecules/ConfirmDeleteDialog'
import { useOpenState } from '@/packages/design-system/src/utils/useOpenState'

Check warning on line 20 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L17-L20

Added lines #L17 - L20 were not covered by tests

interface LabMenuProps {
userId: string
resourceType: ResourceType
observation: Observation
}

export const LabMenu = ({

Check warning on line 28 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L28

Added line #L28 was not covered by tests
userId,
resourceType,
observation,
}: LabMenuProps) => {
const deleteConfirm = useOpenState()
const editObservation = useOpenState()

Check warning on line 34 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L32-L34

Added lines #L32 - L34 were not covered by tests

const handleDelete = async () => {
await deleteObservation({

Check warning on line 37 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L36-L37

Added lines #L36 - L37 were not covered by tests
userId,
resourceType,
observationId: observation.id,
observationType: observation.type,
})
deleteConfirm.close()

Check warning on line 43 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L43

Added line #L43 was not covered by tests
}

return (

Check warning on line 46 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L46

Added line #L46 was not covered by tests
<>
<LabFormDialog
onSubmit={async (data) => {
await updateObservation({

Check warning on line 50 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L49-L50

Added lines #L49 - L50 were not covered by tests
userId,
resourceType,
observationId: observation.id,
...data,
})
editObservation.close()

Check warning on line 56 in app/(dashboard)/patients/[id]/LabMenu.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/LabMenu.tsx#L56

Added line #L56 was not covered by tests
}}
open={editObservation.isOpen}
onOpenChange={editObservation.setIsOpen}
observation={observation}
/>
<ConfirmDeleteDialog
open={deleteConfirm.isOpen}
onOpenChange={deleteConfirm.setIsOpen}
entityName="observation"
onDelete={handleDelete}
/>
<RowDropdownMenu>
<DropdownMenuItem onClick={editObservation.open}>
<Pencil />
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={deleteConfirm.open}>
<Trash />
Delete
</DropdownMenuItem>
</RowDropdownMenu>
</>
)
}
103 changes: 78 additions & 25 deletions app/(dashboard)/patients/[id]/Labs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,88 @@
//
'use client'
import { createColumnHelper } from '@tanstack/table-core'
import { type LabsData } from '@/app/(dashboard)/patients/utils'
import { Plus } from 'lucide-react'
import { useMemo } from 'react'
import { LabFormDialog } from '@/app/(dashboard)/patients/[id]/LabForm'
import { LabMenu } from '@/app/(dashboard)/patients/[id]/LabMenu'
import { createObservation } from '@/app/(dashboard)/patients/actions'

Check warning on line 14 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L10-L14

Added lines #L10 - L14 were not covered by tests
import type { LabsData, Observation } from '@/app/(dashboard)/patients/utils'
import { Button } from '@/packages/design-system/src/components/Button'

Check warning on line 16 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L16

Added line #L16 was not covered by tests
import { DataTable } from '@/packages/design-system/src/components/DataTable'
import { useOpenState } from '@/packages/design-system/src/utils/useOpenState'

Check warning on line 18 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L18

Added line #L18 was not covered by tests

interface LabsProps extends LabsData {}

type Observation = LabsData['observations'][number]

const columnHelper = createColumnHelper<Observation>()
const columns = [
columnHelper.accessor('effectiveDateTime', {
header: 'Date',
cell: (props) => {
const value = props.getValue()
const date = value ? new Date(value) : undefined
return date?.toLocaleDateString() ?? ''
},
}),
columnHelper.accessor('type', {
header: 'Type',
}),
columnHelper.accessor('value', {
header: 'Value',
cell: (props) => {
const observation = props.row.original
return `${observation.value} ${observation.unit}`
},
}),
]

export const Labs = ({ observations }: LabsProps) => {
return <DataTable columns={columns} data={observations} />
export const Labs = ({ observations, userId, resourceType }: LabsProps) => {
const createDialog = useOpenState()

Check warning on line 25 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L24-L25

Added lines #L24 - L25 were not covered by tests

const columns = useMemo(
() => [

Check warning on line 28 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L27-L28

Added lines #L27 - L28 were not covered by tests
columnHelper.accessor('effectiveDateTime', {
header: 'Date',
cell: (props) => {
const value = props.getValue()

Check warning on line 32 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L31-L32

Added lines #L31 - L32 were not covered by tests
const date = value ? new Date(value) : undefined
return date?.toLocaleDateString() ?? ''
},
}),
columnHelper.accessor('type', {
header: 'Type',
}),
columnHelper.accessor('value', {
header: 'Value',
cell: (props) => {
const observation = props.row.original
return `${observation.value} ${observation.unit}`

Check warning on line 44 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L42-L44

Added lines #L42 - L44 were not covered by tests
},
}),
columnHelper.display({
id: 'actions',
cell: (props) => (
<LabMenu

Check warning on line 50 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L50

Added line #L50 was not covered by tests
observation={props.row.original}
userId={userId}
resourceType={resourceType}
/>
),
}),
],
[resourceType, userId],
)

return (

Check warning on line 61 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L61

Added line #L61 was not covered by tests
<>
<LabFormDialog
onSubmit={async (data) => {
await createObservation({

Check warning on line 65 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L64-L65

Added lines #L64 - L65 were not covered by tests
userId,
resourceType,
...data,
})
createDialog.close()

Check warning on line 70 in app/(dashboard)/patients/[id]/Labs.tsx

View check run for this annotation

Codecov / codecov/patch

app/(dashboard)/patients/[id]/Labs.tsx#L70

Added line #L70 was not covered by tests
}}
open={createDialog.isOpen}
onOpenChange={createDialog.setIsOpen}
/>
<DataTable
columns={columns}
data={observations}
header={
<>
<Button
size="sm"
variant="secondary"
className="ml-auto"
onClick={createDialog.open}
>
<Plus />
Add observation
</Button>
</>
}
/>
</>
)
}
Loading
Loading