mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-05 15:22:04 +00:00
* feat(vendors): complete operations roadmap frontend * test(vendors): cover work order assignment flow * fix(vendors): address review feedback * fix(vendors): prevent stale preference saves * fix(vendors): require truthful appointment bounds
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Chip,
|
|
CircularProgress,
|
|
Paper,
|
|
Stack,
|
|
Typography,
|
|
} from "@mui/material";
|
|
import { Link } from "react-router";
|
|
import { vendorOperationsApi } from "@/domain/vendor-operations/api/vendor-operations-api";
|
|
|
|
export default function NotificationsPage() {
|
|
const {
|
|
data = [],
|
|
isLoading,
|
|
error,
|
|
} = useQuery({
|
|
queryKey: ["vendor-operations", "notifications"],
|
|
queryFn: vendorOperationsApi.notifications,
|
|
refetchInterval: 30_000,
|
|
});
|
|
|
|
const groups = data.reduce((result, item) => {
|
|
const items = result.get(item.group) ?? [];
|
|
items.push(item);
|
|
result.set(item.group, items);
|
|
return result;
|
|
}, new Map<string, typeof data>());
|
|
|
|
return (
|
|
<Box className="flex flex-col gap-4 p-4">
|
|
<Box>
|
|
<Typography variant="h5" component="h1">
|
|
Items requiring attention
|
|
</Typography>
|
|
<Typography color="text.secondary">
|
|
Medium-severity vendor reminders refresh automatically. They do not create banners or
|
|
toasts.
|
|
</Typography>
|
|
</Box>
|
|
{isLoading && <CircularProgress aria-label="Loading notifications" />}
|
|
{Boolean(error) && <Alert severity="error">{error?.message}</Alert>}
|
|
{!isLoading && !error && data.length === 0 && (
|
|
<Paper variant="outlined" className="p-6">
|
|
<Typography>No vendor alerts require attention.</Typography>
|
|
</Paper>
|
|
)}
|
|
{[...groups.entries()].map(([group, items]) => (
|
|
<Paper key={group} variant="outlined" className="p-4">
|
|
<Stack direction="row" spacing={1} className="mb-3" sx={{ alignItems: "center" }}>
|
|
<Typography variant="h6">{group}</Typography>
|
|
<Chip label={items.length} size="small" />
|
|
</Stack>
|
|
<Stack spacing={2}>
|
|
{items.map((item) => (
|
|
<Paper key={item.id} variant="outlined" className="p-3">
|
|
<Stack
|
|
direction={{ xs: "column", md: "row" }}
|
|
sx={{ gap: 2, justifyContent: "space-between" }}
|
|
>
|
|
<Box>
|
|
<Stack direction="row" spacing={1} sx={{ alignItems: "center" }}>
|
|
<Chip label={item.severity} color="warning" size="small" />
|
|
<Typography sx={{ fontWeight: 600 }}>{item.title}</Typography>
|
|
</Stack>
|
|
<Typography color="text.secondary" className="mt-1">
|
|
{item.workOrderNumber ??
|
|
item.workOrders?.map((workOrder) => workOrder.workOrderNumber).join(" ↔ ")}
|
|
</Typography>
|
|
{item.scheduledAt != null && (
|
|
<Typography variant="caption">
|
|
Scheduled {new Date(item.scheduledAt).toLocaleString()}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
<Stack direction="row" spacing={1} sx={{ alignItems: "center" }}>
|
|
{item.action != null && (
|
|
<Button component={Link} to={item.action.href} variant="contained">
|
|
{item.action.label}
|
|
</Button>
|
|
)}
|
|
{item.actions?.map((action) => (
|
|
<Button
|
|
key={action.href}
|
|
component={Link}
|
|
to={action.href}
|
|
variant="outlined"
|
|
>
|
|
{action.label}
|
|
</Button>
|
|
))}
|
|
</Stack>
|
|
</Stack>
|
|
</Paper>
|
|
))}
|
|
</Stack>
|
|
</Paper>
|
|
))}
|
|
</Box>
|
|
);
|
|
}
|