Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/helpers/HelperFunctions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function formatShort(dateObj) {
if (!dateObj) return 'N/A';

const { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND } = dateObj;

const date = new Date(YEAR);

date.setMonth(MONTH - 1); // Months are 0-indexed
date.setDate(DAY);
date.setHours(HOUR);
date.setMinutes(MINUTE);
date.setSeconds(SECOND);

return date.toLocaleDateString('en-GB');
}

export async function batchRequests(items, batchSize, fn, onProgress) {
const results = [];

for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);

const batchResults = await Promise.all(
batch.map(async (item) => {
const result = await fn(item);
onProgress(); // increment progress
return result;
})
);

results.push(...batchResults);
}

return results;
}

30 changes: 30 additions & 0 deletions src/tools/WSBulletins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import WS from './WS.js';

class WSBulletins {

getUserBulletins(config = {}) {
return WS._post('/proxy/userbulletins', config);
}

acknowledgeBulletin(bulletinCode, languageCode) {
return WS._post(
'/proxy/userbulletinacknowledgement',
{
"UserBulletin": {
"BULLETINCODE": bulletinCode,
"LANGUAGECODE": languageCode
}
}
);
}

getBulletinHistory(config = {}) {
return WS._get('/proxy/bulletinhistory', config);
}

getBulletinHistoryById(id, languageCode, config = {}) {
return WS._get(`/proxy/bulletinhistory/${id}%23${languageCode}`);
}
}

export default new WSBulletins();
18 changes: 17 additions & 1 deletion src/ui/layout/ApplicationLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useApplicationDataStore from "../../state/useApplicationDataStore";
import useUserDataStore from "../../state/useUserDataStore";
import useEquipmentTreeStore from "../../state/useEquipmentTreeStore";
import useScannedUserStore from "../../state/useScannedUserStore";
import {BulletinToggle} from "@/ui/layout/BulletinToggle.jsx";

