Files
trash_system/frontend/src/components/AnnouncementsPopover/index.js
T
2025-10-13 00:23:31 -03:00

330 lines
9.1 KiB
JavaScript

import React, { useEffect, useReducer, useState, useContext } from "react";
import { makeStyles } from "@material-ui/core/styles";
import toastError from "../../errors/toastError";
import Popover from "@material-ui/core/Popover";
import AnnouncementIcon from "@material-ui/icons/Announcement";
import Notifications from "@material-ui/icons/Notifications"
import {
Avatar,
Badge,
IconButton,
List,
ListItem,
ListItemAvatar,
ListItemText,
Dialog,
Paper,
Typography,
DialogTitle,
DialogContent,
DialogActions,
Button,
DialogContentText,
} from "@material-ui/core";
import api from "../../services/api";
import { isArray } from "lodash";
import moment from "moment";
import { SocketContext } from "../../context/Socket/SocketContext";
const useStyles = makeStyles((theme) => ({
mainPaper: {
flex: 1,
maxHeight: 3000,
maxWidth: 5000,
padding: theme.spacing(1),
overflowY: "scroll",
...theme.scrollbarStyles,
},
}));
function AnnouncementDialog({ announcement, open, handleClose }) {
const getMediaPath = (filename) => {
return `${process.env.REACT_APP_BACKEND_URL}/public/${filename}`;
};
return (
<Dialog
open={open}
onClose={() => handleClose()}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{announcement.title}</DialogTitle>
<DialogContent>
{announcement.mediaPath && (
<div
style={{
border: "1px solid #f1f1f1",
margin: "0 auto 20px",
textAlign: "center",
width: "400px",
height: 300,
backgroundImage: `url(${getMediaPath(announcement.mediaPath)})`,
backgroundRepeat: "no-repeat",
backgroundSize: "contain",
backgroundPosition: "center",
}}
></div>
)}
<DialogContentText id="alert-dialog-description">
{announcement.text}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => handleClose()} color="primary" autoFocus>
Fechar
</Button>
</DialogActions>
</Dialog>
);
}
const reducer = (state, action) => {
if (action.type === "LOAD_ANNOUNCEMENTS") {
const announcements = action.payload;
const newAnnouncements = [];
if (isArray(announcements)) {
announcements.forEach((announcement) => {
const announcementIndex = state.findIndex(
(u) => u.id === announcement.id
);
if (announcementIndex !== -1) {
state[announcementIndex] = announcement;
} else {
newAnnouncements.push(announcement);
}
});
}
return [...state, ...newAnnouncements];
}
if (action.type === "UPDATE_ANNOUNCEMENTS") {
const announcement = action.payload;
const announcementIndex = state.findIndex((u) => u.id === announcement.id);
if (announcementIndex !== -1) {
state[announcementIndex] = announcement;
return [...state];
} else {
return [announcement, ...state];
}
}
if (action.type === "DELETE_ANNOUNCEMENT") {
const announcementId = action.payload;
const announcementIndex = state.findIndex((u) => u.id === announcementId);
if (announcementIndex !== -1) {
state.splice(announcementIndex, 1);
}
return [...state];
}
if (action.type === "RESET") {
return [];
}
};
export default function AnnouncementsPopover() {
const classes = useStyles();
const [loading, setLoading] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
const [hasMore, setHasMore] = useState(false);
const [searchParam] = useState("");
const [announcements, dispatch] = useReducer(reducer, []);
const [invisible, setInvisible] = useState(false);
const [announcement, setAnnouncement] = useState({});
const [showAnnouncementDialog, setShowAnnouncementDialog] = useState(false);
const socketManager = useContext(SocketContext);
useEffect(() => {
dispatch({ type: "RESET" });
setPageNumber(1);
}, [searchParam]);
useEffect(() => {
setLoading(true);
const delayDebounceFn = setTimeout(() => {
fetchAnnouncements();
}, 500);
return () => clearTimeout(delayDebounceFn);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParam, pageNumber]);
useEffect(() => {
const companyId = localStorage.getItem("companyId");
const socket = socketManager.getSocket(companyId);
if (!socket) {
return () => {};
}
socket.on(`company-announcement`, (data) => {
if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_ANNOUNCEMENTS", payload: data.record });
setInvisible(false);
}
if (data.action === "delete") {
dispatch({ type: "DELETE_ANNOUNCEMENT", payload: +data.id });
}
});
return () => {
socket.disconnect();
};
}, [socketManager]);
const fetchAnnouncements = async () => {
try {
const { data } = await api.get("/announcements/", {
params: { searchParam, pageNumber },
});
dispatch({ type: "LOAD_ANNOUNCEMENTS", payload: data.records });
setHasMore(data.hasMore);
setLoading(false);
} catch (err) {
toastError(err);
}
};
const loadMore = () => {
setPageNumber((prevState) => prevState + 1);
};
const handleScroll = (e) => {
if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
if (scrollHeight - (scrollTop + 100) < clientHeight) {
loadMore();
}
};
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
setInvisible(true);
};
const handleClose = () => {
setAnchorEl(null);
};
const borderPriority = (priority) => {
if (priority === 1) {
return "4px solid #b81111";
}
if (priority === 2) {
return "4px solid orange";
}
if (priority === 3) {
return "4px solid grey";
}
};
const getMediaPath = (filename) => {
return `${process.env.REACT_APP_BACKEND_URL}/public/${filename}`;
};
const handleShowAnnouncementDialog = (record) => {
setAnnouncement(record);
setShowAnnouncementDialog(true);
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
return (
<div>
<AnnouncementDialog
announcement={announcement}
open={showAnnouncementDialog}
handleClose={() => setShowAnnouncementDialog(false)}
/>
<IconButton
variant="contained"
aria-describedby={id}
onClick={handleClick}
style={{ color: "white" }}
>
<Badge
color="secondary"
variant="dot"
invisible={invisible || announcements.length < 1}
>
<Notifications />
</Badge>
</IconButton>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
<Paper
variant="outlined"
onScroll={handleScroll}
className={classes.mainPaper}
>
<List
component="nav"
aria-label="main mailbox folders"
style={{ minWidth: 300 }}
>
{isArray(announcements) &&
announcements.map((item, key) => (
<ListItem
key={key}
style={{
//background: key % 2 === 0 ? "#ededed" : "white",
border: "1px solid #eee",
borderLeft: borderPriority(item.priority),
cursor: "pointer",
}}
onClick={() => handleShowAnnouncementDialog(item)}
>
{item.mediaPath && (
<ListItemAvatar>
<Avatar
alt={item.mediaName}
src={getMediaPath(item.mediaPath)}
/>
</ListItemAvatar>
)}
<ListItemText
primary={item.title}
secondary={
<>
<Typography component="span" style={{ fontSize: 12 }}>
{moment(item.createdAt).format("DD/MM/YYYY")}
</Typography>
<span style={{ marginTop: 5, display: "block" }}></span>
<Typography component="span" variant="body2">
{item.text}
</Typography>
</>
}
/>
</ListItem>
))}
{isArray(announcements) && announcements.length === 0 && (
<ListItemText primary="Nenhum registro" />
)}
</List>
</Paper>
</Popover>
</div>
);
}