hospital portal
This commit is contained in:
241
frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx
Executable file
241
frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx
Executable file
@@ -0,0 +1,241 @@
|
||||
import { noCase } from 'change-case';
|
||||
import { useState } from 'react';
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
List,
|
||||
Badge,
|
||||
Button,
|
||||
Avatar,
|
||||
Tooltip,
|
||||
Divider,
|
||||
Typography,
|
||||
ListItemText,
|
||||
ListSubheader,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
} from '@mui/material';
|
||||
// utils
|
||||
import { fToNow } from '../../../utils/formatTime';
|
||||
// _mock_
|
||||
import { _notifications } from '../../../_mock';
|
||||
// components
|
||||
import Iconify from '../../../components/Iconify';
|
||||
import Scrollbar from '../../../components/Scrollbar';
|
||||
import MenuPopover from '../../../components/MenuPopover';
|
||||
import { IconButtonAnimate } from '../../../components/animate';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export default function NotificationsPopover() {
|
||||
const [notifications, setNotifications] = useState(_notifications);
|
||||
|
||||
const totalUnRead = notifications.filter((item) => item.isUnRead === true).length;
|
||||
|
||||
const [open, setOpen] = useState<HTMLElement | null>(null);
|
||||
|
||||
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setOpen(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(null);
|
||||
};
|
||||
|
||||
const handleMarkAllAsRead = () => {
|
||||
setNotifications(
|
||||
notifications.map((notification) => ({
|
||||
...notification,
|
||||
isUnRead: false,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButtonAnimate
|
||||
color={open ? 'primary' : 'default'}
|
||||
onClick={handleOpen}
|
||||
sx={{ width: 40, height: 40 }}
|
||||
>
|
||||
<Badge badgeContent={totalUnRead} color="error">
|
||||
<Iconify icon="eva:bell-fill" width={20} height={20} />
|
||||
</Badge>
|
||||
</IconButtonAnimate>
|
||||
|
||||
<MenuPopover
|
||||
open={Boolean(open)}
|
||||
anchorEl={open}
|
||||
onClose={handleClose}
|
||||
sx={{ width: 360, p: 0, mt: 1.5, ml: 0.75 }}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', py: 2, px: 2.5 }}>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="subtitle1">Notifications</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
You have {totalUnRead} unread messages
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{totalUnRead > 0 && (
|
||||
<Tooltip title=" Mark all as read">
|
||||
<IconButtonAnimate color="primary" onClick={handleMarkAllAsRead}>
|
||||
<Iconify icon="eva:done-all-fill" width={20} height={20} />
|
||||
</IconButtonAnimate>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ borderStyle: 'dashed' }} />
|
||||
|
||||
<Scrollbar sx={{ height: { xs: 340, sm: 'auto' } }}>
|
||||
<List
|
||||
disablePadding
|
||||
subheader={
|
||||
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
|
||||
New
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
{notifications.slice(0, 2).map((notification) => (
|
||||
<NotificationItem key={notification.id} notification={notification} />
|
||||
))}
|
||||
</List>
|
||||
|
||||
<List
|
||||
disablePadding
|
||||
subheader={
|
||||
<ListSubheader disableSticky sx={{ py: 1, px: 2.5, typography: 'overline' }}>
|
||||
Before that
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
{notifications.slice(2, 5).map((notification) => (
|
||||
<NotificationItem key={notification.id} notification={notification} />
|
||||
))}
|
||||
</List>
|
||||
</Scrollbar>
|
||||
|
||||
<Divider sx={{ borderStyle: 'dashed' }} />
|
||||
|
||||
<Box sx={{ p: 1 }}>
|
||||
<Button fullWidth disableRipple>
|
||||
View All
|
||||
</Button>
|
||||
</Box>
|
||||
</MenuPopover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type NotificationItemProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
avatar: string | null;
|
||||
type: string;
|
||||
createdAt: Date;
|
||||
isUnRead: boolean;
|
||||
};
|
||||
|
||||
function NotificationItem({ notification }: { notification: NotificationItemProps }) {
|
||||
const { avatar, title } = renderContent(notification);
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
sx={{
|
||||
py: 1.5,
|
||||
px: 2.5,
|
||||
mt: '1px',
|
||||
...(notification.isUnRead && {
|
||||
bgcolor: 'action.selected',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ bgcolor: 'background.neutral' }}>{avatar}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={title}
|
||||
secondary={
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: 'text.disabled',
|
||||
}}
|
||||
>
|
||||
<Iconify icon="eva:clock-outline" sx={{ mr: 0.5, width: 16, height: 16 }} />
|
||||
{fToNow(notification.createdAt)}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
function renderContent(notification: NotificationItemProps) {
|
||||
const title = (
|
||||
<Typography variant="subtitle2">
|
||||
{notification.title}
|
||||
<Typography component="span" variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{noCase(notification.description)}
|
||||
</Typography>
|
||||
</Typography>
|
||||
);
|
||||
|
||||
if (notification.type === 'order_placed') {
|
||||
return {
|
||||
avatar: (
|
||||
<img
|
||||
alt={notification.title}
|
||||
src="https://minimal-assets-api.vercel.app/assets/icons/ic_notification_package.svg"
|
||||
/>
|
||||
),
|
||||
title,
|
||||
};
|
||||
}
|
||||
if (notification.type === 'order_shipped') {
|
||||
return {
|
||||
avatar: (
|
||||
<img
|
||||
alt={notification.title}
|
||||
src="https://minimal-assets-api.vercel.app/assets/icons/ic_notification_shipping.svg"
|
||||
/>
|
||||
),
|
||||
title,
|
||||
};
|
||||
}
|
||||
if (notification.type === 'mail') {
|
||||
return {
|
||||
avatar: (
|
||||
<img
|
||||
alt={notification.title}
|
||||
src="https://minimal-assets-api.vercel.app/assets/icons/ic_notification_mail.svg"
|
||||
/>
|
||||
),
|
||||
title,
|
||||
};
|
||||
}
|
||||
if (notification.type === 'chat_message') {
|
||||
return {
|
||||
avatar: (
|
||||
<img
|
||||
alt={notification.title}
|
||||
src="https://minimal-assets-api.vercel.app/assets/icons/ic_notification_chat.svg"
|
||||
/>
|
||||
),
|
||||
title,
|
||||
};
|
||||
}
|
||||
return {
|
||||
avatar: notification.avatar ? <img alt={notification.title} src={notification.avatar} /> : null,
|
||||
title,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user