const styles = {
topBarLink: {
Expand All @@ -32,6 +33,17 @@ const styles = {
topBarSpan: {
fontSize: "12px",
},
notifAndInfoButtons: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
flexGrow: 1,
},
separationLine: {
borderLeft: "1px solid rgba(255, 255, 255, 0.8)",
width: 1,
height: 22,
}
};

export default withStyles(styles)(function ApplicationLayout(props) {
Expand Down Expand Up @@ -122,7 +134,11 @@ export default withStyles(styles)(function ApplicationLayout(props) {
</div>
)}

<UserInfo />
<div className={clsx(classes.notifAndInfoButtons)}>
<BulletinToggle />
<span className={clsx(classes.separationLine)}/>
<UserInfo />
</div>
</div>
</div>
);
Expand Down
194 changes: 194 additions & 0 deletions src/ui/layout/BulletinToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import {Bell } from "mdi-material-ui";
import {Badge, Drawer, IconButton, Tooltip} from "@mui/material";
import {useEffect, useState} from "react";
import {MyBulletins} from "@/ui/pages/bulletins/BulletinWindow/MyBulletins.jsx";
import WSBulletins from "@/tools/WSBulletins.js";

import { keyframes } from "@mui/system";

const smoothScalePulse = keyframes`
0% {
transform: scale(1);
}
50% {
transform: scale(1.13);
}
100% {
transform: scale(1);
}
`;

export const BulletinToggle = () => {

const bellIcon = {
fontSize: 20,
margin: 5,
cursor: "pointer",
color: 'rgba(255, 255, 255, 0.8)'
};

const [hasUnreadBulletins, setHasUnreadBulletins] = useState(false);
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [loading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [unreadBulletins, setUnreadBulletins] = useState([]);
const [hasUnreadCriticalBulletins, setHasUnreadCriticalBulletins] = useState(false);
const [tooltipOpen, setTooltipOpen] = useState(false);

const closeDrawer = () => {
setIsDrawerOpen(false);
}

const openDrawer = () => {
setIsDrawerOpen(true);
}

const checkForCriticalBulletins = (bulletins) => {
if (bulletins.some(bulletin => bulletin.CRITICAL === "true")) {
setHasUnreadCriticalBulletins(true);
}
}

const fetchBulletins = async () => {
try {
setIsLoading(true);
const response = await WSBulletins.getUserBulletins();
const bulletins = response.body.Result.ResultData.UserBulletins.Bulletin || [];
if (bulletins.length > 0) {
setHasUnreadBulletins(true);
setUnreadBulletins(bulletins);
checkForCriticalBulletins(bulletins);
}
} catch (err) {
setError(err);
console.error('Failed to fetch notifications: ', err)
} finally {
setIsLoading(false);
}
}

const handleBulletinAcknowledged = (bulletinCode) => {
setUnreadBulletins(prev => prev.filter(b => b.BULLETINCODE !== bulletinCode));

const remainingBulletins = unreadBulletins.filter(b => b.BULLETINCODE !== bulletinCode);
setHasUnreadBulletins(remainingBulletins.length > 0);

const hasCritical = remainingBulletins.some(bulletin => bulletin.CRITICAL === "true");
setHasUnreadCriticalBulletins(hasCritical);
}


useEffect(() => {
fetchBulletins();
}, []);

useEffect(() => {
if (isDrawerOpen) {
setTooltipOpen(false)
} else if (hasUnreadCriticalBulletins) {
setTooltipOpen(true)
}
}, [hasUnreadCriticalBulletins, isDrawerOpen]);

const tooltipMessage = hasUnreadCriticalBulletins
? "You have critical unread notifications!"
: "You have unread notifications!";

return (
<>
{ !isDrawerOpen && (
<Tooltip
title={hasUnreadBulletins ? tooltipMessage : "Show notifications"}
arrow
open={tooltipOpen}
onOpen={() => {
if (!hasUnreadCriticalBulletins && !isDrawerOpen) {
setTooltipOpen(true);
}
}}
onClose={() => {
if (!hasUnreadCriticalBulletins) {
setTooltipOpen(false);
}
}}
PopperProps={{
sx: hasUnreadBulletins ? {
'& .MuiTooltip-tooltip': {
backgroundColor: hasUnreadCriticalBulletins ? 'error.main' : 'text.secondary',
color: 'white',
fontSize: '0.875rem',
fontWeight: 500,
},
'& .MuiTooltip-arrow': {
color: hasUnreadCriticalBulletins ? 'error.main' : 'text.secondary',
}
} : {}
}}
>
<IconButton
size="small"
onClick={isDrawerOpen ? closeDrawer : openDrawer}
>
{hasUnreadBulletins ? (
<Badge
badgeContent={unreadBulletins.length}
color="error"
sx={{
"& .MuiBadge-badge": {
minWidth: 8,
height: 8,
padding: 1,
position: "relative",
top: 8,
right: 24,
},
...(hasUnreadCriticalBulletins &&
!isDrawerOpen && {
animation: `${smoothScalePulse} 1.8s cubic-bezier(0.4, 0, 0.2, 1) infinite`,
}),
}}
>
<Bell style={bellIcon} />
</Badge>
) : (
<Bell style={bellIcon} />
)}
</IconButton>
</Tooltip>
)}

<Drawer
anchor="right"
open={isDrawerOpen}
onClose={closeDrawer}
SlideProps={{
style: {
zIndex: 1260
}
}}
PaperProps={{
sx: {
width: 800,
},
}}
ModalProps={{
keepMounted: false,
sx: {
"& .MuiBackdrop-root": {
backgroundColor: "rgba(0, 0, 0, 0.3)",
}
}
}}
>
<MyBulletins
closeDrawer={closeDrawer}
bulletins={unreadBulletins}
loading={loading}
error={error}
onAcknowledge={handleBulletinAcknowledged}
activeTabOnOpen={ hasUnreadBulletins ? 0 : 1}
/>
</Drawer>
</>
)
}
2 changes: 0 additions & 2 deletions src/ui/layout/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import { isCernMode } from "@/ui/components/CERNMode";
import { version } from "../../../package.json";
Expand All @@ -17,7 +16,6 @@ const Footer = (props) => {
display: "flex",
justifyContent: "end",
alignItems: "center",
zIndex: 1250, // snackbar has 1400
};

return (
Expand Down
2 changes: 0 additions & 2 deletions src/ui/layout/UserInfo.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from "react";
import { Account, Logout } from "mdi-material-ui";
import { IconButton } from "@mui/material";
import { logout } from "../../AuthWrapper";
Expand All @@ -13,7 +12,6 @@ const UserInfo = () => {

const userInfoStyle = {
color: "rgba(255, 255, 255, 0.8)",
flexGrow: 1,
height: 48,
display: "flex",
alignItems: "center",
Expand Down
Loading