Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • andersharrisson/ce-deploy-ui
  • ccce/dev/ce-deploy-ui
2 results
Show changes
Commits on Source (8)
Showing
with 283 additions and 734 deletions
SERVER_ADDRESS=''
ENVIRONMENT_TITLE='LOCAL'
API_BASE_ENDPOINT='/api/spec'
SWAGGER_UI_URL='/api/docs'
TOKEN_RENEW_INTERVAL=180000
NAMING_ADDRESS='https://naming.esss.lu.se'
ENVIRONMENT_TITLE = ''
BACKEND_VERSION='1.0.0-rc9'
BACKEND_REPOSITORY_TAGS='https://gitlab.esss.lu.se/ccce/dev/ce-deploy-backend/-/tags/'
FRONTEND_VERSION='1.0.0-rc6'
FRONTEND_VERSION='0.0.0-local'
FRONTEND_REPOSITORY_TAGS='https://gitlab.esss.lu.se/ccce/dev/ce-deploy-ui/-/tags/'
SUPPORT_URL='https://jira.esss.lu.se/plugins/servlet/desk/portal/44?requestGroup=137'
\ No newline at end of file
import React, { useEffect } from "react";
import React, { useContext, useEffect } from "react";
import "./App.css";
import { Navigate, Route, Routes, BrowserRouter } from "react-router-dom";
import { StyledEngineProvider } from "@mui/material";
......@@ -7,10 +7,11 @@ import { CssBaseline } from "@mui/material";
import { theme } from "./style/Theme";
import { IOCListView } from "./views/IOC/IOCListView";
import { HomeAccessControl } from "./views/home/HomeAccessControl";
import { GlobalAppBar } from "./components/navigation/GlobalAppBar/GlobalAppBar";
import NavigationMenu from "./components/navigation/NavigationMenu";
import { IOCDetailsAccessControl } from "./views/IOC/IOCDetailsAccessControl";
import { JobDetailsAccessControl } from "./views/jobs/JobDetailsAccessControl";
import { APIProvider, UserProvider } from "./api/SwaggerApi";
import { APIProvider } from "./api/SwaggerApi";
import { UserProvider } from "./api/UserProvider";
import { HostListView } from "./views/host/HostListView";
import { HostDetailsAccessControl } from "./views/host/HostDetailsAccessControl";
import { StatisticsView } from "./views/statistics/StatisticsView";
......@@ -19,18 +20,19 @@ import { SnackbarProvider } from "notistack";
import TokenRenew from "./components/auth/TokenRenew";
import { NotificationProvider } from "./components/common/notification/Notifications";
import NotFound from "./components/navigation/NotFound";
import { applicationSubTitle } from "./components/common/Helper";
import { AppErrorBoundary } from "./components/navigation/ErrorBoundary/ErrorBoundary";
import { LoginView } from "./views/login/LoginView";
import { JobLogAccessControl } from "./views/jobs/JobLogAccessControl";
import { RecordListView } from "./views/records/RecordListView";
import { RecordDetailsView } from "./views/records/RecordDetailsView";
import { GlobalAppBarContext } from "@ess-ics/ce-ui-common";
import { applicationTitle } from "./components/common/Helper";
import { UserPageView } from "./views/UserPage/UserPageView";
// setting up the application (TAB)title
function App() {
useEffect(() => {
document.title = "CE deploy & monitor" + applicationSubTitle();
}, []);
const { setTitle } = useContext(GlobalAppBarContext);
useEffect(() => setTitle(applicationTitle()), [setTitle]);
return (
<AppErrorBoundary>
......@@ -46,7 +48,7 @@ function App() {
<UserProvider>
<TokenRenew />
<NotificationProvider>
<GlobalAppBar>
<NavigationMenu>
<Routes>
<Route
path="/"
......@@ -111,13 +113,18 @@ function App() {
element={<LoginView />}
exact
/>
<Route
path="/user/:userName"
element={<UserPageView />}
exact
/>
<Route
path="*"
element={<NotFound />}
exact
/>
</Routes>
</GlobalAppBar>
</NavigationMenu>
</NotificationProvider>
</UserProvider>
</APIProvider>
......
......@@ -9,6 +9,7 @@ import SwaggerClient from "swagger-client";
import { createContext, useContext, useEffect, useState } from "react";
import { CircularProgress } from "@mui/material";
import { useCustomSnackbar } from "../components/common/snackbar/Snackbar";
import { userContext } from "@ess-ics/ce-ui-common";
const createSwaggerClient = async () => {
const swaggerOptions = {
......@@ -38,87 +39,6 @@ export function useAPI() {
}
export const apiContext = createContext(null);
export const userContext = createContext({
user: null,
userRoles: [],
getUserRoles: () => {},
login: () => {},
logout: () => {}
});
export function UserProvider({ children }) {
function onLoginError(message) {
setLoginError(message);
}
const [user, getUser, resetUser, userLoading] = useUser();
const [loginError, setLoginError] = useState();
const [loginResponse, loginFcn /* reset*/, , loginLoading] =
useLogin(onLoginError);
const [logoutFcn, logoutLoading] = useLogout();
const [initialized, setInitialized] = useState(false);
const resetLoginError = useCallback(
() => setLoginError(null),
[setLoginError]
);
useEffect(getUser, [loginResponse, getUser]);
const logout = useCallback(async () => {
resetUser();
logoutFcn();
}, [resetUser, logoutFcn]);
const login = useCallback(
(username, password) => {
loginFcn(username, password);
},
[loginFcn]
);
const loading = Boolean(loginLoading || userLoading || logoutLoading);
const createValue = useCallback(() => {
return {
user: user?.userInfo,
userRoles: user?.userRoles,
getUser,
login,
loginError,
resetLoginError,
logout
};
}, [
user?.userInfo,
user?.userRoles,
getUser,
login,
loginError,
resetLoginError,
logout
]);
const [value, setValue] = useState(createValue());
useEffect(() => {
if (!loading) {
setInitialized(true);
}
}, [loading]);
useEffect(() => {
if (!loading) {
setValue(createValue());
}
}, [loading, setValue, createValue]);
return (
initialized && (
<userContext.Provider value={value}>{children}</userContext.Provider>
)
);
}
export function APIProvider({ children }) {
const [api] = useAPI();
......@@ -643,8 +563,10 @@ export function useUser() {
console.warn("useUser error: ", message);
}, []);
const [userInfo, getUserInfo, resetUserInfo, userInfoLoading] =
useUserInfo(onError);
const [userInfo, getUserInfo, resetUserInfo, userInfoLoading] = useUserInfo(
null,
onError
);
const [userRoles, getUserRoles, resetUserRoles, userRolesLoading] =
useUserRoles(onError);
const [user, setUser] = useState();
......@@ -678,10 +600,13 @@ export function useUser() {
return [user, getUser, resetUser, loading];
}
export function useUserInfo(onError) {
export function useUserInfo(userName, onError) {
const api = useContext(apiContext);
const method = useCallAndUnpack(api.apis.Git.infoFromUserName, unpackUser);
return useAsync({ fcn: method, call: false, onError: onError });
const boundMethod = useCallback(method.bind(null, { user_name: userName }), [
userName
]);
return useAsync({ fcn: boundMethod, call: false, onError: onError });
}
export function unpackUserRoles(roles) {
......
import React from "react";
import { useCallback, useEffect, useState } from "react";
import { useLogin, useLogout, useUser } from "./SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common";
export function UserProvider({ children }) {
function onLoginError(message) {
setLoginError(message);
}
const [user, getUser, resetUser, userLoading] = useUser();
const [loginError, setLoginError] = useState();
const [loginResponse, loginFcn /* reset*/, , loginLoading] =
useLogin(onLoginError);
const [logoutFcn, logoutLoading] = useLogout();
const [initialized, setInitialized] = useState(false);
const resetLoginError = useCallback(
() => setLoginError(null),
[setLoginError]
);
useEffect(getUser, [loginResponse, getUser]);
const logout = useCallback(async () => {
resetUser();
logoutFcn();
}, [resetUser, logoutFcn]);
const login = useCallback(
(username, password) => {
loginFcn(username, password);
},
[loginFcn]
);
const loading = Boolean(loginLoading || userLoading || logoutLoading);
const createValue = useCallback(() => {
return {
user: user?.userInfo,
userRoles: user?.userRoles,
login,
loginError,
resetLoginError,
logout
};
}, [
user?.userInfo,
user?.userRoles,
login,
loginError,
resetLoginError,
logout
]);
const [value, setValue] = useState(createValue());
useEffect(() => {
if (!loading) {
setInitialized(true);
}
}, [loading]);
useEffect(() => {
if (!loading) {
setValue(createValue());
}
}, [loading, setValue, createValue]);
return (
initialized && (
<userContext.Provider value={value}>{children}</userContext.Provider>
)
);
}
import React, { useContext } from "react";
import { APIProvider, userContext, UserProvider } from "./SwaggerApi";
import { APIProvider } from "./SwaggerApi";
import { UserProvider } from "./UserProvider";
import { userContext } from "@ess-ics/ce-ui-common";
import { SnackbarProvider } from "notistack";
function AppHarness({ children }) {
......
......@@ -7,9 +7,9 @@ import { SimpleModal } from "../../common/SimpleModal/SimpleModal";
import {
useUpdateAndDeployIoc,
useCreateUndeployment,
useJobById,
userContext
useJobById
} from "../../../api/SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common";
import AlertMessages from "../AlertMessages";
import { SimpleAccordion } from "../../common/Accordion/SimpleAccordion";
import AccessControl from "../../auth/AccessControl";
......
import React, { useContext } from "react";
import { userContext } from "../../../api/SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common";
import AccessDenied from "../AccessDenied";
const checkPermissions = (userRoles, allowedRoles) => {
......
import { useContext, useCallback } from "react";
import { useRenewToken, userContext } from "../../../api/SwaggerApi";
import { useRenewToken } from "../../../api/SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common";
import { useSafePolling } from "../../../hooks/Polling";
export default function TokenRenew() {
......
import { SnackbarProvider } from "notistack";
import React from "react";
import { APIProvider, UserProvider } from "../../../api/SwaggerApi";
import { APIProvider } from "../../../api/SwaggerApi";
import { UserProvider } from "../../../api/UserProvider";
import TokenRenew from ".";
function AppHarness({ children }) {
......
......@@ -81,7 +81,7 @@ export function circularPalette(palette, index, opacity = 1.0) {
return alpha(colors[((index % n) + n) % n], opacity);
}
export function applicationSubTitle() {
function applicationSubTitle() {
const title = `${window.ENVIRONMENT_TITLE}`;
if (title && title !== "undefined") {
......@@ -91,6 +91,12 @@ export function applicationSubTitle() {
return "";
}
export function applicationTitle(...breadcrumbs) {
return [`CE deploy & monitor ${applicationSubTitle()}`, ...breadcrumbs].join(
" / "
);
}
export function initRequestParams(lazyParams, filter, columnSort) {
let requestParams = {
page: lazyParams.page,
......
import React from "react";
import { Card, CardHeader } from "@mui/material";
import { useState, useEffect } from "react";
import { useIOCSearch } from "../../../api/SwaggerApi";
import { IOCAsyncList } from "../../IOC/IOCAsyncList";
import { initRequestParams } from "../Helper";
export function UserIocList({ userName }) {
const [iocs, getIocs, , loading] = useIOCSearch();
const [iocList, setIocList] = useState([]);
const rowsPerPage = [20, 50];
const [lazyParams, setLazyParams] = useState({
first: 0,
rows: 20,
page: 0
});
useEffect(() => {
setIocList(iocs.iocList);
}, [iocs, setIocList]);
useEffect(() => {
let requestParams = initRequestParams(lazyParams);
requestParams.owner = userName;
getIocs(requestParams);
}, [getIocs, lazyParams, userName]);
return (
<Card>
<CardHeader
title={`IOCs registered by ${userName}`}
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
<IOCAsyncList
iocs={iocList}
setIocs={setIocList}
loading={loading}
rowType="explore"
lazyParams={lazyParams}
setLazyParams={setLazyParams}
totalCount={iocs.totalCount}
rowsPerPage={rowsPerPage}
/>
</Card>
);
}
import React from "react";
import { Card, CardHeader } from "@mui/material";
import { initRequestParams } from "../Helper";
import { useEffect, useState } from "react";
import { useOperationsSearch } from "../../../api/SwaggerApi";
import { JobAsyncList } from "../../Job/JobAsyncList";
export function UserOperationList({ userName }) {
const [operations, getOperations, , loading] = useOperationsSearch();
const [lazyParams, setLazyParams] = useState({
first: 0,
rows: 20,
page: 0
});
const rowsPerPage = [20, 50];
const [jobList, setJobList] = useState([]);
useEffect(() => {
let requestParams = initRequestParams(lazyParams);
requestParams.user = userName;
getOperations(requestParams);
}, [getOperations, lazyParams, userName]);
useEffect(() => {
setJobList(operations.operationsList);
}, [operations, setJobList]);
return (
<Card>
<CardHeader
title={`Operations by ${userName}`}
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
<JobAsyncList
jobs={jobList}
setJobs={setJobList}
loading={loading}
totalCount={operations.totalCount}
lazyParams={lazyParams}
setLazyParams={setLazyParams}
rowsPerPage={rowsPerPage}
/>
</Card>
);
}
import React from "react";
import { Avatar, Card, CardHeader, Typography } from "@mui/material";
import { KeyValueTable } from "@ess-ics/ce-ui-common/dist/components/common/KeyValueTable";
export function UserProfile({ userInfo, errorMessage }) {
const userData = {
username: userInfo?.loginName,
avatar: (
<Avatar
src={userInfo?.avatar}
variant="rounded"
/>
)
};
return (
<Card>
<CardHeader
title="User profile page"
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
{errorMessage ? (
<Typography>{errorMessage}</Typography>
) : (
<KeyValueTable
obj={userData}
variant="overline"
/>
)}
</Card>
);
}
import { UserIocList } from "./UserIOCList";
import { UserOperationList } from "./UserOperationList";
import { UserProfile } from "./UserProfile";
export { UserIocList, UserOperationList, UserProfile };
import { SnackbarProvider } from "notistack";
import React, { useContext } from "react";
import {
APIProvider,
userContext,
UserProvider
} from "../../../api/SwaggerApi";
import { APIProvider } from "../../../api/SwaggerApi";
import { UserProvider } from "../../../api/UserProvider";
import { userContext } from "@ess-ics/ce-ui-common";
import { useEffectOnMount } from "../../../hooks/MountEffects";
import { notificationContext, NotificationProvider } from "./Notifications";
......
......@@ -11,7 +11,7 @@ import {
export function HostStatusIcon({ host }) {
const theme = useTheme();
let { status, alert, alerts } = host;
let { status, alertSeverity, alerts } = host;
const iconConfig = {
available: {
......@@ -39,28 +39,31 @@ export function HostStatusIcon({ host }) {
let state = null;
status = status?.toLowerCase();
if (typeof alerts === "object") alert = alerts?.[0]?.type;
if (typeof alerts === "object") alertSeverity = alerts?.[0]?.type;
alert = alert?.toLowerCase();
alertSeverity = alertSeverity?.toLowerCase();
if (status === "available" && (alert === undefined || alert === "info")) {
if (
status === "available" &&
(alertSeverity === undefined || alertSeverity === "info")
) {
// Available status / no error or warning
state = "available";
} else if (
status === "available" &&
(alert === "warning" || alert === "error")
(alertSeverity === "warning" || alertSeverity === "error")
) {
// Available status / with error or warning
state = "alert";
} else if (
status === "unreachable" &&
(alert === undefined || alert === "info")
(alertSeverity === undefined || alertSeverity === "info")
) {
// Unreachable status / no error or warning
state = "inactive";
} else if (
status === "unreachable" &&
(alert === "warning" || alert === "error")
(alertSeverity === "warning" || alertSeverity === "error")
) {
// Unreachable status / with error or warning
state = "inactive alert";
......
......@@ -43,8 +43,8 @@ export function createRow(hostContainer) {
host: noWrapText(host.name),
description: rowDescription(host.description),
network: noWrapText(host.network),
discrepancy: hostContainer.alert === "WARNING",
inconsistentState: hostContainer.alert === "ERROR",
discrepancy: hostContainer.alertSeverity === "WARNING",
inconsistentState: hostContainer.alertSeverity === "ERROR",
shortenLongData: true,
iocNotDeployed: !hostContainer.iocDeployed
};
......
import React, {
createContext,
useCallback,
useContext,
useEffect,
useRef,
useState
} from "react";
import {
AppBar,
Avatar,
Button,
Chip,
IconButton,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Typography,
Menu,
MenuItem,
Tooltip,
Badge,
Popover,
Grid,
List,
Box,
Divider,
useTheme,
ListItemButton,
styled
} from "@mui/material";
import {
LockOpen,
Home,
Assignment,
Storage,
TrendingUp,
SettingsInputComponent
} from "@mui/icons-material";
import HelpIcon from "@mui/icons-material/Help";
import { DeploymentStatusIcon } from "../../deployments/DeploymentIcons";
import DeleteIcon from "@mui/icons-material/Delete";
import NotificationsIcon from "@mui/icons-material/Notifications";
import { MenuDrawer } from "../Menu/MenuDrawer";
import { Link } from "react-router-dom";
import { userContext } from "../../../api/SwaggerApi";
import { notificationContext } from "../../common/notification/Notifications";
import { useWindowDimensions } from "../../common/Helper";
import { CCCEControlSymbol } from "../../../icons/CCCEControlSymbol";
import { applicationSubTitle } from "../../common/Helper";
import { LoginDialog } from "../../auth/Login";
import { useRedirect } from "../../../hooks/Redirect";
const DivRoot = styled("div")(({ theme }) => ({
flexGrow: 1,
paddingBottom: theme.spacing(2)
}));
const StyledAppBar = styled(AppBar)(({ theme }) => ({
// fixme: a hack to make the appBar stay on top of the drawer, should be between 1201 - 1299
zIndex: 1250
}));
const IconButtonMenuButton = styled(IconButton)(({ theme }) => ({
marginRight: theme.spacing(2)
}));
const TypographyTitle = styled(Typography)(({ theme }) => ({
flexGrow: 1
}));
const DividerHelpDivider = styled(Divider)(({ theme }) => ({
marginTop: theme.spacing(5)
}));
const ListItemIconMenuIcon = styled(ListItemIcon)(({ theme }) => ({
marginLeft: theme.spacing(1)
}));
const ListNotificationList = styled(List)(({ theme }) => ({
overflow: "auto",
maxHeight: 360
}));
const IconButtonNotificationButton = styled(IconButton)(({ theme }) => ({
marginRight: theme.spacing(2)
}));
const DivContent = styled("div")(({ theme }) => ({
marginTop: theme.spacing(1)
}));
const ShiftContentLeft = styled("div")(({ theme }) => ({
marginLeft: theme.drawer.widthClose
}));
const ShiftContentRight = styled("div")(({ theme }) => ({
marginLeft: theme.drawer.widthOpen
}));
const InternalStyledMenu = styled(Menu)({
"& .MuiPaper-root": {
border: "1px solid #d3d4d5"
}
});
const StyledMenu = (props) => (
<>
<div style={{ ...props.theme.mixins.toolbar }} />
<InternalStyledMenu
elevation={0}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
{...props}
/>
</>
);
function InfoMenuItem({ children }) {
return (
<MenuItem
disabled
style={{ opacity: 1.0 }}
>
{children}
</MenuItem>
);
}
function RBACRoleInfo({ role }) {
return (
<InfoMenuItem key={role}>
<Chip
label={role}
color="secondary"
/>
</InfoMenuItem>
);
}
export function LoginControls() {
const [loginFormOpen, setLoginFormOpen] = useState(false);
const { user, login, loginError, resetLoginError } = useContext(userContext);
const userRef = useRef(user);
const redirect = useRedirect();
const handleClose = useCallback(() => {
setLoginFormOpen(false);
}, []);
// detect logout and redirect to login
useEffect(() => {
if (userRef.current && !user) {
redirect("/login", {}, true);
}
userRef.current = user;
}, [redirect, user]);
useEffect(() => {
if (user) {
handleClose();
}
}, [handleClose, user]);
return user ? (
<>
<NotificationMenu />
<ProfileMenu />
</>
) : (
<>
<Button
variant="outlined"
color="inherit"
onClick={() => {
resetLoginError();
setLoginFormOpen(true);
}}
>
Login
</Button>
<LoginDialog
login={login}
open={loginFormOpen}
handleClose={handleClose}
error={loginError}
resetError={resetLoginError}
/>
</>
);
}
export function ButtonAppBar({ homeUrl, homeClick, title, button }) {
return (
<DivRoot>
<StyledAppBar position="fixed">
<Toolbar>
<IconButtonMenuButton
edge="start"
component={Link}
to={homeUrl}
onClick={homeClick}
color="inherit"
aria-label="menu"
size="large"
>
<Home />
</IconButtonMenuButton>
<TypographyTitle
variant="h1"
noWrap
>
{title}
</TypographyTitle>
{button}
</Toolbar>
</StyledAppBar>
</DivRoot>
);
}
const defaultTitle = "CE deploy & monitor";
export const GlobalAppBarContext = createContext({
title: defaultTitle,
setButton: () => {
console.info("default GlobalAppBarContext.setButton does nothing.");
},
setTitle: () => {
console.info("default GlobalAppBarContext.setTitle does nothing.");
}
});
function MenuListItem({ url, icon, text, tooltip }) {
const currentUrl = `${window.location}`;
return (
<>
{url.includes("help") ? <DividerHelpDivider /> : <></>}
<Tooltip
title={tooltip ? text : ""}
placement="right"
arrow
>
<ListItemButton
component={Link}
to={url}
selected={currentUrl.split("?")[0].endsWith(url)}
>
<ListItemIconMenuIcon>{icon}</ListItemIconMenuIcon>
<ListItemText
primary={text}
primaryTypographyProps={{ variant: "h5" }}
/>
</ListItemButton>
</Tooltip>
</>
);
}
function MenuListItems({ menuItems, drawerOpen }) {
return (
<>
{menuItems.map(({ text, url, icon }) => (
<MenuListItem
key={text}
tooltip={!drawerOpen}
url={url}
icon={icon}
text={text}
/>
))}
</>
);
}
export function UserInfo({ user }) {
return (
<ListItem>
<ListItemIcon>
<Avatar
src={user.avatar}
variant="rounded"
/>
</ListItemIcon>
<ListItemText
primary={user.fullName}
secondary={user.email}
/>
</ListItem>
);
}
export function ProfileMenu() {
const theme = useTheme();
const { user, userRoles, logout } = useContext(userContext);
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
<Avatar
src={user.avatar}
variant="circular"
onClick={handleClick}
aria-controls="profile-menu"
aria-haspopup="true"
/>
<StyledMenu
id="profile-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
theme={theme}
>
{userRoles
.filter((role) => role.includes("DeploymentTool"))
.map((role) => (
<RBACRoleInfo
role={role}
key={role}
/>
))}
<MenuItem
onClick={() => {
handleClose();
logout();
}}
>
<ListItemIcon>
<LockOpen fontSize="small" />
</ListItemIcon>
<ListItemText
primary="Logout"
primaryTypographyProps={{ variant: "h5" }}
/>
</MenuItem>
</StyledMenu>
</>
);
}
const StyledListItemButton = styled(ListItemButton)({
"& .MuiListItemButton-root": {
margin: 0,
paddingLeft: 10
}
});
const StyledListItemIcon = styled(ListItemIcon)({
"& .MuiListItemIcon-root": {
minWidth: 0,
marginRight: 12
}
});
export function NotificationMenu() {
const [anchorEl, setAnchorEl] = useState(null);
const { user } = useContext(userContext);
const { notifications, clearNotifications } = useContext(notificationContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
function haveNotification() {
return (
<>
<Grid
item
xs={12}
>
<ListNotificationList>
{notifications.map((notification) => {
return (
<>
{notification.link ? (
<StyledListItemButton
component={Link}
to={notification.link}
onClick={() => {
handleClose();
}}
>
<StyledListItemIcon>
<DeploymentStatusIcon status={notification.status} />
</StyledListItemIcon>
<Typography>{notification.message}</Typography>
</StyledListItemButton>
) : (
<StyledListItemButton key={notification.message}>
<StyledListItemIcon>
<DeploymentStatusIcon status={notification.status} />
</StyledListItemIcon>
<Typography>{notification.message}</Typography>
</StyledListItemButton>
)}
</>
);
})}
</ListNotificationList>
</Grid>
<Grid
item
xs={1}
>
<Box
display="flex"
justifyContent="flex-end"
>
<IconButton
color="inherit"
onClick={() => {
clearNotifications();
handleClose();
}}
size="large"
>
<Tooltip
title="Clear all notifications"
arrow
>
<DeleteIcon fontSize="small" />
</Tooltip>
</IconButton>
</Box>
</Grid>
</>
);
}
function noNotification() {
return (
<Typography
inset="true"
style={{ margin: "4px" }}
>
You have no notifications!
</Typography>
);
}
return (
<>
{user ? (
<>
<IconButtonNotificationButton
color="inherit"
onClick={handleClick}
size="large"
>
<Badge
badgeContent={notifications.length}
color="secondary"
>
<NotificationsIcon />
</Badge>
</IconButtonNotificationButton>
<Popover
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
>
<Grid
container
spacing={0}
justifyContent="flex-end"
>
{notifications.length > 0 ? haveNotification() : noNotification()}
</Grid>
</Popover>
</>
) : (
<></>
)}
</>
);
}
export const DRAWER_OPEN = "DRAWER_OPEN";
const defaultButton = <LoginControls />;
export function GlobalAppBar({ children }) {
const theme = useTheme();
const [button, setButton] = useState(defaultButton);
const [title, setTitle] = useState(defaultTitle);
const [value] = useState({ setButton, setTitle });
const { width } = useWindowDimensions();
const [drawerOpen, setDrawerOpen] = useState(isDrawerOpen() ?? width > 1730);
useEffect(() => {
if (isDrawerOpen()) {
setDrawerOpen(width > 1730);
}
}, [width, setDrawerOpen]);
const toggleDrawer = () => {
storeDrawerOpen(!drawerOpen);
setDrawerOpen(!drawerOpen);
};
function isDrawerOpen() {
return JSON.parse(localStorage.getItem(DRAWER_OPEN));
}
function storeDrawerOpen(open) {
localStorage.setItem(DRAWER_OPEN, JSON.stringify(open));
}
const makeLink = (text, url, icon) => ({ text, url, icon });
const menuItemsAll = [
makeLink("Records", "/records", <SettingsInputComponent />),
makeLink("IOCs", "/iocs", <CCCEControlSymbol />),
makeLink("IOC hosts", "/hosts", <Storage />),
makeLink("Log", "/jobs", <Assignment />),
makeLink("Statistics", "/statistics", <TrendingUp />),
makeLink("Help", "/help", <HelpIcon />)
];
return (
<>
<ButtonAppBar
homeUrl="/home"
title={title}
button={button}
/>
<div>
<MenuDrawer
open={drawerOpen}
toggleDrawer={toggleDrawer}
>
<MenuListItems
menuItems={menuItemsAll}
drawerOpen={drawerOpen}
/>
</MenuDrawer>
</div>
<div>
<GlobalAppBarContext.Provider value={value}>
<div style={{ ...theme.mixins.toolbar }} />
<DivContent>
{drawerOpen ? (
<ShiftContentRight>{children}</ShiftContentRight>
) : (
<ShiftContentLeft>{children}</ShiftContentLeft>
)}
</DivContent>
</GlobalAppBarContext.Provider>
</div>
</>
);
}
export function useGlobalAppBar(initTitle, initButton = defaultButton) {
const [button, setButton] = useState(initButton);
const [title, setTitle] = useState(initTitle);
const appBar = useContext(GlobalAppBarContext);
useEffect(() => {
appBar.setTitle(
"CE deploy & monitor " + applicationSubTitle() + " / " + title
);
appBar.setButton(button);
}, [appBar, title, button]);
return { setButton, setTitle };
}
import {
GlobalAppBar,
useGlobalAppBar,
LoginControls,
ButtonAppBar,
GlobalAppBarContext,
UserInfo,
ProfileMenu,
NotificationMenu,
DRAWER_OPEN
} from "./GlobalAppBar";
export {
GlobalAppBar,
useGlobalAppBar,
LoginControls,
ButtonAppBar,
GlobalAppBarContext,
UserInfo,
ProfileMenu,
NotificationMenu,
DRAWER_OPEN
};
export default GlobalAppBar;
import React, { useContext, useEffect, useCallback, useState } from "react";
import { CookiesProvider } from "react-cookie";
import { useCookies } from "react-cookie";
import { userContext } from "../../../api/SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common";
import { useEffectOnMount } from "../../../hooks/MountEffects";
// import { useRedirect } from "../../../hooks/Redirect";
import { useNavigate } from "react-router-dom";
......