feat: Add empty state to notifications and remove no more data (#8277)

CLOSES CC-242
CLOSES CC-243
CLOSES CC-245
This commit is contained in:
Stevche Radevski
2024-07-25 17:08:16 +02:00
committed by GitHub
parent 5d9ea4f718
commit 47c132c70b
6 changed files with 93 additions and 14 deletions

View File

@@ -1,15 +1,14 @@
import { QueryKey, useInfiniteQuery } from "@tanstack/react-query"
import { ReactNode, useEffect, useMemo, useRef } from "react"
import { Skeleton } from "../skeleton"
import { toast } from "@medusajs/ui"
import { Spinner } from "@medusajs/icons"
import { useTranslation } from "react-i18next"
type InfiniteListProps<TResponse, TEntity, TParams> = {
queryKey: QueryKey
queryFn: (params: TParams) => Promise<TResponse>
queryOptions?: { enabled?: boolean }
renderItem: (item: TEntity) => ReactNode
renderEmpty: () => ReactNode
responseKey: keyof TResponse
pageSize?: number
}
@@ -23,11 +22,10 @@ export const InfiniteList = <
queryFn,
queryOptions,
renderItem,
renderEmpty,
responseKey,
pageSize = 20,
}: InfiniteListProps<TResponse, TEntity, TParams>) => {
const { t } = useTranslation()
const {
data,
error,
@@ -114,18 +112,22 @@ export const InfiniteList = <
}, [error])
if (isPending) {
return <Skeleton className="h-[148px] w-full rounded-lg" />
return (
<div className="flex h-full flex-col items-center justify-center">
<Spinner className="animate-spin" />
</div>
)
}
return (
<div ref={parentRef}>
{items &&
items.map((item) => <div key={item.id}>{renderItem(item)}</div>)}
<div ref={parentRef} className="h-full">
{items?.length
? items.map((item) => <div key={item.id}>{renderItem(item)}</div>)
: renderEmpty()}
{(isFetching || !hasNextPage) && (
<div className="my-4 flex flex-col items-center justify-center">
{isFetching && <Spinner className="animate-spin" />}
{!hasNextPage && <p className="m-2">{t("general.noMoreData")}</p>}
{isFetching && (
<div className="flex flex-col items-center justify-center py-4">
<Spinner className="animate-spin" />
</div>
)}
</div>

View File

@@ -1,17 +1,18 @@
import {
ArrowDownTray,
BellAlert,
BellAlertDone,
InformationCircleSolid,
} from "@medusajs/icons"
import { Drawer, Heading, IconButton, Label, Text } from "@medusajs/ui"
import { Drawer, Heading, IconButton, Text } from "@medusajs/ui"
import { useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { HttpTypes } from "@medusajs/types"
import { formatDistance } from "date-fns"
import { Divider } from "../../common/divider"
import { InfiniteList } from "../../common/infinite-list"
import { sdk } from "../../../lib/client"
import { notificationQueryKeys } from "../../../hooks/api"
import { TFunction } from "i18next"
interface NotificationData {
title: string
@@ -70,6 +71,7 @@ export const Notifications = () => {
queryKey={notificationQueryKeys.all}
queryFn={(params) => sdk.admin.notification.list(params)}
queryOptions={{ enabled: open }}
renderEmpty={() => <NotificationsEmptyState t={t} />}
renderItem={(notification) => {
return (
<Notification
@@ -157,3 +159,20 @@ const NotificationFile = ({ file }: { file: NotificationData["file"] }) => {
</div>
)
}
const NotificationsEmptyState = ({ t }: { t: TFunction }) => {
return (
<div className="flex h-full flex-col items-center justify-center">
<BellAlertDone />
<Text size="small" leading="compact" weight="plus" className="mt-3">
{t("notifications.emptyState.title")}
</Text>
<Text
size="small"
className="text-ui-fg-muted mt-1 max-w-[294px] text-center"
>
{t("notifications.emptyState.description")}
</Text>
</div>
)
}

View File

@@ -2112,6 +2112,10 @@
},
"notifications": {
"domain": "Notifications",
"emptyState": {
"title": "No notifications",
"description": "You don't have any notifications at the moment, but once you do they will live here."
},
"accessibility": {
"description": "notifications about Medusa activities will be listed here."
}

View File

@@ -0,0 +1,16 @@
import * as React from "react"
import { cleanup, render, screen } from "@testing-library/react"
import BellAlertDone from "../bell-alert-done"
describe("BellAlertDone", () => {
it("should render the icon without errors", async () => {
render(<BellAlertDone data-testid="icon" />)
const svgElement = screen.getByTestId("icon")
expect(svgElement).toBeInTheDocument()
cleanup()
})
})

View File

@@ -0,0 +1,37 @@
import * as React from "react"
import type { IconProps } from "../types"
const BellAlertDone = React.forwardRef<SVGSVGElement, IconProps>(
({ color = "currentColor", ...props }, ref) => {
return (
<svg
width={15}
height={15}
fill="none"
xmlns="http://www.w3.org/2000/svg"
ref={ref}
{...props}
>
<g>
<circle cx="12.75" cy="2.5" r="2.5" fill="#60A5FA" />
<circle
cx="12.75"
cy="2.5"
r="2"
stroke={color}
stroke-opacity="0.12"
/>
<path
d="M9.2426 0.575398C8.77142 0.427273 8.27003 0.347412 7.75 0.347412C5.00423 0.347412 2.77778 2.57387 2.77778 5.31963V9.54186C2.77778 10.1099 2.31801 10.5696 1.75 10.5696C1.33579 10.5696 1 10.9054 1 11.3196C1 11.7338 1.33579 12.0696 1.75 12.0696H13.75C14.1642 12.0696 14.5 11.7338 14.5 11.3196C14.5 10.9054 14.1642 10.5696 13.75 10.5696C13.182 10.5696 12.7222 10.1099 12.7222 9.54186V6.49991C12.1913 6.49629 11.6849 6.38924 11.2222 6.19788V9.54186C11.2222 9.90786 11.2999 10.2557 11.4398 10.5696H4.06022C4.20006 10.2557 4.27778 9.90786 4.27778 9.54186V5.31963C4.27778 3.40229 5.83266 1.84741 7.75 1.84741C8.10883 1.84741 8.45496 1.90187 8.78058 2.00297C8.84406 1.4908 9.00436 1.00865 9.2426 0.575398Z"
fill={color}
/>
<path
d="M9.16158 13.0394C9.07714 12.9354 8.95091 12.875 8.81669 12.875H6.68425C6.55003 12.875 6.4238 12.9354 6.33936 13.0394C6.25491 13.1434 6.22203 13.2803 6.24958 13.411C6.40336 14.1417 7.02114 14.6528 7.75091 14.6528C8.48069 14.6528 9.09847 14.1417 9.25225 13.411C9.2798 13.2803 9.24603 13.1434 9.16158 13.0394Z"
fill={color}
/>
</g>
</svg>
)
}
)
BellAlertDone.displayName = "BellAlertDone"
export default BellAlertDone

View File

@@ -41,6 +41,7 @@ export { default as BarsThree } from "./bars-three"
export { default as Beaker } from "./beaker"
export { default as BellAlertSolid } from "./bell-alert-solid"
export { default as BellAlert } from "./bell-alert"
export { default as BellAlertDone } from "./bell-alert-done"
export { default as BoltSolid } from "./bolt-solid"
export { default as Bolt } from "./bolt"
export { default as BookOpen } from "./book-open"