From ba5d3e9d2019acda27157c34a182008c30bf03f6 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 27 Aug 2023 00:14:33 +0200 Subject: [PATCH] added multiple contexts --- src/App.js | 52 +++++++- src/Components/AppRoutes/index.js | 21 ++- src/Components/MyAvatar/index.js | 13 ++ src/Components/MyTypography/index.js | 1 - src/Components/SideMenu/index.js | 73 ++++++----- src/Contexts/AppContext.js | 37 ++++++ src/Contexts/SideBarContext.js | 32 +++++ src/Contexts/UserProfileContext.js | 26 ++++ src/Contexts/UsersContext.js | 32 +++++ src/Contexts/WebSocketContext.js | 100 +++++++++++++++ src/Handlers/WebSocketMessageHandler.js | 1 + src/Pages/AllUsers/index.js | 120 ++++++++++-------- .../ViewEquipmentDocumentation.js | 14 +- src/Pages/EquipmentDocumentation/index.js | 5 +- src/Pages/UserProfile/index.js | 85 +++++++------ src/utils.js | 21 +-- 16 files changed, 470 insertions(+), 163 deletions(-) create mode 100644 src/Contexts/AppContext.js create mode 100644 src/Contexts/SideBarContext.js create mode 100644 src/Contexts/UserProfileContext.js create mode 100644 src/Contexts/UsersContext.js create mode 100644 src/Contexts/WebSocketContext.js create mode 100644 src/Handlers/WebSocketMessageHandler.js diff --git a/src/App.js b/src/App.js index 89cf7e2..0376d49 100644 --- a/src/App.js +++ b/src/App.js @@ -2,11 +2,52 @@ import { useState } from "react"; import "antd/dist/reset.css"; import "./App.css"; import Login from "./Pages/Login"; -import { Layout, message, notification } from "antd"; -import { UseUserSession, WebSocketProvider } from "./utils"; +import { Layout, notification } from "antd"; +import { UseUserSession } from "./utils"; import DashboardLayout from "./Components/DashboardLayout"; +import WebSocketProvider from "./Contexts/WebSocketContext"; +import SideBarProvider from "./Contexts/SideBarContext"; +import { AppProvider } from "./Contexts/AppContext"; export default function App() { + const { userSession, setUserSession } = UseUserSession(); + const [isWebSocketReady, setIsWebSocketReady] = useState(true); + + if (!userSession) { + return ; + } + + console.info( + "\n %c Admin Dashboard %c v0.1.0 %c \n", + "background-color: #555;color: #fff;padding: 3px 2px 3px 3px;border-radius: 3px 0 0 3px;font-family: DejaVu Sans,Verdana,Geneva,sans-serif;text-shadow: 0 1px 0 rgba(1, 1, 1, 0.3)", + "background-color: #bc81e0;background-image: linear-gradient(90deg, #e67e22, #9b59b6);color: #fff;padding: 3px 3px 3px 2px;border-radius: 0 3px 3px 0;font-family: DejaVu Sans,Verdana,Geneva,sans-serif;text-shadow: 0 1px 0 rgba(1, 1, 1, 0.3)", + "background-color: transparent" + ); + + return ( + + + + + + + + + + + + ); +} + +/*export default function App() { const [notificationApi, notificationContextHolder] = notification.useNotification(); const { userSession, setUserSession } = UseUserSession(); @@ -26,6 +67,7 @@ export default function App() { return ( {notificationContextHolder} + ); -} +} */ -const ReconnectingView = ({ webSocketIsReady }) => { +const ReconnectingView = ({ isWebSocketReady }) => { return (
{ right: 0, bottom: 0, backgroundColor: "rgba(0, 0, 0, 0.8)", - display: webSocketIsReady ? "none" : "block", + display: isWebSocketReady ? "none" : "block", justifyContent: "center", alignItems: "center", zIndex: 9999, diff --git a/src/Components/AppRoutes/index.js b/src/Components/AppRoutes/index.js index 04b051e..6a07580 100644 --- a/src/Components/AppRoutes/index.js +++ b/src/Components/AppRoutes/index.js @@ -11,11 +11,12 @@ import Scanners from "../../Pages/Scanners"; import AdminAreaRoles from "../../Pages/AdminArea/Roles"; import AdminAreaLogs from "../../Pages/AdminArea/Logs"; import AllUsers from "../../Pages/AllUsers"; -import { useContext } from "react"; import GroupTasks from "../../Pages/GroupTasks/Overview"; import GroupTasksHistory from "../../Pages/GroupTasks/History"; import PageNotFound from "../../Pages/PageNotFound"; import EquipmentDocumentationOverview from "../../Pages/EquipmentDocumentation"; +import { UserProfileProvider } from "../../Contexts/UserProfileContext"; +import { UsersProvider } from "../../Contexts/UsersContext"; export default function AppRoutes() { // const webSocketContext = useContext(WebSocketContext); @@ -79,9 +80,23 @@ export default function AppRoutes() { } /> - } /> + + + + } + /> - } /> + + + + } + /> } /> diff --git a/src/Components/MyAvatar/index.js b/src/Components/MyAvatar/index.js index 84c237a..77b63b0 100644 --- a/src/Components/MyAvatar/index.js +++ b/src/Components/MyAvatar/index.js @@ -58,3 +58,16 @@ export function MyAvatar({ return ; } + +export function MyUserAvatar({ avatar, size = "default" }) { + if (avatar === "") { + return } />; + } + + return ( + + ); +} diff --git a/src/Components/MyTypography/index.js b/src/Components/MyTypography/index.js index 462a47b..19ba0ad 100644 --- a/src/Components/MyTypography/index.js +++ b/src/Components/MyTypography/index.js @@ -1,7 +1,6 @@ import { EditOutlined } from "@ant-design/icons"; import { Input, Typography } from "antd"; import { useState } from "react"; -import { Constants } from "../../utils"; export default function MyTypography({ value, setValue, maxLength }) { const [editing, setEditing] = useState(false); diff --git a/src/Components/SideMenu/index.js b/src/Components/SideMenu/index.js index 442f033..3310e69 100644 --- a/src/Components/SideMenu/index.js +++ b/src/Components/SideMenu/index.js @@ -14,15 +14,11 @@ import Sider from "antd/es/layout/Sider"; import { useContext, useEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; -import { - Constants, - WebSocketContext, - getUserId, - hasOnePermission, - hasPermission, -} from "../../utils"; +import { Constants, hasOnePermission, hasPermission } from "../../utils"; import { useTranslation } from "react-i18next"; -import { MyAvatar } from "../MyAvatar"; +import { MyUserAvatar } from "../MyAvatar"; +import { useSideBarContext } from "../../Contexts/SideBarContext"; +import { useAppContext } from "../../Contexts/AppContext"; export default function SideMenu({ userSession, @@ -30,9 +26,10 @@ export default function SideMenu({ isSideMenuCollapsed, setIsSideMenuCollapsed, }) { + const appContext = useAppContext(); + const sidebarContext = useSideBarContext(); const location = useLocation(); const [selectedKeys, setSelectedKeys] = useState("/"); - const webSocketContext = useContext(WebSocketContext); const { t } = useTranslation(); useEffect(() => setSelectedKeys(location.pathname), [location.pathname]); @@ -40,13 +37,17 @@ export default function SideMenu({ const navigate = useNavigate(); const getCurrentUsedScannerName = () => { - const scannerName = webSocketContext.Scanners.find( + /*const scannerName = webSocketContext.Scanners.find( (scanner) => scanner.UsedByUserId === getUserId() )?.Name; return scannerName === undefined ? t("sideMenu.adminArea.noScannerSelected") - : scannerName; + : scannerName; */ + + // TODO: handle scanner name + + return Constants.LOADING; }; const getFirstMenuItems = () => { @@ -57,19 +58,19 @@ export default function SideMenu({ key: "/", }, ]; - /* + if ( hasPermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.VIEW ) - ) { */ - items.push({ - label: t("sideMenu.equipmentDocumentation"), - icon: , - key: "/equipment-documentation", - }); - //} + ) { + items.push({ + label: t("sideMenu.equipmentDocumentation"), + icon: , + key: "/equipment-documentation", + }); + } let groupTasks = { label: t("sideMenu.groupTasks.menuCategory"), @@ -85,7 +86,7 @@ export default function SideMenu({ if ( hasPermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.GROUP_TASKS.HISTORY ) ) { @@ -104,7 +105,7 @@ export default function SideMenu({ if ( hasOnePermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.ADMIN_AREA.ROLES.CREATE_NEW_ROLE, Constants.PERMISSIONS.ADMIN_AREA.ROLES.UPDATE_ROLE, Constants.PERMISSIONS.ADMIN_AREA.ROLES.DELETE_ROLE, @@ -121,7 +122,7 @@ export default function SideMenu({ if ( hasOnePermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.ADMIN_AREA.ROLES.CREATE_NEW_ROLE, Constants.PERMISSIONS.ADMIN_AREA.ROLES.UPDATE_ROLE, Constants.PERMISSIONS.ADMIN_AREA.ROLES.DELETE_ROLE, @@ -137,7 +138,7 @@ export default function SideMenu({ if ( hasPermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.ADMIN_AREA.LOGS ) ) { @@ -155,11 +156,13 @@ export default function SideMenu({ }; const getSecondMenuItems = () => { + console.log("getSecondMenuItems", sidebarContext.connectionBadgeStatus); + let items = []; if ( hasPermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.SCANNER.USE_SCANNERS ) ) { @@ -174,9 +177,9 @@ export default function SideMenu({ { icon: ( - ), + label: ` ${appContext.username}`, + icon: , key: "/user-profile", }, { @@ -216,6 +214,11 @@ export default function SideMenu({ return items; }; + console.log( + "sidebarContext.connectionBadgeStatus", + sidebarContext.connectionBadgeStatus + ); + return ( useContext(AppContext); + +export function AppProvider({ children }) { + const [username, setUsername] = useState(Constants.LOADING); + const [avatar, setAvatar] = useState(""); + const [userPermissions, setUserPermissions] = useState([]); + const [availableGroupTasks, setAvailableGroupTasks] = useState([]); + + return ( + + {children} + + ); +} diff --git a/src/Contexts/SideBarContext.js b/src/Contexts/SideBarContext.js new file mode 100644 index 0000000..7595972 --- /dev/null +++ b/src/Contexts/SideBarContext.js @@ -0,0 +1,32 @@ +import { createContext, useContext, useState } from "react"; + +const preview = { + connectionBadgeStatus: "", + connectedUsers: 0, + selectedScanner: "", +}; + +const SideBarContext = createContext(preview); + +export const useSideBarContext = () => useContext(SideBarContext); + +export default function SideBarProvider({ children }) { + const [connectionBadgeStatus, setConnectionBadgeStatus] = useState("error"); + const [connectedUsers, setConnectedUsers] = useState(0); + const [selectedScanner, setSelectedScanner] = useState(""); + + return ( + + {children} + + ); +} diff --git a/src/Contexts/UserProfileContext.js b/src/Contexts/UserProfileContext.js new file mode 100644 index 0000000..ca85951 --- /dev/null +++ b/src/Contexts/UserProfileContext.js @@ -0,0 +1,26 @@ +import { createContext, useContext, useState } from "react"; +import { Constants } from "../utils"; + +// only for tab completion +const preview = { + id: Constants.LOADING, + avatar: "", + username: Constants.LOADING, + email: Constants.LOADING, + sessions: [], + apiKeys: [], +}; + +const UserProfileContext = createContext({ userProfile: preview }); + +export const useUserProfileContext = () => useContext(UserProfileContext); + +export function UserProfileProvider({ children }) { + const [userProfile, setUserProfile] = useState(preview); + + return ( + + {children} + + ); +} diff --git a/src/Contexts/UsersContext.js b/src/Contexts/UsersContext.js new file mode 100644 index 0000000..f3ba0c7 --- /dev/null +++ b/src/Contexts/UsersContext.js @@ -0,0 +1,32 @@ +import { createContext, useContext, useState } from "react"; + +const preview = { + roleId: "", + users: [], + roles: [], +}; + +const UsersContext = createContext(preview); + +export const useUsersContext = () => useContext(UsersContext); + +export function UsersProvider({ children }) { + const [roleId, setRoleId] = useState(""); + const [users, setUsers] = useState([]); + const [roles, setRoles] = useState([]); + + return ( + + {children} + + ); +} diff --git a/src/Contexts/WebSocketContext.js b/src/Contexts/WebSocketContext.js new file mode 100644 index 0000000..9a3ae3b --- /dev/null +++ b/src/Contexts/WebSocketContext.js @@ -0,0 +1,100 @@ +import { createContext, useContext, useEffect, useRef, useState } from "react"; +import { Constants, ReceivedMessagesCommands, myFetch } from "../utils"; +import { useSideBarContext } from "./SideBarContext"; +import { useAppContext } from "./AppContext"; +import { message } from "antd"; + +const WebSocketContext = createContext(null); + +export const useWebSocketContext = () => useContext(WebSocketContext); + +export default function WebSocketProvider({ + children, + userSession, + setUserSession, + isWebSocketReady, + setIsWebSocketReady, +}) { + const ws = useRef(null); + const appContext = useAppContext(); + const sideBarContext = useSideBarContext(); + + const connect = () => { + ws.current = new WebSocket(`${Constants.WS_ADDRESS}?auth=${userSession}`); + + ws.current.onopen = () => { + console.log("connected"); + sideBarContext.setConnectionBadgeStatus("success"); + setIsWebSocketReady(true); + + myFetch("/user/", "GET").then((data) => { + console.log("user info", data); + + appContext.setUsername(data.Username); + appContext.setAvatar(data.Avatar); + appContext.setUserPermissions(data.Permissions); + appContext.setAvailableGroupTasks(data.AvailableGroupTasks); + }); + }; + + ws.current.onmessage = (event) => { + const data = JSON.parse(event.data); + console.log("received message", data); + + const cmd = data.Cmd; + const body = data.Body; + + switch (cmd) { + case ReceivedMessagesCommands.UpdateConnectedUsers: + sideBarContext.setConnectedUsers(body.WebSocketUsersCount); + break; + } + }; + + ws.current.onclose = (event) => { + setIsWebSocketReady(false); + sideBarContext.setConnectionBadgeStatus("error"); + console.warn("closed", event); + + // custom code defined by the backend server + if (event.code === 4001 || event.code === 4002) { + //Unauthorized || SessionClosed + + setUserSession(); + window.location.href = "/"; + return; + } + + if (event.reason.code === 1005) return; + + console.warn("reconnecting..."); + + setTimeout(() => connect(), 1000); + }; + }; + + const SendSocketMessage = (cmd, body) => { + if (isWebSocketReady) { + ws.current.send(JSON.stringify({ Cmd: cmd, Body: body })); + } else { + /*notificationApi["error"]({ + message: `Websocket is not ready`, + description: `Please check your internet connection`, + }); */ + + message.error(`Websocket is not ready`); + } + }; + + useEffect(() => { + connect(); + + return () => ws.current.close(); + }, []); + + return ( + + {children} + + ); +} diff --git a/src/Handlers/WebSocketMessageHandler.js b/src/Handlers/WebSocketMessageHandler.js new file mode 100644 index 0000000..b52cd90 --- /dev/null +++ b/src/Handlers/WebSocketMessageHandler.js @@ -0,0 +1 @@ +export function handleWebSocketMessage(message) {} diff --git a/src/Pages/AllUsers/index.js b/src/Pages/AllUsers/index.js index 09d693c..7ec9222 100644 --- a/src/Pages/AllUsers/index.js +++ b/src/Pages/AllUsers/index.js @@ -12,20 +12,25 @@ import { Constants, FormatDatetime, SentMessagesCommands, - WebSocketContext, getConnectionStatusItem, hasOnePermission, hasPermission, + myFetch, } from "../../utils"; -import { useContext, useState } from "react"; +import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { UserAddOutlined } from "@ant-design/icons"; import CreateUserModal from "./CreateUserModal"; import { useTranslation } from "react-i18next"; import { MyAvatar } from "../../Components/MyAvatar"; +import { useUsersContext } from "../../Contexts/UsersContext"; +import { useAppContext } from "../../Contexts/AppContext"; +import { useWebSocketContext } from "../../Contexts/WebSocketContext"; export default function AllUsers() { - const webSocketContext = useContext(WebSocketContext); + const webSocketContext = useWebSocketContext(); + const appContext = useAppContext(); + const usersContext = useUsersContext(); const { t } = useTranslation(); const [selectedRoleId, setSelectedRoleId] = useState(""); const [notificationApi, notificationContextHolder] = @@ -63,7 +68,7 @@ export default function AllUsers() { if ( hasOnePermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.ALL_USERS.ACTION.CHANGE_ROLE, Constants.PERMISSIONS.ALL_USERS.ACTION.DELETE_USER, Constants.PERMISSIONS.ALL_USERS.ACTION.DEACTIVATE_USER @@ -75,17 +80,16 @@ export default function AllUsers() { render: (_, record) => ( {hasPermission( - webSocketContext.User.Permissions, + appContext.userPermissions, Constants.PERMISSIONS.ALL_USERS.ACTION.CHANGE_ROLE ) && - (webSocketContext.AllRoles.find( - (role) => role.Id === webSocketContext.User.RoleId + (usersContext.roles.find( + (role) => role.Id === usersContext.roleId ).SortingOrder < - webSocketContext.AllRoles.find( - (role) => role.Id === record._roleId - ).SortingOrder || - webSocketContext.AllRoles.find( - (role) => role.Id === webSocketContext.User.RoleId + usersContext.roles.find((role) => role.Id === record._roleId) + .SortingOrder || + usersContext.roles.find( + (role) => role.Id === usersContext.roleId ).Master) && ( user.Id === record.key - ).RoleId, + usersContext.users.find((user) => user.Id === record.key) + .RoleId, }} description={ - webSocketContext.setUserProfileStateUsername(e.target.value) + setUserProfile({ ...userProfile, Username: e.target.value }) } minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH} maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH} @@ -359,15 +367,13 @@ export default function UserProfile() { - webSocketContext.setUserProfileStateEmail(e.target.value) + setUserProfile({ ...userProfile, Email: e.target.value }) } /> @@ -431,8 +437,7 @@ export default function UserProfile() {
- {t("userProfile.header.yourSessions")} ( - {webSocketContext.User.Sessions.length}) + {t("userProfile.header.yourSessions")} ({userProfile.sessions.length}) {hasPermission( - webSocketContext.User.Permissions, + userProfile.Permissions, Constants.PERMISSIONS.USER_PROFILE.API_KEYS ) && ( <> @@ -455,7 +460,7 @@ export default function UserProfile() { > {t("userProfile.header.yourApiKeys")} ( - {webSocketContext.User.ApiKeys.length}){" "} + {userProfile.apiKeys.length}){" "}