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

Improve state handling #95

Merged
merged 5 commits into from
Jan 14, 2025
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: 2 additions & 0 deletions main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
entityName="page"
/>
),
defaultPendingMs: 300,
defaultPendingMinMs: 200,

Check warning on line 25 in main.tsx

View check run for this annotation

Codecov / codecov/patch

main.tsx#L24-L25

Added lines #L24 - L25 were not covered by tests
})

declare module '@tanstack/react-router' {
Expand Down
27 changes: 14 additions & 13 deletions modules/notifications/NotificationsTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {
DataTable,
DataTableBasicView,
type DataTableProps,
} from '@stanfordspezi/spezi-web-design-system/components/DataTable'
import { parseNilLocalizedText } from '@/modules/firebase/localizedText'
import { type UserMessage } from '@/modules/firebase/models'
Expand Down Expand Up @@ -36,12 +36,14 @@
}),
]

interface NotificationsTableProps {
interface NotificationsTableProps
extends Omit<DataTableProps<UserMessage>, 'data' | 'columns'> {
notifications: UserMessage[]
}

export const NotificationsTable = ({
notifications,
...props

Check warning on line 46 in modules/notifications/NotificationsTable/index.tsx

View check run for this annotation

Codecov / codecov/patch

modules/notifications/NotificationsTable/index.tsx#L46

Added line #L46 was not covered by tests
}: NotificationsTableProps) => (
<DataTable
columns={columns}
Expand All @@ -57,18 +59,17 @@
initialState={{
columnFilters: [{ id: columnIds.isRead, value: false }],
}}
{...props}

Check warning on line 62 in modules/notifications/NotificationsTable/index.tsx

View check run for this annotation

Codecov / codecov/patch

modules/notifications/NotificationsTable/index.tsx#L62

Added line #L62 was not covered by tests
>
{(props) => (
<DataTableBasicView {...props}>
{(rows) =>
rows.map((row) => {
const notification = row.original
return (
<Notification key={notification.id} notification={notification} />
)
})
}
</DataTableBasicView>
{({ rows }) => (
<div>
{rows.map((row) => {
const notification = row.original
return (
<Notification key={notification.id} notification={notification} />

Check warning on line 69 in modules/notifications/NotificationsTable/index.tsx

View check run for this annotation

Codecov / codecov/patch

modules/notifications/NotificationsTable/index.tsx#L64-L69

Added lines #L64 - L69 were not covered by tests
)
})}
</div>

Check warning on line 72 in modules/notifications/NotificationsTable/index.tsx

View check run for this annotation

Codecov / codecov/patch

modules/notifications/NotificationsTable/index.tsx#L71-L72

Added lines #L71 - L72 were not covered by tests
)}
</DataTable>
)
780 changes: 404 additions & 376 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
"lint:ci": "eslint --output-file eslint_report.json --format json .",
"test": "vitest run --coverage",
"docs": "typedoc",
"docs:ci": "typedoc --out ./out/docs --githubPages true"
"docs:ci": "typedoc --out ./out/docs --githubPages true",
"prepush": "npm run lint:fix && tsc --noEmit"
},
"dependencies": {
"@stanfordbdhg/engagehf-models": "^0.4.0",
"@stanfordspezi/spezi-web-design-system": "^0.2.1",
"@stanfordspezi/spezi-web-design-system": "^0.3.0",
"@t3-oss/env-core": "^0.11.1",
"@tanstack/react-query": "^5.59.19",
"@tanstack/react-router": "^1.78.3",
"@tanstack/react-query": "^5.62.0",
"@tanstack/react-router": "^1.85.0",
"@tanstack/react-table": "^8.20.5",
"class-variance-authority": "^0.7.0",
"date-fns": "^3.6.0",
Expand All @@ -53,7 +54,7 @@
"@storybook/react": "^8.3.5",
"@storybook/react-vite": "^8.3.5",
"@storybook/test": "^8.3.5",
"@tanstack/router-plugin": "^1.78.3",
"@tanstack/router-plugin": "^1.84.4",
"@testing-library/jest-dom": "^6",
"@testing-library/react": "^16",
"@total-typescript/ts-reset": "^0.6.1",
Expand Down
9 changes: 7 additions & 2 deletions routes/~__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@

import { Toaster } from '@stanfordspezi/spezi-web-design-system/components/Toaster'
import { SpeziProvider } from '@stanfordspezi/spezi-web-design-system/SpeziProvider'
import { createRootRoute, Outlet, redirect } from '@tanstack/react-router'
import { createRootRoute, Link, Outlet, redirect } from '@tanstack/react-router'
import { type ComponentProps } from 'react'
import { Helmet } from 'react-helmet'
import { auth } from '@/modules/firebase/app'
import { AuthProvider } from '@/modules/firebase/AuthProvider'
import { ReactQueryClientProvider } from '@/modules/query/ReactQueryClientProvider'
import { routes } from '@/modules/routes'
import '../modules/globals.css'

const routerProps: ComponentProps<typeof SpeziProvider>['router'] = {
Link: ({ href, ...props }) => <Link to={href} {...props} />,
}

Check warning on line 22 in routes/~__root.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~__root.tsx#L20-L22

Added lines #L20 - L22 were not covered by tests

const Root = () => (
<AuthProvider>
<SpeziProvider>
<SpeziProvider router={routerProps}>

Check warning on line 26 in routes/~__root.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~__root.tsx#L26

Added line #L26 was not covered by tests
<ReactQueryClientProvider>
<Helmet defaultTitle="ENGAGE-HF" titleTemplate="%s - ENGAGE-HF" />
<Outlet />
Expand Down
24 changes: 24 additions & 0 deletions routes/~_dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,34 @@
// SPDX-License-Identifier: MIT
//

import { ErrorState } from '@stanfordspezi/spezi-web-design-system/components/ErrorState'
import { Spinner } from '@stanfordspezi/spezi-web-design-system/components/Spinner'
import { StateContainer } from '@stanfordspezi/spezi-web-design-system/components/StateContainer'
import { PageTitle } from '@stanfordspezi/spezi-web-design-system/molecules/DashboardLayout'
import { createFileRoute } from '@tanstack/react-router'
import { ShieldX } from 'lucide-react'
import { currentUserQueryOptions } from '@/modules/firebase/UserProvider'
import { queryClient } from '@/modules/query/queryClient'
import { DashboardLayout } from '@/routes/~_dashboard/DashboardLayout'

export const Route = createFileRoute('/_dashboard')({
loader: () => queryClient.ensureQueryData(currentUserQueryOptions()),
pendingComponent: () => (
<DashboardLayout>
<StateContainer grow className="min-h-screen">
<Spinner />
</StateContainer>
</DashboardLayout>

Check warning on line 26 in routes/~_dashboard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard.tsx#L21-L26

Added lines #L21 - L26 were not covered by tests
),
errorComponent: ({ error }) => (
<DashboardLayout title={<PageTitle title="Error" icon={<ShieldX />} />}>
<StateContainer grow>
<ErrorState>

Check warning on line 31 in routes/~_dashboard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard.tsx#L28-L31

Added lines #L28 - L31 were not covered by tests
Unhandled error happened. Please try again later.
<br />
Message: {error.message}
</ErrorState>
</StateContainer>
</DashboardLayout>

Check warning on line 37 in routes/~_dashboard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard.tsx#L33-L37

Added lines #L33 - L37 were not covered by tests
),
})
11 changes: 8 additions & 3 deletions routes/~_dashboard/MenuLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
export const MenuLinks = ({ userType }: MenuLinksProps) => {
const location = useLocation()

const hrefProps = (href: string) => ({
const hrefProps = (href: string, exact = false) => ({

Check warning on line 23 in routes/~_dashboard/MenuLinks.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/MenuLinks.tsx#L23

Added line #L23 was not covered by tests
href,
isActive: location.pathname === href,
isActive:
exact ? location.pathname === href : location.pathname.startsWith(href),

Check warning on line 26 in routes/~_dashboard/MenuLinks.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/MenuLinks.tsx#L25-L26

Added lines #L25 - L26 were not covered by tests
})

const { hasUnreadNotification } = useHasUnreadNotification()

return (
<>
<MenuItem {...hrefProps('/')} label="Home" icon={<Home />} />
<MenuItem
{...hrefProps(routes.home, true)}
label="Home"
icon={<Home />}
/>

Check warning on line 37 in routes/~_dashboard/MenuLinks.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/MenuLinks.tsx#L33-L37

Added lines #L33 - L37 were not covered by tests
<MenuItem
{...hrefProps(routes.notifications)}
label="Notifications"
Expand Down
24 changes: 13 additions & 11 deletions routes/~_dashboard/NotificationsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
// SPDX-License-Identifier: MIT
//

import {
Async,
queriesToAsyncProps,
} from '@stanfordspezi/spezi-web-design-system/components/Async'
import { Button } from '@stanfordspezi/spezi-web-design-system/components/Button'
import {
Card,
CardHeader,
CardTitle,
} from '@stanfordspezi/spezi-web-design-system/components/Card'
import { EmptyState } from '@stanfordspezi/spezi-web-design-system/components/EmptyState'
import { useQuery } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import { Loader2 } from 'lucide-react'
import { useUser } from '@/modules/firebase/UserProvider'
import { filterUnreadNotifications } from '@/modules/notifications/helpers'
import { Notification } from '@/modules/notifications/Notification'
Expand All @@ -25,29 +27,29 @@
export const NotificationsCard = () => {
const { auth } = useUser()

const { data: notifications = [], isLoading } = useQuery({
const notificationQuery = useQuery({

Check warning on line 30 in routes/~_dashboard/NotificationsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/NotificationsCard.tsx#L30

Added line #L30 was not covered by tests
...notificationQueries.list({ userId: auth.uid }),
select: (notifications) =>
filterUnreadNotifications(notifications).slice(0, 3),
})
const notifications = notificationQuery.data ?? []

Check warning on line 35 in routes/~_dashboard/NotificationsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/NotificationsCard.tsx#L35

Added line #L35 was not covered by tests

return (
<Card className="flex flex-col">
<CardHeader>
<CardTitle>Notifications</CardTitle>
</CardHeader>
{isLoading ?
<div className="flex-center py-8">
<Loader2 className="animate-spin text-muted-foreground" />
</div>
: notifications.length === 0 ?
<EmptyState entityName="unread notifications" className="py-8" />
: <div>
<Async
{...queriesToAsyncProps([notificationQuery])}
empty={notifications.length === 0}
entityName="unread notifications"

Check warning on line 45 in routes/~_dashboard/NotificationsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/NotificationsCard.tsx#L42-L45

Added lines #L42 - L45 were not covered by tests
>
<div>

Check warning on line 47 in routes/~_dashboard/NotificationsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/NotificationsCard.tsx#L47

Added line #L47 was not covered by tests
{notifications.map((notification) => (
<Notification key={notification.id} notification={notification} />
))}
</div>
}
</Async>

Check warning on line 52 in routes/~_dashboard/NotificationsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/NotificationsCard.tsx#L52

Added line #L52 was not covered by tests
<Button
asChild
variant="ghostPrimary"
Expand Down
82 changes: 38 additions & 44 deletions routes/~_dashboard/UpcomingAppointmentsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import { queriesToAsyncProps } from '@stanfordspezi/spezi-web-design-system/components/Async'
import {
Card,
CardHeader,
Expand All @@ -17,23 +18,24 @@
} from '@stanfordspezi/spezi-web-design-system/components/DataTable'
import { Tooltip } from '@stanfordspezi/spezi-web-design-system/components/Tooltip'
import { getUserName } from '@stanfordspezi/spezi-web-design-system/modules/auth'
import { combineQueries } from '@stanfordspezi/spezi-web-design-system/utils/query'
import { useQueries, useQuery } from '@tanstack/react-query'
import { useNavigate } from '@tanstack/react-router'
import { createColumnHelper } from '@tanstack/table-core'
import { addWeeks, isBefore, isFuture } from 'date-fns'
import { Info, Loader2 } from 'lucide-react'
import { Info } from 'lucide-react'
import { useMemo } from 'react'
import { appointmentsQueries } from '@/modules/firebase/appointment'
import { routes } from '@/modules/routes'
import { patientsQueries } from '@/modules/user/patients'
import { PatientPageTab } from '@/routes/~_dashboard/~patients/~$id/~index'
import { useNavigateOrOpen } from '@/utils/useNavigateOrOpen'

export const UpcomingAppointmentsCard = () => {
const navigate = useNavigate()
const navigateOrOpen = useNavigateOrOpen()

Check warning on line 34 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L34

Added line #L34 was not covered by tests
const patientsQuery = useQuery(patientsQueries.listUserPatients())
const { data: patients } = patientsQuery

const results = useQueries({
const appointmentsQuery = useQueries({

Check warning on line 38 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L38

Added line #L38 was not covered by tests
queries:
patients?.map((patient) =>
appointmentsQueries.list({
Expand All @@ -42,19 +44,15 @@
}),
) ?? [],
combine: (results) => ({
isLoading: results.some((result) => result.isLoading),
isError: results.some((result) => result.isError),
...combineQueries(results),

Check warning on line 47 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L47

Added line #L47 was not covered by tests
data: results.map((result) => result.data),
isSuccess: results.every((result) => result.isSuccess),
}),
})

const isLoading = patientsQuery.isLoading || results.isLoading

const upcomingAppointments = useMemo(() => {
if (!results.isSuccess || !patients) return []
if (!appointmentsQuery.isSuccess || !patients) return []

Check warning on line 53 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L53

Added line #L53 was not covered by tests
const twoWeeksFromNow = addWeeks(new Date(), 2)
return results.data
return appointmentsQuery.data

Check warning on line 55 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L55

Added line #L55 was not covered by tests
.flatMap((appointments, index) => {
const patient = patients.at(index)
if (!patient || !appointments) return null
Expand All @@ -76,7 +74,7 @@
})
.filter(Boolean)
.sort((a, b) => a.date.getTime() - b.date.getTime())
}, [patients, results.data, results.isSuccess])
}, [patients, appointmentsQuery.data, appointmentsQuery.isSuccess])

Check warning on line 77 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L77

Added line #L77 was not covered by tests

const columnHelper =
createColumnHelper<(typeof upcomingAppointments)[number]>()
Expand All @@ -89,39 +87,35 @@
<Info className="size-5 text-muted-foreground" />
</Tooltip>
</CardHeader>
{isLoading ?
<div className="flex-center py-8">
<Loader2 className="animate-spin text-muted-foreground" />
</div>
: <DataTable
data={upcomingAppointments}
columns={[
columnHelper.accessor('patient.name', {
header: 'Patient',
}),
columnHelper.accessor('date', {
header: 'Start',
cell: dateTimeColumn,
<DataTable
data={upcomingAppointments}
columns={[
columnHelper.accessor('patient.name', {
header: 'Patient',
}),
columnHelper.accessor('date', {
header: 'Start',
cell: dateTimeColumn,
}),
]}
minimal
bordered={false}
pageSize={6}
entityName="upcoming appointments"
tableView={{
onRowClick: (appointment, event) =>
void navigateOrOpen(event, {
to: routes.patients.patient(
appointment.patient.id,
appointment.patient.resourceType,
{
tab: PatientPageTab.appointments,
},
),

Check warning on line 114 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L90-L114

Added lines #L90 - L114 were not covered by tests
}),
]}
minimal
bordered={false}
pageSize={6}
entityName="upcoming appointments"
tableView={{
onRowClick: (appointment) =>
void navigate({
to: routes.patients.patient(
appointment.patient.id,
appointment.patient.resourceType,
{
tab: PatientPageTab.appointments,
},
),
}),
}}
/>
}
}}
{...queriesToAsyncProps([patientsQuery, appointmentsQuery])}
/>

Check warning on line 118 in routes/~_dashboard/UpcomingAppointmentsCard.tsx

View check run for this annotation

Codecov / codecov/patch

routes/~_dashboard/UpcomingAppointmentsCard.tsx#L116-L118

Added lines #L116 - L118 were not covered by tests
</Card>
)
}
Loading
Loading