Files
medusa-store/packages/admin-next/dashboard/src/components/layout/notifications/notifications.tsx
T
Stevche Radevski 47c132c70b feat: Add empty state to notifications and remove no more data (#8277)
CLOSES CC-242
CLOSES CC-243
CLOSES CC-245
2024-07-25 15:08:16 +00:00

179 lines
5.1 KiB
TypeScript

import {
ArrowDownTray,
BellAlert,
BellAlertDone,
InformationCircleSolid,
} from "@medusajs/icons"
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 { InfiniteList } from "../../common/infinite-list"
import { sdk } from "../../../lib/client"
import { notificationQueryKeys } from "../../../hooks/api"
import { TFunction } from "i18next"
interface NotificationData {
title: string
description?: string
file?: {
filename?: string
url?: string
mimeType?: string
}
}
export const Notifications = () => {
const [open, setOpen] = useState(false)
const { t } = useTranslation()
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "n" && (e.metaKey || e.ctrlKey)) {
setOpen((prev) => !prev)
}
}
document.addEventListener("keydown", onKeyDown)
return () => {
document.removeEventListener("keydown", onKeyDown)
}
}, [])
return (
<Drawer open={open} onOpenChange={setOpen}>
<Drawer.Trigger asChild>
<IconButton
variant="transparent"
className="text-ui-fg-muted hover:text-ui-fg-subtle"
>
<BellAlert />
</IconButton>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title asChild>
<Heading>{t("notifications.domain")}</Heading>
</Drawer.Title>
<Drawer.Description className="sr-only">
{t("notifications.accessibility.description")}
</Drawer.Description>
</Drawer.Header>
<Drawer.Body className="overflow-y-auto px-0">
<InfiniteList<
HttpTypes.AdminNotificationListResponse,
HttpTypes.AdminNotification,
HttpTypes.AdminNotificationListParams
>
responseKey="notifications"
queryKey={notificationQueryKeys.all}
queryFn={(params) => sdk.admin.notification.list(params)}
queryOptions={{ enabled: open }}
renderEmpty={() => <NotificationsEmptyState t={t} />}
renderItem={(notification) => {
return (
<Notification
key={notification.id}
notification={notification}
/>
)
}}
/>
</Drawer.Body>
</Drawer.Content>
</Drawer>
)
}
const Notification = ({
notification,
}: {
notification: HttpTypes.AdminNotification
}) => {
const data = notification.data as unknown as NotificationData | undefined
// We need at least the title to render a notification in the feed
if (!data?.title) {
return null
}
return (
<>
<div className="flex items-start justify-center gap-3 border-b p-6">
<div className="text-ui-fg-muted flex size-5 items-center justify-center">
<InformationCircleSolid />
</div>
<div className="flex w-full flex-col gap-y-3">
<div>
<div className="align-center flex flex-row justify-between">
<Text size="small" leading="compact" weight="plus">
{data.title}
</Text>
<Text
as={"span"}
className="text-ui-fg-subtle"
size="small"
leading="compact"
weight="plus"
>
{formatDistance(notification.created_at, new Date(), {
addSuffix: true,
})}
</Text>
</div>
{!!data.description && (
<Text
className="text-ui-fg-subtle whitespace-pre-line"
size="small"
>
{data.description}
</Text>
)}
</div>
<NotificationFile file={data.file} />
</div>
</div>
</>
)
}
const NotificationFile = ({ file }: { file: NotificationData["file"] }) => {
if (!file?.url) {
return null
}
return (
<div className="shadow-elevation-card-rest bg-ui-bg-component transition-fg rounded-md px-3 py-2">
<div className="flex w-full flex-row items-center justify-between gap-2">
<Text size="small" leading="compact">
{file?.filename ?? file.url}
</Text>
<IconButton variant="transparent" asChild>
<a href={file.url} download={file.filename ?? `${Date.now()}`}>
<ArrowDownTray />
</a>
</IconButton>
</div>
</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>
)
}