feat: Implement notifications feed (#8224)
Designs: https://www.figma.com/design/z3aUuOVWUKmdHH0ofmMpEV/Web-app-3.0?node-id=10-50&t=9k6K9k7oJh5tIi09-0  CLOSES CC-219
This commit is contained in:
@@ -1,9 +1,31 @@
|
||||
import { BellAlert } from "@medusajs/icons"
|
||||
import { Drawer, Heading, IconButton } from "@medusajs/ui"
|
||||
import {
|
||||
ArrowDownTray,
|
||||
BellAlert,
|
||||
InformationCircleSolid,
|
||||
} from "@medusajs/icons"
|
||||
import { Drawer, Heading, IconButton, Label, 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"
|
||||
|
||||
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) => {
|
||||
@@ -31,10 +53,107 @@ export const Notifications = () => {
|
||||
</Drawer.Trigger>
|
||||
<Drawer.Content>
|
||||
<Drawer.Header>
|
||||
<Heading>Notifications</Heading>
|
||||
<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>Notifications will go here</Drawer.Body>
|
||||
<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 }}
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
|
||||
import {
|
||||
BellAlert,
|
||||
SidebarLeft,
|
||||
TriangleRightMini,
|
||||
XMark,
|
||||
} from "@medusajs/icons"
|
||||
import { SidebarLeft, TriangleRightMini, XMark } from "@medusajs/icons"
|
||||
import { IconButton, clx } from "@medusajs/ui"
|
||||
import { PropsWithChildren } from "react"
|
||||
import { Link, Outlet, UIMatch, useMatches } from "react-router-dom"
|
||||
@@ -14,6 +9,7 @@ import { useTranslation } from "react-i18next"
|
||||
import { KeybindProvider } from "../../../providers/keybind-provider"
|
||||
import { useGlobalShortcuts } from "../../../providers/keybind-provider/hooks"
|
||||
import { useSidebar } from "../../../providers/sidebar-provider"
|
||||
import { Notifications } from "../notifications"
|
||||
|
||||
export const Shell = ({ children }: PropsWithChildren) => {
|
||||
const globalShortcuts = useGlobalShortcuts()
|
||||
@@ -107,18 +103,6 @@ const Breadcrumbs = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const ToggleNotifications = () => {
|
||||
return (
|
||||
<IconButton
|
||||
size="small"
|
||||
variant="transparent"
|
||||
className="text-ui-fg-muted transition-fg hover:text-ui-fg-subtle"
|
||||
>
|
||||
<BellAlert />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
||||
const ToggleSidebar = () => {
|
||||
const { toggle } = useSidebar()
|
||||
|
||||
@@ -152,7 +136,7 @@ const Topbar = () => {
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-x-3">
|
||||
<ToggleNotifications />
|
||||
<Notifications />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user