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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user