Merged PR 83: Frontend Upgrades - The frontend has been upgraded to leverage TypeScript, React 18, and Material UI 5, enhancing code quality, UI consistency, and leveraging the latest features of these technologies.
• Frontend Upgrades - The frontend has been upgraded to leverage TypeScript, React 18, and Material UI 5, enhancing code quality, UI consistency, and leveraging the latest features of these technologies. • Build process overhaul - I've switched from react-scripts to react-app-rewired. This gives me more control to tweak the webpack configuration as I need. Plus, it's now set up to handle CommonJS (CJS) modules. • TypeScript refactoring - Several components have been rewritten in TypeScript, improving type safety and predictability in our codebase.master
commit
e80d246fc0
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
<PropertyGroup>
|
||||
<Version>1.2.7</Version>
|
||||
<Version>1.3.0</Version>
|
||||
<Authors>Tudor Stanciu</Authors>
|
||||
<Company>STA</Company>
|
||||
<PackageTags>NetworkResurrector</PackageTags>
|
||||
|
|
|
@ -201,4 +201,15 @@
|
|||
• Updated menu component to permanently display the selected item.
|
||||
</Content>
|
||||
</Note>
|
||||
<Note>
|
||||
<Version>1.3.0</Version>
|
||||
<Date>2024-03-31 23:24</Date>
|
||||
<Content>
|
||||
Major enhancements
|
||||
• Unified repository structure - The backend and frontend codebases have been consolidated into a single repository, improving code management and cross-component consistency.
|
||||
• Frontend Upgrades - The frontend has been upgraded to leverage TypeScript, React 18, and Material UI 5, enhancing code quality, UI consistency, and leveraging the latest features of these technologies.
|
||||
• Build process overhaul - I've switched from react-scripts to react-app-rewired. This gives me more control to tweak the webpack configuration as I need. Plus, it's now set up to handle CommonJS (CJS) modules.
|
||||
• TypeScript refactoring - Several components have been rewritten in TypeScript, improving type safety and predictability in our codebase.
|
||||
</Content>
|
||||
</Note>
|
||||
</ReleaseNotes>
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"prettier",
|
||||
"plugin:prettier/recommended",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint", "prettier", "react", "react-hooks"],
|
||||
"rules": {
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-debugger": "warn"
|
||||
},
|
||||
"ignorePatterns": ["**/public"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"JSX": true
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"requireConfigFile": false,
|
||||
"babelOptions": {
|
||||
"presets": ["@babel/preset-react"]
|
||||
},
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "react-hooks", "jest"],
|
||||
"ignorePatterns": ["**/public"],
|
||||
"rules": {
|
||||
"indent": 0,
|
||||
"linebreak-style": 0,
|
||||
"quotes": 0,
|
||||
"semi": 0,
|
||||
"no-console": 0,
|
||||
"no-debugger": "warn",
|
||||
"react/display-name": "off",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"no-unused-vars": [1, { "args": "after-used", "argsIgnorePattern": "^_" }]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"printWidth": 120,
|
||||
"trailingComma": "none",
|
||||
"singleQuote": false,
|
||||
"endOfLine": "auto"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"arrowParens": "avoid"
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
const getCacheIdentifier = require("react-dev-utils/getCacheIdentifier");
|
||||
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
||||
|
||||
module.exports = function override(config, webpackEnv) {
|
||||
console.log("overriding webpack config...");
|
||||
|
||||
const isEnvDevelopment = webpackEnv === "development";
|
||||
const isEnvProduction = webpackEnv === "production";
|
||||
const loaders = config.module.rules[1].oneOf;
|
||||
|
||||
loaders.splice(loaders.length - 1, 0, {
|
||||
test: /\.(js|mjs|cjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve("babel-loader"),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [[require.resolve("babel-preset-react-app/dependencies"), { helpers: true }]],
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
// @remove-on-eject-begin
|
||||
cacheIdentifier: getCacheIdentifier(isEnvProduction ? "production" : isEnvDevelopment && "development", [
|
||||
"babel-plugin-named-asset-import",
|
||||
"babel-preset-react-app",
|
||||
"react-dev-utils",
|
||||
"react-scripts"
|
||||
]),
|
||||
// @remove-on-eject-end
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap
|
||||
}
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "network-resurrector-frontend",
|
||||
"version": "1.2.7",
|
||||
"version": "1.3.0",
|
||||
"description": "Frontend component of Network resurrector system",
|
||||
"author": {
|
||||
"name": "Tudor Stanciu",
|
||||
|
@ -9,41 +9,49 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector-frontend"
|
||||
"url": "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@flare/js-utils": "^1.1.0",
|
||||
"@flare/tuitio-client-react": "^1.2.6",
|
||||
"@material-ui/core": "^4.11.2",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"axios": "^1.3.4",
|
||||
"i18next": "^19.4.4",
|
||||
"i18next-browser-languagedetector": "^4.1.1",
|
||||
"i18next-http-backend": "^1.4.0",
|
||||
"moment": "^2.29.3",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-flags": "^0.1.18",
|
||||
"react-i18next": "^11.4.0",
|
||||
"@flare/tuitio-client-react": "^1.2.10",
|
||||
"@mui/icons-material": "^5.14.16",
|
||||
"@mui/lab": "^5.0.0-alpha.169",
|
||||
"@mui/material": "^5.14.16",
|
||||
"axios": "^1.6.8",
|
||||
"i18next": "^22.4.15",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.2.0",
|
||||
"moment": "^2.29.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.2.2",
|
||||
"react-lazylog": "^4.5.3",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.1",
|
||||
"react-toastify": "^6.2.0"
|
||||
"react-router-dom": "^6.10.0",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-world-flags": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
"@testing-library/user-event": "^12.5.0",
|
||||
"prettier": "^2.5.1"
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@types/react": "^18.2.33",
|
||||
"@types/react-dom": "^18.2.14",
|
||||
"@types/react-world-flags": "^1.4.5",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"prettier": "^2.8.4",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"start": "react-app-rewired start",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "react-app-rewired eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
@ -53,17 +61,14 @@
|
|||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.3%",
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version",
|
||||
">0.3%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
REACT v4:
|
||||
https://v4.mui.com/getting-started/installation/
|
||||
https://v4.mui.com/components/material-icons/
|
||||
|
||||
Theming:
|
||||
https://v4.mui.com/customization/palette/ (Dark theme)
|
||||
|
||||
Add in settings:
|
||||
- ping interval
|
||||
- notifications
|
||||
- test notification mechanism
|
||||
- permissions
|
||||
- permissions hierarchy
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 937 B |
|
@ -1,73 +0,0 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import App from "./App";
|
||||
import { BrowserRouter, Switch, Redirect, Route } from "react-router-dom";
|
||||
import { useTuitioToken } from "@flare/tuitio-client-react";
|
||||
import LoginContainer from "../features/login/components/LoginContainer";
|
||||
|
||||
const PrivateRoute = ({ component, ...rest }) => {
|
||||
const { valid } = useTuitioToken();
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
valid ? (
|
||||
React.createElement(component, props)
|
||||
) : (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/login",
|
||||
state: {
|
||||
from: props.location
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
PrivateRoute.propTypes = {
|
||||
component: PropTypes.func.isRequired,
|
||||
location: PropTypes.object
|
||||
};
|
||||
|
||||
const PublicRoute = ({ component, ...rest }) => {
|
||||
const { valid } = useTuitioToken();
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
valid ? (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
React.createElement(component, props)
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
PublicRoute.propTypes = {
|
||||
component: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const AppRouter = () => {
|
||||
return (
|
||||
<BrowserRouter basename={process.env.PUBLIC_URL || ""}>
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to="/dashboard" />} />
|
||||
<PublicRoute path="/login" component={LoginContainer} />
|
||||
<PrivateRoute path="/" component={App} />
|
||||
</Switch>
|
||||
</BrowserRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppRouter;
|
|
@ -0,0 +1,70 @@
|
|||
import React, { useMemo } from "react";
|
||||
import App from "./App";
|
||||
import { BrowserRouter, Navigate, Route, Routes, useLocation } from "react-router-dom";
|
||||
import { useTuitioToken } from "@flare/tuitio-client-react";
|
||||
import LoginContainer from "../features/login/components/LoginContainer";
|
||||
|
||||
const PrivateRoute = ({ children }: { children: JSX.Element }): JSX.Element => {
|
||||
const { valid } = useTuitioToken();
|
||||
const location = useLocation();
|
||||
return valid ? (
|
||||
children
|
||||
) : (
|
||||
<Navigate
|
||||
to="/login"
|
||||
state={{
|
||||
from: location.pathname,
|
||||
search: location.search
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const PublicRoute = ({ children }: { children: JSX.Element }): JSX.Element => {
|
||||
const location = useLocation();
|
||||
const { valid } = useTuitioToken();
|
||||
const to = useMemo(() => {
|
||||
if (location.state?.from) {
|
||||
return location.state.from + location.state.search;
|
||||
}
|
||||
return "/";
|
||||
}, [location.state?.from, location.state?.search]);
|
||||
return valid ? (
|
||||
<Navigate
|
||||
to={to}
|
||||
state={{
|
||||
from: location.pathname
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
};
|
||||
|
||||
const AppRouter: React.FC = () => {
|
||||
return (
|
||||
<BrowserRouter basename={process.env.PUBLIC_URL || ""}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="/dashboard" />} />
|
||||
<Route
|
||||
path="/login"
|
||||
element={
|
||||
<PublicRoute>
|
||||
<LoginContainer />
|
||||
</PublicRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/*"
|
||||
element={
|
||||
<PrivateRoute>
|
||||
<App />
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppRouter;
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const getStyles = theme => ({
|
||||
panel: {
|
||||
display: "flex"
|
||||
},
|
||||
|
@ -13,21 +13,19 @@ const useStyles = makeStyles(theme => ({
|
|||
data: {
|
||||
fontWeight: theme.typography.fontWeightMedium
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
const DataLabel = ({ label, data }) => {
|
||||
const classes = useStyles();
|
||||
const lbl = useMemo(
|
||||
() => (label.endsWith(":") ? label : `${label}:`),
|
||||
[label]
|
||||
);
|
||||
const theme = useTheme();
|
||||
const lbl = useMemo(() => (label.endsWith(":") ? label : `${label}:`), [label]);
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={classes.panel}>
|
||||
<Typography variant="body2" className={classes.label}>
|
||||
<div style={styles.panel}>
|
||||
<Typography variant="body2" sx={styles.label}>
|
||||
{lbl}
|
||||
</Typography>
|
||||
<Typography variant="body2" className={classes.data}>
|
||||
<Typography variant="body2" sx={styles.data}>
|
||||
{data}
|
||||
</Typography>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import React, { CSSProperties, useMemo } from "react";
|
||||
import Flag, { FlagProps } from "react-world-flags";
|
||||
|
||||
const defaultStyle: CSSProperties = { height: "1.2rem", width: "2rem", objectFit: "cover" };
|
||||
|
||||
const FlagIcon: React.FC<FlagProps> = ({ code, style, ...rest }) => {
|
||||
const localCode = useMemo(() => (code === "en" ? "gb" : code), [code]);
|
||||
const localStyle = useMemo(() => ({ ...defaultStyle, ...style }), [style]);
|
||||
return <Flag {...rest} code={localCode} style={localStyle} />;
|
||||
};
|
||||
|
||||
export default FlagIcon;
|
|
@ -1,8 +1,7 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
import { ToggleButtonGroup, ToggleButton } from "@mui/material";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
const NavigationButtons = ({ tabs, onTabChange }) => {
|
||||
const [selected, setSelected] = useState(tabs[0].code);
|
||||
|
@ -13,19 +12,9 @@ const NavigationButtons = ({ tabs, onTabChange }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={selected}
|
||||
exclusive
|
||||
onChange={handleTabSelection}
|
||||
>
|
||||
<ToggleButtonGroup size="small" value={selected} exclusive onChange={handleTabSelection}>
|
||||
{tabs.map(tab => (
|
||||
<ToggleButton
|
||||
key={tab.code}
|
||||
value={tab.code}
|
||||
aria-label="navigation buttons"
|
||||
disabled={selected === tab.code}
|
||||
>
|
||||
<ToggleButton key={tab.code} value={tab.code} aria-label="navigation buttons" disabled={selected === tab.code}>
|
||||
<Tooltip title={tab.tooltip}>
|
||||
<tab.icon color="primary" />
|
||||
</Tooltip>
|
||||
|
@ -36,9 +25,7 @@ const NavigationButtons = ({ tabs, onTabChange }) => {
|
|||
};
|
||||
|
||||
NavigationButtons.propTypes = {
|
||||
tabs: PropTypes.arrayOf(
|
||||
PropTypes.shape({ code: PropTypes.string.isRequired })
|
||||
).isRequired,
|
||||
tabs: PropTypes.arrayOf(PropTypes.shape({ code: PropTypes.string.isRequired })).isRequired,
|
||||
onTabChange: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
import React from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||
import { Alert, AlertTitle, Box } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
alert: {
|
||||
width: "100%",
|
||||
"& > * + *": {
|
||||
marginTop: theme.spacing(1)
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const NotAllowed = () => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={classes.alert}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& > * + *": {
|
||||
marginTop: 1
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Alert variant="outlined" severity="error">
|
||||
<AlertTitle>{t("Announcements.NotAllowed.Title")}</AlertTitle>
|
||||
{t("Announcements.NotAllowed.Message")}
|
||||
</Alert>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { Typography, Box } from "@mui/material";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const styles = {
|
||||
box: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: theme.spacing(1),
|
||||
marginTop: theme.spacing(0)
|
||||
marginBottom: 1,
|
||||
marginTop: 0
|
||||
},
|
||||
title: {
|
||||
display: "flex",
|
||||
|
@ -16,22 +15,22 @@ const useStyles = makeStyles(theme => ({
|
|||
flexDirection: "column",
|
||||
minHeight: "40px"
|
||||
},
|
||||
titleText: { textTransform: "uppercase" }
|
||||
}));
|
||||
titleText: {
|
||||
textTransform: "uppercase"
|
||||
}
|
||||
};
|
||||
|
||||
const PageTitle = ({ text, toolBar, navigation }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.box}>
|
||||
<Box sx={styles.box}>
|
||||
{navigation && navigation}
|
||||
<div className={classes.title}>
|
||||
<Typography className={classes.titleText} variant="h3" size="sm">
|
||||
<Box sx={styles.title}>
|
||||
<Typography sx={styles.titleText} variant="h3" size="sm">
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
</Box>
|
||||
{toolBar && toolBar}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Typography, Box } from "@mui/material";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const styles = {
|
||||
paper: {
|
||||
margin: theme.spacing(1)
|
||||
margin: 1
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const PaperTitle = ({ text }) => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div className={classes.paper}>
|
||||
<Box sx={styles.paper}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import DataLabel from "./DataLabel";
|
||||
import PaperTitle from "./PaperTitle";
|
||||
import FlagIcon from "./FlagIcon";
|
||||
|
||||
export { DataLabel, PaperTitle };
|
||||
export { DataLabel, PaperTitle, FlagIcon };
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
InputAdornment,
|
||||
TextField,
|
||||
makeStyles,
|
||||
IconButton
|
||||
} from "@material-ui/core";
|
||||
import { Visibility, VisibilityOff, LockOutlined } from "@material-ui/icons";
|
||||
import { InputAdornment, TextField, IconButton } from "@mui/material";
|
||||
import { Visibility, VisibilityOff, LockOutlined } from "@mui/icons-material";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const styles = {
|
||||
margin: {
|
||||
margin: theme.spacing(1)
|
||||
margin: 1
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const PasswordField = ({ label, ...rest }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const classes = useStyles();
|
||||
|
||||
const handleClickShowPassword = () => {
|
||||
setShowPassword(!showPassword);
|
||||
|
@ -28,7 +22,7 @@ const PasswordField = ({ label, ...rest }) => {
|
|||
|
||||
return (
|
||||
<TextField
|
||||
className={classes.margin}
|
||||
sx={styles.margin}
|
||||
label={label || "Password"}
|
||||
{...rest}
|
||||
type={showPassword ? "text" : "password"}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import React from "react";
|
||||
import * as MuiIcons from "./list";
|
||||
import { Icon as MuiIcon, IconProps } from "@mui/material";
|
||||
|
||||
interface Props extends IconProps {
|
||||
code?: string | null;
|
||||
fallback?: JSX.Element;
|
||||
}
|
||||
|
||||
const DynamicIcon: React.FC<Props> = ({ code, fallback, ...res }) => {
|
||||
if (code && code in MuiIcons) {
|
||||
const Icon = MuiIcons[code as keyof typeof MuiIcons] as typeof MuiIcon;
|
||||
return <Icon {...res} />;
|
||||
} else {
|
||||
return <>{fallback ?? ""}</>;
|
||||
}
|
||||
};
|
||||
|
||||
export default DynamicIcon;
|
|
@ -0,0 +1,4 @@
|
|||
import DynamicIcon from "./DynamicIcon";
|
||||
|
||||
export * from "./list";
|
||||
export { DynamicIcon };
|
|
@ -0,0 +1 @@
|
|||
export { Home, Dashboard, Dns, DeviceHub, Build, Settings, FeaturedPlayList, Info } from "@mui/icons-material";
|
|
@ -1,34 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import AppRoutes from "./AppRoutes";
|
||||
import TopBar from "./TopBar";
|
||||
import Sidebar from "./Sidebar";
|
||||
import styles from "./styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const AppLayout = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const classes = useStyles();
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<TopBar open={open} handleDrawerOpen={handleDrawerOpen} />
|
||||
<Sidebar open={open} handleDrawerClose={handleDrawerClose} />
|
||||
<main className={classes.content}>
|
||||
<div className={classes.toolbar} />
|
||||
<AppRoutes />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
|
@ -0,0 +1,54 @@
|
|||
import React, { useState } from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import AppRoutes from "./AppRoutes";
|
||||
import TopBar from "./TopBar";
|
||||
import Sidebar from "./SideBar";
|
||||
|
||||
import { drawerWidth } from "./constants";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const DrawerHeader = styled("div")(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: theme.spacing(0, 1),
|
||||
// necessary for content to be below app bar
|
||||
...theme.mixins.toolbar
|
||||
}));
|
||||
|
||||
const AppLayout: React.FC = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", minHeight: "100vh" }}>
|
||||
<TopBar open={open} onDrawerOpen={handleDrawerOpen} />
|
||||
<Sidebar open={open} onDrawerOpen={handleDrawerOpen} onDrawerClose={handleDrawerClose} />
|
||||
<Box
|
||||
component="main"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
paddingTop: 2,
|
||||
paddingBottom: 1,
|
||||
paddingLeft: 1,
|
||||
paddingRight: 1,
|
||||
width: `calc(100% - ${drawerWidth}px)`
|
||||
}}
|
||||
>
|
||||
<DrawerHeader />
|
||||
<AppRoutes />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import NetworkContainer from "../../features/network/components/NetworkContainer";
|
||||
import SystemContainer from "../../features/system/SystemContainer";
|
||||
|
@ -8,17 +8,17 @@ import DashboardContainer from "../../features/dashboard/DashboardContainer";
|
|||
import UserProfileContainer from "../../features/user/profile/card/UserProfileContainer";
|
||||
import AboutContainer from "../../features/about/AboutContainer";
|
||||
|
||||
const AppRoutes = () => {
|
||||
const AppRoutes: React.FC = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path="/dashboard" component={DashboardContainer} />
|
||||
<Route exact path="/user-profile" component={UserProfileContainer} />
|
||||
<Route exact path="/machines" component={NetworkContainer} />
|
||||
<Route exact path="/system" component={SystemContainer} />
|
||||
<Route exact path="/settings" component={SettingsContainer} />
|
||||
<Route exact path="/about" component={AboutContainer} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
<Routes>
|
||||
<Route path="/dashboard" element={<DashboardContainer />} />
|
||||
<Route path="/user-profile" element={<UserProfileContainer />} />
|
||||
<Route path="/machines" element={<NetworkContainer />} />
|
||||
<Route path="/system" element={<SystemContainer />} />
|
||||
<Route path="/settings" element={<SettingsContainer />} />
|
||||
<Route path="/about" element={<AboutContainer />} />
|
||||
<Route path="/*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
import React from "react";
|
||||
import { IconButton } from "@material-ui/core";
|
||||
import {
|
||||
Brightness2 as MoonIcon,
|
||||
WbSunny as SunIcon
|
||||
} from "@material-ui/icons";
|
||||
import { IconButton } from "@mui/material";
|
||||
import { Brightness2 as MoonIcon, WbSunny as SunIcon } from "@mui/icons-material";
|
||||
import { useApplicationTheme } from "../../providers/ThemeProvider";
|
||||
|
||||
const LightDarkToggle = () => {
|
||||
|
@ -12,11 +9,7 @@ const LightDarkToggle = () => {
|
|||
const handleChange = () => onDarkModeChanged(!isDark);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="light-dark-toggle"
|
||||
color="inherit"
|
||||
onClick={handleChange}
|
||||
>
|
||||
<IconButton aria-label="light-dark-toggle" color="inherit" onClick={handleChange}>
|
||||
{isDark ? <SunIcon /> : <MoonIcon />}
|
||||
</IconButton>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import React from "react";
|
||||
import { SxProps, Theme } from "@mui/material/styles";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Badge from "@mui/material/Badge";
|
||||
import { ExpandLess, ExpandMore } from "@mui/icons-material";
|
||||
import { Collapse, Divider, Tooltip } from "@mui/material";
|
||||
|
||||
type MenuItemProps = {
|
||||
open: boolean;
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
onClick?: () => void;
|
||||
subMenus?: MenuItemProps[];
|
||||
sx?: SxProps<Theme>;
|
||||
};
|
||||
|
||||
const MenuItem: React.FC<MenuItemProps> = ({ open, icon, label, onClick, subMenus, sx }) => {
|
||||
const [openSubMenu, setOpenSubMenu] = React.useState(false);
|
||||
|
||||
const handleSubMenuToggle = () => {
|
||||
setOpenSubMenu(!openSubMenu);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem disablePadding sx={{ display: "block" }}>
|
||||
<Tooltip title={open ? undefined : label} placement="right" arrow>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
minHeight: 48,
|
||||
justifyContent: open ? "initial" : "center",
|
||||
px: 2.5,
|
||||
...sx
|
||||
}}
|
||||
onClick={onClick ? onClick : handleSubMenuToggle}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 0,
|
||||
mr: open ? 3 : "auto",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
{open || !subMenus ? (
|
||||
icon
|
||||
) : (
|
||||
<Badge badgeContent={openSubMenu ? <ExpandLess fontSize="small" /> : <ExpandMore fontSize="small" />}>
|
||||
{icon}
|
||||
</Badge>
|
||||
)}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={label}
|
||||
sx={{ opacity: open ? 1 : 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
|
||||
/>
|
||||
{subMenus && open && (
|
||||
<ListItemIcon sx={{ minWidth: 0, ml: "auto" }}>
|
||||
{openSubMenu ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
</ListItem>
|
||||
{subMenus && (
|
||||
<Collapse in={openSubMenu} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding>
|
||||
{subMenus.map((subItem, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<MenuItem key={index} {...subItem} sx={{ marginLeft: subItem.open ? 2 : 0 }} />
|
||||
{index === subMenus.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuItem;
|
|
@ -1,28 +1,17 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Typography,
|
||||
ListItemIcon
|
||||
} from "@material-ui/core";
|
||||
import AccountCircle from "@material-ui/icons/AccountCircle";
|
||||
import ExitToAppIcon from "@material-ui/icons/ExitToApp";
|
||||
import AccountBoxIcon from "@material-ui/icons/AccountBox";
|
||||
import SettingsIcon from "@material-ui/icons/Settings";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { IconButton, Menu, MenuItem, Typography, ListItemIcon } from "@mui/material";
|
||||
import AccountCircle from "@mui/icons-material/AccountCircle";
|
||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
||||
import AccountBoxIcon from "@mui/icons-material/AccountBox";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTuitioClient } from "@flare/tuitio-client-react";
|
||||
import { useToast } from "../../hooks";
|
||||
import styles from "./styles";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const ProfileButton = () => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { error } = useToast();
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { logout } = useTuitioClient({
|
||||
|
@ -69,28 +58,28 @@ const ProfileButton = () => {
|
|||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
history.push("/user-profile");
|
||||
navigate("/user-profile");
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
||||
<AccountBoxIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="inherit">{t("User.Profile.Label")}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
history.push("/settings");
|
||||
navigate("/settings");
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
||||
<SettingsIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="inherit">{t("User.Settings")}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={logout}>
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
||||
<ExitToAppIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="inherit">{t("User.Logout")}</Typography>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React from "react";
|
||||
import { IconButton } from "@material-ui/core";
|
||||
import {
|
||||
Visibility as VisibilityIcon,
|
||||
VisibilityOff as VisibilityOffIcon
|
||||
} from "@material-ui/icons";
|
||||
import { IconButton } from "@mui/material";
|
||||
import { Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon } from "@mui/icons-material";
|
||||
import { useSensitiveInfo } from "../../hooks";
|
||||
|
||||
const SensitiveInfoToggle = () => {
|
||||
|
@ -12,11 +9,7 @@ const SensitiveInfoToggle = () => {
|
|||
const handleChange = () => onSensitiveInfoEnabled(!enabled);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="sensitive-info-toggle"
|
||||
color="inherit"
|
||||
onClick={handleChange}
|
||||
>
|
||||
<IconButton aria-label="sensitive-info-toggle" color="inherit" onClick={handleChange}>
|
||||
{enabled ? <VisibilityOffIcon /> : <VisibilityIcon />}
|
||||
</IconButton>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
import * as React from "react";
|
||||
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
||||
import MuiDrawer from "@mui/material/Drawer";
|
||||
import List from "@mui/material/List";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { drawerWidth } from "./constants";
|
||||
import MenuItem from "./MenuItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { menu } from "./constants";
|
||||
|
||||
const openedMixin = (theme: Theme): CSSObject => ({
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
}),
|
||||
overflowX: "hidden"
|
||||
});
|
||||
|
||||
const closedMixin = (theme: Theme): CSSObject => ({
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
overflowX: "hidden",
|
||||
width: `calc(${theme.spacing(7)} + 1px)`,
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
width: `calc(${theme.spacing(8)} + 1px)`
|
||||
}
|
||||
});
|
||||
|
||||
const DrawerHeader = styled("div")(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: theme.spacing(0, 1),
|
||||
// necessary for content to be below app bar
|
||||
...theme.mixins.toolbar
|
||||
}));
|
||||
|
||||
const Drawer = styled(MuiDrawer, { shouldForwardProp: prop => prop !== "open" })(({ theme, open }) => ({
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
whiteSpace: "nowrap",
|
||||
boxSizing: "border-box",
|
||||
...(open && {
|
||||
...openedMixin(theme),
|
||||
"& .MuiDrawer-paper": openedMixin(theme)
|
||||
}),
|
||||
...(!open && {
|
||||
...closedMixin(theme),
|
||||
"& .MuiDrawer-paper": closedMixin(theme)
|
||||
})
|
||||
}));
|
||||
|
||||
type SideBarProps = {
|
||||
open: boolean;
|
||||
onDrawerOpen: () => void;
|
||||
onDrawerClose: () => void;
|
||||
};
|
||||
|
||||
const SideBar: React.FC<SideBarProps> = ({ open, onDrawerOpen, onDrawerClose }) => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
menu.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||
|
||||
return (
|
||||
<Drawer variant="permanent" open={open}>
|
||||
<DrawerHeader>
|
||||
<IconButton onClick={open ? onDrawerClose : onDrawerOpen}>
|
||||
{open ? <ChevronLeftIcon /> : <ChevronRightIcon />}
|
||||
</IconButton>
|
||||
</DrawerHeader>
|
||||
<Divider />
|
||||
|
||||
{menu.map((section, index) => {
|
||||
const isLast = index === menu.length - 1;
|
||||
return (
|
||||
<React.Fragment key={`section-${section.order}`}>
|
||||
<List>
|
||||
{section.items
|
||||
.sort((i1, i2) => i1.order - i2.order)
|
||||
.map(item => (
|
||||
<MenuItem
|
||||
key={`${item.code}-${index}`}
|
||||
open={open}
|
||||
icon={item.icon}
|
||||
label={t(item.name)}
|
||||
onClick={item.subMenus ? undefined : () => navigate(item.route)}
|
||||
subMenus={item.subMenus?.map(si => ({
|
||||
open,
|
||||
icon: si.icon,
|
||||
label: t(si.name),
|
||||
onClick: () => navigate(si.route)
|
||||
}))}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
{!isLast && <Divider />}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideBar;
|
|
@ -1,162 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import clsx from "clsx";
|
||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
||||
import {
|
||||
Drawer,
|
||||
List,
|
||||
Divider,
|
||||
IconButton,
|
||||
ListItemIcon,
|
||||
ListItemText
|
||||
} from "@material-ui/core";
|
||||
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
|
||||
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import BuildIcon from "@material-ui/icons/Build";
|
||||
import DnsIcon from "@material-ui/icons/Dns";
|
||||
import DeviceHubIcon from "@material-ui/icons/DeviceHub";
|
||||
import SettingsIcon from "@material-ui/icons/Settings";
|
||||
import DashboardIcon from "@material-ui/icons/Dashboard";
|
||||
import FeaturedPlayListIcon from "@material-ui/icons/FeaturedPlayList";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styles from "./styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const menu = [
|
||||
{
|
||||
order: 0,
|
||||
items: [
|
||||
{
|
||||
code: "dashboard",
|
||||
name: "Menu.Dashboard",
|
||||
route: "/dashboard",
|
||||
icon: <DashboardIcon />,
|
||||
order: 0
|
||||
},
|
||||
{
|
||||
code: "machines",
|
||||
name: "Menu.Machines",
|
||||
route: "/machines",
|
||||
icon: <DnsIcon />,
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
code: "system",
|
||||
name: "Menu.System",
|
||||
route: "/system",
|
||||
icon: <DeviceHubIcon />,
|
||||
order: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
order: 1,
|
||||
items: [
|
||||
{
|
||||
code: "administration",
|
||||
name: "Menu.Administration",
|
||||
route: "/administration",
|
||||
icon: <BuildIcon />,
|
||||
order: 0
|
||||
},
|
||||
{
|
||||
code: "settings",
|
||||
name: "Menu.Settings",
|
||||
route: "/settings",
|
||||
icon: <SettingsIcon />,
|
||||
order: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
items: [
|
||||
{
|
||||
code: "about",
|
||||
name: "Menu.About",
|
||||
route: "/about",
|
||||
icon: <FeaturedPlayListIcon />,
|
||||
order: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const sortedMenu = menu.sort((i1, i2) => i1 - i2);
|
||||
|
||||
const Sidebar = ({ open, handleDrawerClose }) => {
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const history = useHistory();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClick = route => () => {
|
||||
setSelected(route);
|
||||
history.push(route);
|
||||
};
|
||||
|
||||
const isSelected = key => selected === key;
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
className={clsx(classes.drawer, {
|
||||
[classes.drawerOpen]: open,
|
||||
[classes.drawerClose]: !open
|
||||
})}
|
||||
classes={{
|
||||
paper: clsx({
|
||||
[classes.drawerOpen]: open,
|
||||
[classes.drawerClose]: !open
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={classes.toolbar}>
|
||||
<IconButton onClick={handleDrawerClose}>
|
||||
{theme.direction === "rtl" ? (
|
||||
<ChevronRightIcon />
|
||||
) : (
|
||||
<ChevronLeftIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
{sortedMenu.map((menu, index) => {
|
||||
const isLast = index === sortedMenu.length - 1;
|
||||
return (
|
||||
<React.Fragment key={`menu-${menu.order}`}>
|
||||
<List>
|
||||
{menu.items
|
||||
.sort((i1, i2) => i1 - i2)
|
||||
.map(item => (
|
||||
<ListItem
|
||||
button
|
||||
key={item.code}
|
||||
onClick={handleClick(item.route)}
|
||||
selected={isSelected(item.route)}
|
||||
>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={t(item.name)} />
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
{!isLast && <Divider />}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
Sidebar.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
handleDrawerClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Sidebar;
|
|
@ -1,52 +0,0 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import clsx from "clsx";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { AppBar, Toolbar, Typography, IconButton } from "@material-ui/core";
|
||||
import MenuIcon from "@material-ui/icons/Menu";
|
||||
import ProfileButton from "./ProfileButton";
|
||||
import LightDarkToggle from "./LightDarkToggle";
|
||||
import SensitiveInfoToggle from "./SensitiveInfoToggle";
|
||||
import styles from "./styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const TopBar = ({ open, handleDrawerOpen }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
position="fixed"
|
||||
className={clsx(classes.appBar, {
|
||||
[classes.appBarShift]: open
|
||||
})}
|
||||
>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
onClick={handleDrawerOpen}
|
||||
edge="start"
|
||||
className={clsx(classes.menuButton, {
|
||||
[classes.hide]: open
|
||||
})}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap className={classes.title}>
|
||||
Network resurrector
|
||||
</Typography>
|
||||
<SensitiveInfoToggle />
|
||||
<LightDarkToggle />
|
||||
<ProfileButton />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
};
|
||||
|
||||
TopBar.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
handleDrawerOpen: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default TopBar;
|
|
@ -0,0 +1,72 @@
|
|||
import React from "react";
|
||||
import { Toolbar, Typography, IconButton, Box } from "@mui/material";
|
||||
import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import ProfileButton from "./ProfileButton";
|
||||
import LightDarkToggle from "./LightDarkToggle";
|
||||
import SensitiveInfoToggle from "./SensitiveInfoToggle";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import { drawerWidth } from "./constants";
|
||||
|
||||
interface AppBarProps extends MuiAppBarProps {
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
const AppBar = styled(MuiAppBar, {
|
||||
shouldForwardProp: prop => prop !== "open"
|
||||
})<AppBarProps>(({ theme, open }) => ({
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
...(open && {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
})
|
||||
})
|
||||
}));
|
||||
|
||||
type TopBarProps = {
|
||||
open: boolean;
|
||||
onDrawerOpen: () => void;
|
||||
};
|
||||
|
||||
const title = "Network resurrector";
|
||||
const TopBar: React.FC<TopBarProps> = ({ open, onDrawerOpen }) => {
|
||||
// const { userInfo } = useTuitioUserInfo();
|
||||
// to do: Avatar: userInfo.profilePictureUrl
|
||||
return (
|
||||
<AppBar position="fixed" open={open}>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="open drawer"
|
||||
onClick={onDrawerOpen}
|
||||
edge="start"
|
||||
sx={{
|
||||
marginRight: 5,
|
||||
...(open && { display: "none" })
|
||||
}}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
{title}
|
||||
</Typography>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 1 }}>
|
||||
<SensitiveInfoToggle />
|
||||
<LightDarkToggle />
|
||||
<ProfileButton />
|
||||
</Box>
|
||||
</Toolbar>
|
||||
{/* <ProgressBar /> */}
|
||||
</AppBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopBar;
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./menu";
|
||||
|
||||
export const drawerWidth = 240;
|
|
@ -0,0 +1,98 @@
|
|||
import React from "react";
|
||||
import { Dashboard, Dns, DeviceHub, Build, Settings, Info } from "../../icons";
|
||||
|
||||
type MenuItem = {
|
||||
code: string;
|
||||
name: string;
|
||||
route: string;
|
||||
icon: JSX.Element;
|
||||
order: number;
|
||||
subMenus?: MenuItem[];
|
||||
};
|
||||
|
||||
type MenuSection = {
|
||||
order: number;
|
||||
items: MenuItem[];
|
||||
};
|
||||
|
||||
type Menu = MenuSection[];
|
||||
|
||||
const menu: Menu = [
|
||||
{
|
||||
order: 0,
|
||||
items: [
|
||||
{
|
||||
code: "dashboard",
|
||||
name: "Menu.Dashboard",
|
||||
route: "/dashboard",
|
||||
icon: <Dashboard />,
|
||||
order: 0
|
||||
},
|
||||
{
|
||||
code: "machines",
|
||||
name: "Menu.Machines",
|
||||
route: "/machines",
|
||||
icon: <Dns />,
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
code: "system",
|
||||
name: "Menu.System",
|
||||
route: "/system",
|
||||
icon: <DeviceHub />,
|
||||
order: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
order: 1,
|
||||
items: [
|
||||
{
|
||||
code: "administration",
|
||||
name: "Menu.Administration",
|
||||
route: "/administration",
|
||||
icon: <Build />,
|
||||
order: 0,
|
||||
subMenus: [
|
||||
{
|
||||
code: "machines",
|
||||
name: "Menu.Machines",
|
||||
route: "/administration/machines",
|
||||
icon: <Build />,
|
||||
order: 0
|
||||
},
|
||||
{
|
||||
code: "agents",
|
||||
name: "Menu.Agents",
|
||||
route: "/administration/agents",
|
||||
icon: <Build />,
|
||||
order: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
code: "settings",
|
||||
name: "Menu.Settings",
|
||||
route: "/settings",
|
||||
icon: <Settings />,
|
||||
order: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
items: [
|
||||
{
|
||||
code: "about",
|
||||
name: "Menu.About",
|
||||
route: "/about",
|
||||
icon: <Info />,
|
||||
order: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export type { MenuItem, MenuSection, Menu };
|
||||
export { menu };
|
||||
export default menu;
|
|
@ -1,71 +0,0 @@
|
|||
const drawerWidth = 240;
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: "flex"
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
})
|
||||
},
|
||||
appBarShift: {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
})
|
||||
},
|
||||
menuButton: {
|
||||
marginRight: 36
|
||||
},
|
||||
hide: {
|
||||
display: "none"
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
whiteSpace: "nowrap"
|
||||
},
|
||||
drawerOpen: {
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen
|
||||
})
|
||||
},
|
||||
drawerClose: {
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
}),
|
||||
overflowX: "hidden",
|
||||
width: theme.spacing(7) + 1,
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
width: theme.spacing(9) + 1
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: theme.spacing(0, 1),
|
||||
// necessary for content to be below app bar
|
||||
...theme.mixins.toolbar
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(2)
|
||||
},
|
||||
menuItemIcon: {
|
||||
minWidth: "26px"
|
||||
}
|
||||
});
|
||||
|
||||
export default styles;
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState, useMemo } from "react";
|
||||
import PageTitle from "../../components/common/PageTitle";
|
||||
import BubbleChartIcon from "@material-ui/icons/BubbleChart";
|
||||
import NotesIcon from "@material-ui/icons/Notes";
|
||||
import TimelineIcon from "@material-ui/icons/Timeline";
|
||||
import BubbleChartIcon from "@mui/icons-material/BubbleChart";
|
||||
import NotesIcon from "@mui/icons-material/Notes";
|
||||
import TimelineIcon from "@mui/icons-material/Timeline";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AboutSystemContainer from "./system/AboutSystemContainer";
|
||||
import ReleaseNotesContainer from "./releaseNotes/ReleaseNotesContainer";
|
||||
|
@ -33,24 +33,14 @@ const AboutContainer = () => {
|
|||
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigationTabs = useMemo(
|
||||
() => tabs.map(z => ({ ...z, tooltip: t(z.code) })),
|
||||
[t]
|
||||
);
|
||||
const navigationTabs = useMemo(() => tabs.map(z => ({ ...z, tooltip: t(z.code) })), [t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle
|
||||
text={t(tab)}
|
||||
navigation={
|
||||
<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />
|
||||
}
|
||||
/>
|
||||
<PageTitle text={t(tab)} navigation={<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />} />
|
||||
{tab === NavigationTabs.SYSTEM && <AboutSystemContainer />}
|
||||
{tab === NavigationTabs.RELEASE_NOTES && <ReleaseNotesContainer />}
|
||||
{tab === NavigationTabs.TIMELINE && (
|
||||
<ReleaseNotesContainer view="timeline" />
|
||||
)}
|
||||
{tab === NavigationTabs.TIMELINE && <ReleaseNotesContainer view="timeline" />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
const ReleaseNote = ({ releaseNote }) => {
|
||||
return (
|
||||
<div>
|
||||
{releaseNote.notes.map(note => {
|
||||
return (
|
||||
<Typography
|
||||
key={releaseNote.notes.indexOf(note)}
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
>
|
||||
<Typography key={releaseNote.notes.indexOf(note)} variant="body2" gutterBottom>
|
||||
{note}
|
||||
</Typography>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Grid, Typography } from "@material-ui/core";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const ReleaseNoteSummary = ({ releaseNote, collapsed }) => {
|
||||
|
|
|
@ -4,8 +4,7 @@ import ReleaseNotesList from "./ReleaseNotesList";
|
|||
import TimelineComponent from "../timeline/TimelineComponent";
|
||||
import { routes, get } from "../../../utils/api";
|
||||
|
||||
const sort = releases =>
|
||||
releases.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
const sort = releases => releases.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
const ReleaseNotesContainer = ({ view }) => {
|
||||
const [state, setState] = useState({ data: [], loaded: false });
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails
|
||||
} from "@material-ui/core";
|
||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||
import { Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import ReleaseNoteSummary from "./ReleaseNoteSummary";
|
||||
import ReleaseNote from "./ReleaseNote";
|
||||
|
||||
|
@ -29,18 +25,9 @@ const ReleaseNotesList = ({ releases }) => {
|
|||
<>
|
||||
{releases.map(release => {
|
||||
return (
|
||||
<Accordion
|
||||
key={release.version}
|
||||
onChange={handleToggle(release.version)}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
id={`panel-${release.version}-header`}
|
||||
>
|
||||
<ReleaseNoteSummary
|
||||
releaseNote={release}
|
||||
collapsed={isCollapsed(release.version)}
|
||||
/>
|
||||
<Accordion key={release.version} onChange={handleToggle(release.version)}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />} id={`panel-${release.version}-header`}>
|
||||
<ReleaseNoteSummary releaseNote={release} collapsed={isCollapsed(release.version)} />
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<ReleaseNote releaseNote={release} />
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import CardActions from "@material-ui/core/CardActions";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Card from "@mui/material/Card";
|
||||
import CardActions from "@mui/material/CardActions";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import OpenInNewIcon from "@material-ui/icons/OpenInNew";
|
||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const styles = {
|
||||
bullet: {
|
||||
display: "inline-block",
|
||||
margin: "0 4px",
|
||||
transform: "scale(1.5)"
|
||||
},
|
||||
service: {
|
||||
marginTop: theme.spacing(1)
|
||||
marginTop: 1
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
code: "About.System.Services.Frontend",
|
||||
url: "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector-frontend"
|
||||
url: "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector/src/branch/master/frontend"
|
||||
},
|
||||
{
|
||||
code: "About.System.Services.Api",
|
||||
|
@ -40,10 +39,9 @@ const buttons = [
|
|||
];
|
||||
|
||||
const AboutSystemComponent = ({ handleOpenInNewTab }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const bullet = <span className={classes.bullet}>•</span>;
|
||||
const bullet = <span style={styles.bullet}>•</span>;
|
||||
|
||||
return (
|
||||
<Card variant="outlined">
|
||||
|
@ -51,30 +49,26 @@ const AboutSystemComponent = ({ handleOpenInNewTab }) => {
|
|||
<Typography variant="h5" gutterBottom>
|
||||
{t("About.System.Description.Title")}
|
||||
</Typography>
|
||||
<Typography color="textSecondary">
|
||||
{t("About.System.Description.FirstPhrase")}
|
||||
</Typography>
|
||||
<Typography color="textSecondary">
|
||||
{t("About.System.Description.SecondPhrase")}
|
||||
</Typography>
|
||||
<Typography color="textSecondary">{t("About.System.Description.FirstPhrase")}</Typography>
|
||||
<Typography color="textSecondary">{t("About.System.Description.SecondPhrase")}</Typography>
|
||||
|
||||
<Typography className={classes.service} color="textSecondary">
|
||||
<Typography sx={styles.service} color="textSecondary">
|
||||
{bullet}
|
||||
{t("About.System.Description.Frontend")}
|
||||
</Typography>
|
||||
<Typography className={classes.service} color="textSecondary">
|
||||
<Typography sx={styles.service} color="textSecondary">
|
||||
{bullet}
|
||||
{t("About.System.Description.Api")}
|
||||
</Typography>
|
||||
<Typography className={classes.service} color="textSecondary">
|
||||
<Typography sx={styles.service} color="textSecondary">
|
||||
{bullet}
|
||||
{t("About.System.Description.Server")}
|
||||
</Typography>
|
||||
<Typography className={classes.service} color="textSecondary">
|
||||
<Typography sx={styles.service} color="textSecondary">
|
||||
{bullet}
|
||||
{t("About.System.Description.Agent")}
|
||||
</Typography>
|
||||
<Typography className={classes.service} color="textSecondary">
|
||||
<Typography sx={styles.service} color="textSecondary">
|
||||
{bullet}
|
||||
{t("About.System.Description.Tuitio")}
|
||||
</Typography>
|
||||
|
|
|
@ -1,35 +1,32 @@
|
|||
import React from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Box } from "@mui/material";
|
||||
import AboutSystemComponent from "./AboutSystemComponent";
|
||||
import SystemVersionContainer from "./SystemVersionContainer";
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
page: {
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
},
|
||||
element: {
|
||||
marginTop: theme.spacing(1)
|
||||
}
|
||||
};
|
||||
});
|
||||
const styles = {
|
||||
page: {
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
},
|
||||
element: {
|
||||
marginTop: 1
|
||||
}
|
||||
};
|
||||
|
||||
const AboutSystemContainer = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
const handleOpenInNewTab = url => event => {
|
||||
window.open(url, "_blank");
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.page}>
|
||||
<Box sx={styles.page}>
|
||||
<AboutSystemComponent handleOpenInNewTab={handleOpenInNewTab} />
|
||||
<div className={classes.element}>
|
||||
<Box sx={styles.element}>
|
||||
<SystemVersionContainer />
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutSystemContainer;
|
||||
|
|
|
@ -1,50 +1,44 @@
|
|||
import React, { useMemo, useEffect, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemAvatar
|
||||
} from "@material-ui/core";
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import WebAssetIcon from "@material-ui/icons/WebAsset";
|
||||
import DeveloperBoardIcon from "@material-ui/icons/DeveloperBoard";
|
||||
import SettingsInputSvideoIcon from "@material-ui/icons/SettingsInputSvideo";
|
||||
import { List, ListItem, ListItemText, ListItemAvatar } from "@mui/material";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import WebAssetIcon from "@mui/icons-material/WebAsset";
|
||||
import DeveloperBoardIcon from "@mui/icons-material/DeveloperBoard";
|
||||
import SettingsInputSvideoIcon from "@mui/icons-material/SettingsInputSvideo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import packageData from "../../../../package.json";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
horizontally: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
padding: 0
|
||||
},
|
||||
vertical: {
|
||||
width: "100%"
|
||||
},
|
||||
value: {
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: theme.typography.fontWeightMedium
|
||||
},
|
||||
versionAvatar: {
|
||||
backgroundColor: theme.palette.secondary.main
|
||||
}
|
||||
};
|
||||
const getStyles = theme => ({
|
||||
horizontally: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
padding: 0
|
||||
},
|
||||
vertical: {
|
||||
width: "100%"
|
||||
},
|
||||
value: {
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: theme.typography.fontWeightMedium
|
||||
},
|
||||
versionAvatar: {
|
||||
backgroundColor: theme.palette.secondary.main
|
||||
}
|
||||
});
|
||||
|
||||
const SystemVersionComponent = ({ data }) => {
|
||||
const classes = useStyles();
|
||||
const [listClass, setListClass] = useState(classes.horizontally);
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
const [listClass, setListClass] = useState(styles.horizontally);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia("(max-width: 800px)");
|
||||
|
||||
function handleMatches(event) {
|
||||
const cssClass = event.matches ? classes.vertical : classes.horizontally;
|
||||
const cssClass = event.matches ? styles.vertical : styles.horizontally;
|
||||
setListClass(cssClass);
|
||||
}
|
||||
|
||||
|
@ -54,7 +48,7 @@ const SystemVersionComponent = ({ data }) => {
|
|||
return () => {
|
||||
mediaQuery.removeListener(handleMatches);
|
||||
};
|
||||
}, [classes.horizontally, classes.vertical]);
|
||||
}, [styles.horizontally, styles.vertical]);
|
||||
|
||||
const lastReleaseDate = useMemo(() => {
|
||||
const format = "DD-MM-YYYY HH:mm:ss";
|
||||
|
@ -84,16 +78,16 @@ const SystemVersionComponent = ({ data }) => {
|
|||
|
||||
return (
|
||||
<Paper variant="outlined">
|
||||
<List className={listClass}>
|
||||
<List sx={listClass}>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar className={classes.versionAvatar}>
|
||||
<Avatar sx={styles.versionAvatar}>
|
||||
<DeveloperBoardIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<span className={classes.value}>
|
||||
<span style={styles.value}>
|
||||
{t("About.System.Version.Server", {
|
||||
version: data.server.version
|
||||
})}
|
||||
|
@ -106,13 +100,13 @@ const SystemVersionComponent = ({ data }) => {
|
|||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar className={classes.versionAvatar}>
|
||||
<Avatar sx={styles.versionAvatar}>
|
||||
<SettingsInputSvideoIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<span className={classes.value}>
|
||||
<span style={styles.value}>
|
||||
{t("About.System.Version.Api", {
|
||||
version: data.api.version
|
||||
})}
|
||||
|
@ -125,13 +119,13 @@ const SystemVersionComponent = ({ data }) => {
|
|||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar className={classes.versionAvatar}>
|
||||
<Avatar sx={styles.versionAvatar}>
|
||||
<WebAssetIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<span className={classes.value}>
|
||||
<span style={styles.value}>
|
||||
{t("About.System.Version.Frontend", {
|
||||
version: process.env.APP_VERSION ?? packageData.version
|
||||
})}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Timeline from "@material-ui/lab/Timeline";
|
||||
import TimelineItem from "@material-ui/lab/TimelineItem";
|
||||
import TimelineSeparator from "@material-ui/lab/TimelineSeparator";
|
||||
import TimelineConnector from "@material-ui/lab/TimelineConnector";
|
||||
import TimelineContent from "@material-ui/lab/TimelineContent";
|
||||
import TimelineOppositeContent from "@material-ui/lab/TimelineOppositeContent";
|
||||
import TimelineDot from "@material-ui/lab/TimelineDot";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Timeline from "@mui/lab/Timeline";
|
||||
import TimelineItem from "@mui/lab/TimelineItem";
|
||||
import TimelineSeparator from "@mui/lab/TimelineSeparator";
|
||||
import TimelineConnector from "@mui/lab/TimelineConnector";
|
||||
import TimelineContent from "@mui/lab/TimelineContent";
|
||||
import TimelineOppositeContent from "@mui/lab/TimelineOppositeContent";
|
||||
import TimelineDot from "@mui/lab/TimelineDot";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getRandomElement } from "../../../utils";
|
||||
import {
|
||||
Announcement,
|
||||
AmpStories,
|
||||
Book,
|
||||
Apps,
|
||||
BugReport,
|
||||
DeviceHub,
|
||||
|
@ -29,11 +28,11 @@ import {
|
|||
Star,
|
||||
Whatshot,
|
||||
Widgets
|
||||
} from "@material-ui/icons";
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const timelineIcons = [
|
||||
Announcement,
|
||||
AmpStories,
|
||||
Book,
|
||||
Apps,
|
||||
BugReport,
|
||||
DeviceHub,
|
||||
|
@ -55,14 +54,7 @@ const timelineDotVariants = [
|
|||
{ color: "secondary", variant: "outlined" }
|
||||
];
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
paper: {
|
||||
padding: "6px 16px"
|
||||
}
|
||||
}));
|
||||
|
||||
const TimelineComponent = ({ releases }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const _releases = releases.map((release, index) => {
|
||||
|
@ -73,7 +65,7 @@ const TimelineComponent = ({ releases }) => {
|
|||
});
|
||||
|
||||
return (
|
||||
<Timeline align="alternate">
|
||||
<Timeline position="alternate">
|
||||
{_releases.map(release => (
|
||||
<TimelineItem key={release.version}>
|
||||
<TimelineOppositeContent>
|
||||
|
@ -84,16 +76,18 @@ const TimelineComponent = ({ releases }) => {
|
|||
</Typography>
|
||||
</TimelineOppositeContent>
|
||||
<TimelineSeparator>
|
||||
<TimelineDot
|
||||
color={release.dot.color}
|
||||
variant={release.dot.variant}
|
||||
>
|
||||
<TimelineDot color={release.dot.color} variant={release.dot.variant}>
|
||||
<release.icon />
|
||||
</TimelineDot>
|
||||
{!release.isLast && <TimelineConnector />}
|
||||
</TimelineSeparator>
|
||||
<TimelineContent>
|
||||
<Paper elevation={3} className={classes.paper}>
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
padding: "6px 16px"
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="h1">
|
||||
{release.notes[0]}
|
||||
</Typography>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import React from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||
import styles from "../styles";
|
||||
import { Alert, AlertTitle } from "@mui/material";
|
||||
import { getStyles } from "../styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
export default function GuestAnnouncement() {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={classes.alert}>
|
||||
<div style={styles.alert}>
|
||||
<Alert variant="outlined" severity="warning">
|
||||
<AlertTitle>{t("Dashboard.Announcements.Guest.Title")}</AlertTitle>
|
||||
{t("Dashboard.Announcements.Guest.Message")}
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import React from "react";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||
import styles from "../styles";
|
||||
import { Alert, AlertTitle } from "@mui/material";
|
||||
import { getStyles } from "../styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTuitioUser } from "@flare/tuitio-client-react";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
export default function UserAnnouncement() {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const { userName } = useTuitioUser();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={classes.alert}>
|
||||
<div style={styles.alert}>
|
||||
<Alert variant="outlined" severity="info">
|
||||
<AlertTitle>
|
||||
{t("Dashboard.Announcements.User.Title", {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const styles = theme => ({
|
||||
const getStyles = theme => ({
|
||||
alert: {
|
||||
width: "100%",
|
||||
"& > * + *": {
|
||||
|
@ -7,4 +7,4 @@ const styles = theme => ({
|
|||
}
|
||||
});
|
||||
|
||||
export default styles;
|
||||
export { getStyles };
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Card } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Card } from "@mui/material";
|
||||
import styles from "../styles";
|
||||
import LoginComponent from "./LoginComponent";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const LoginCard = ({ credentials, onChange, onLogin }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.appLogin}>
|
||||
<div style={styles.appLogin}>
|
||||
<Card variant="outlined">
|
||||
<LoginComponent
|
||||
credentials={credentials}
|
||||
onChange={onChange}
|
||||
onLogin={onLogin}
|
||||
/>
|
||||
<LoginComponent credentials={credentials} onChange={onChange} onLogin={onLogin} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,29 +1,19 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
TextField,
|
||||
InputAdornment,
|
||||
Button,
|
||||
CardActions,
|
||||
CardContent
|
||||
} from "@material-ui/core";
|
||||
import { AccountCircleOutlined } from "@material-ui/icons";
|
||||
import { TextField, InputAdornment, Button, CardActions, CardContent } from "@mui/material";
|
||||
import { AccountCircleOutlined } from "@mui/icons-material";
|
||||
import PasswordField from "../../../components/common/inputs/PasswordField";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styles from "../styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<CardContent>
|
||||
<TextField
|
||||
className={classes.field}
|
||||
sx={styles.field}
|
||||
id="username"
|
||||
label={t("Login.Username")}
|
||||
onChange={onChange("userName")}
|
||||
|
@ -40,7 +30,7 @@ const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
|||
<PasswordField
|
||||
id="password"
|
||||
label={t("Login.Password")}
|
||||
className={classes.field}
|
||||
sx={styles.field}
|
||||
onChange={onChange("password")}
|
||||
value={credentials.password}
|
||||
onKeyDown={e => {
|
||||
|
@ -48,13 +38,8 @@ const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
|||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardActions className={classes.actions}>
|
||||
<Button
|
||||
className={classes.onRight}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={onLogin}
|
||||
>
|
||||
<CardActions sx={styles.actions}>
|
||||
<Button sx={styles.onRight} variant="contained" color="primary" onClick={onLogin}>
|
||||
{t("Login.Label")}
|
||||
</Button>
|
||||
</CardActions>
|
||||
|
|
|
@ -26,13 +26,7 @@ const LoginContainer = () => {
|
|||
return login(userName, password);
|
||||
};
|
||||
|
||||
return (
|
||||
<LoginCard
|
||||
credentials={credentials}
|
||||
onChange={handleChange}
|
||||
onLogin={handleLogin}
|
||||
/>
|
||||
);
|
||||
return <LoginCard credentials={credentials} onChange={handleChange} onLogin={handleLogin} />;
|
||||
};
|
||||
|
||||
export default LoginContainer;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const styles = theme => ({
|
||||
const styles = {
|
||||
onRight: {
|
||||
marginLeft: "auto"
|
||||
},
|
||||
|
@ -9,13 +9,13 @@ const styles = theme => ({
|
|||
alignItems: "center"
|
||||
},
|
||||
field: {
|
||||
margin: theme.spacing(1),
|
||||
margin: 1,
|
||||
width: "300px"
|
||||
},
|
||||
actions: {
|
||||
paddingRight: "16px",
|
||||
paddingLeft: "16px"
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default styles;
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Grid
|
||||
} from "@material-ui/core";
|
||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import MachineCollapsedContent from "./common/MachineCollapsedContent";
|
||||
import { DataLabel } from "../../../components/common";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSensitiveInfo } from "../../../hooks";
|
||||
import ActionsGroup from "./common/ActionsGroup";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
panel: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}
|
||||
}));
|
||||
|
||||
const IconLeftAccordionSummary = withStyles(theme => ({
|
||||
root: {
|
||||
minHeight: "20px",
|
||||
height: "42px",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
height: "62px"
|
||||
},
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
height: "102px"
|
||||
}
|
||||
},
|
||||
expandIcon: {
|
||||
order: -1
|
||||
}
|
||||
}))(AccordionSummary);
|
||||
|
||||
const GridCell = ({ label, value }) => {
|
||||
const { mask } = useSensitiveInfo();
|
||||
return (
|
||||
<Grid item xs={12} md={6} lg={3}>
|
||||
<DataLabel label={label} data={mask(value)} />
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
GridCell.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
const MachineAccordion = ({ machine, actions, logs, addLog }) => {
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Accordion>
|
||||
<IconLeftAccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-label="Expand"
|
||||
aria-controls="additional-actions1-content"
|
||||
id="additional-actions1-header"
|
||||
IconButtonProps={{ edge: "start" }}
|
||||
>
|
||||
<Grid container className={classes.panel}>
|
||||
<Grid item xs={11}>
|
||||
<Grid container>
|
||||
<GridCell
|
||||
label={t("Machine.FullName")}
|
||||
value={machine.fullMachineName}
|
||||
/>
|
||||
<GridCell label={t("Machine.Name")} value={machine.machineName} />
|
||||
<GridCell label={t("Machine.IP")} value={machine.iPv4Address} />
|
||||
<GridCell label={t("Machine.MAC")} value={machine.macAddress} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={1} style={{ textAlign: "right" }}>
|
||||
<ActionsGroup
|
||||
className={classes.actions}
|
||||
machine={machine}
|
||||
actions={actions}
|
||||
addLog={addLog}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</IconLeftAccordionSummary>
|
||||
<AccordionDetails>
|
||||
<MachineCollapsedContent
|
||||
description={machine.description}
|
||||
logs={logs}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
MachineAccordion.propTypes = {
|
||||
machine: PropTypes.shape({
|
||||
machineId: PropTypes.number.isRequired,
|
||||
machineName: PropTypes.string.isRequired,
|
||||
fullMachineName: PropTypes.string.isRequired,
|
||||
macAddress: PropTypes.string.isRequired,
|
||||
iPv4Address: PropTypes.string,
|
||||
description: PropTypes.string
|
||||
}).isRequired,
|
||||
actions: PropTypes.array.isRequired,
|
||||
logs: PropTypes.array.isRequired,
|
||||
addLog: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default MachineAccordion;
|
|
@ -0,0 +1,106 @@
|
|||
import React from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import MuiAccordion, { AccordionProps } from "@mui/material/Accordion";
|
||||
import MuiAccordionSummary, { AccordionSummaryProps } from "@mui/material/AccordionSummary";
|
||||
import MuiAccordionDetails from "@mui/material/AccordionDetails";
|
||||
import { models } from "types";
|
||||
import { Grid } from "@mui/material";
|
||||
import ActionsGroup from "./common/ActionsGroup";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSensitiveInfo } from "hooks";
|
||||
import { DataLabel } from "components/common";
|
||||
import MachineCollapsedContent from "./common/MachineCollapsedContent";
|
||||
|
||||
const Accordion = styled((props: AccordionProps) => <MuiAccordion disableGutters elevation={0} square {...props} />)(
|
||||
({ theme }) => ({
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
"&:not(:last-child)": {
|
||||
borderBottom: 0
|
||||
},
|
||||
"&::before": {
|
||||
display: "none"
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const AccordionSummary = styled((props: AccordionSummaryProps) => (
|
||||
<MuiAccordionSummary expandIcon={<ExpandMoreIcon />} {...props} />
|
||||
))(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === "dark" ? "rgba(255, 255, 255, .05)" : "rgba(0, 0, 0, .03)",
|
||||
flexDirection: "row-reverse",
|
||||
"& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": {
|
||||
transform: "rotate(180deg)"
|
||||
},
|
||||
"& .MuiAccordionSummary-content": {
|
||||
marginLeft: theme.spacing(1)
|
||||
},
|
||||
minHeight: "20px",
|
||||
height: "42px",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
height: "62px"
|
||||
},
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
height: "102px"
|
||||
}
|
||||
}));
|
||||
|
||||
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
||||
padding: theme.spacing(1),
|
||||
borderTop: "1px solid rgba(0, 0, 0, .125)"
|
||||
}));
|
||||
|
||||
type GridCellProps = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const GridCell: React.FC<GridCellProps> = ({ label, value }) => {
|
||||
const { mask } = useSensitiveInfo();
|
||||
return (
|
||||
<Grid item xs={12} md={6} lg={3}>
|
||||
<DataLabel label={label} data={mask(value)} />
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
machine: models.Machine;
|
||||
actions: Array<any>; // Replace any with the actual type of the actions
|
||||
logs: Array<any>; // Replace any with the actual type of the logs
|
||||
addLog: () => void; // Replace with the actual function signature
|
||||
};
|
||||
|
||||
const MachineAccordion: React.FC<Props> = ({ machine, actions, logs, addLog }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary aria-controls={`machine-${machine.machineId}-summary`} id={`machine-${machine.machineId}`}>
|
||||
<Grid
|
||||
container
|
||||
sx={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Grid item xs={11}>
|
||||
<Grid container>
|
||||
<GridCell label={t("Machine.FullName")} value={machine.fullMachineName} />
|
||||
<GridCell label={t("Machine.Name")} value={machine.machineName} />
|
||||
<GridCell label={t("Machine.IP")} value={machine.iPv4Address || ""} />
|
||||
<GridCell label={t("Machine.MAC")} value={machine.macAddress} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={1} style={{ textAlign: "right" }}>
|
||||
<ActionsGroup machine={machine} actions={actions} addLog={addLog} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<MachineCollapsedContent description={machine.description} logs={logs} style={{ width: "100%" }} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default MachineAccordion;
|
|
@ -4,7 +4,7 @@ import MachineTableRow from "./MachineTableRow";
|
|||
import MachineAccordion from "./MachineAccordion";
|
||||
import { ViewModes } from "./ViewModeSelection";
|
||||
import { useToast } from "../../../hooks";
|
||||
import { LastPage, RotateLeft, Launch, Stop } from "@material-ui/icons";
|
||||
import { LastPage, RotateLeft, Launch, Stop } from "@mui/icons-material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { routes, post } from "../../../utils/api";
|
||||
|
||||
|
@ -96,7 +96,9 @@ const MachineContainer = ({ machine, viewMode }) => {
|
|||
},
|
||||
{
|
||||
code: "advanced",
|
||||
effect: () => {},
|
||||
effect: () => {
|
||||
// to do: implement
|
||||
},
|
||||
icon: Launch,
|
||||
tooltip: t("Machine.Actions.Advanced"),
|
||||
main: false
|
||||
|
@ -106,20 +108,10 @@ const MachineContainer = ({ machine, viewMode }) => {
|
|||
return (
|
||||
<>
|
||||
{viewMode === ViewModes.TABLE && (
|
||||
<MachineTableRow
|
||||
machine={machine}
|
||||
actions={actions}
|
||||
logs={logs}
|
||||
addLog={addLog}
|
||||
/>
|
||||
<MachineTableRow machine={machine} actions={actions} logs={logs} addLog={addLog} />
|
||||
)}
|
||||
{viewMode === ViewModes.ACCORDION && (
|
||||
<MachineAccordion
|
||||
machine={machine}
|
||||
actions={actions}
|
||||
logs={logs}
|
||||
addLog={addLog}
|
||||
/>
|
||||
<MachineAccordion machine={machine} actions={actions} logs={logs} addLog={addLog} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,34 +1,26 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { TableCell, TableRow, IconButton, Collapse } from "@material-ui/core";
|
||||
import { KeyboardArrowDown, KeyboardArrowUp } from "@material-ui/icons";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { TableCell, TableRow, IconButton, Collapse } from "@mui/material";
|
||||
import { KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material";
|
||||
import MachineCollapsedContent from "./common/MachineCollapsedContent";
|
||||
import { useSensitiveInfo } from "../../../hooks";
|
||||
import ActionsGroup from "./common/ActionsGroup";
|
||||
|
||||
const useRowStyles = makeStyles({
|
||||
root: {
|
||||
"& > *": {
|
||||
borderBottom: "unset"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const MachineTableRow = ({ machine, actions, logs, addLog }) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useRowStyles();
|
||||
const { mask } = useSensitiveInfo();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow className={classes.root}>
|
||||
<TableRow
|
||||
sx={{
|
||||
"& .MuiTableCell-root": {
|
||||
borderBottom: "unset"
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
aria-label="expand row"
|
||||
size="small"
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
|
||||
{open ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
|
@ -48,7 +40,7 @@ const MachineTableRow = ({ machine, actions, logs, addLog }) => {
|
|||
<MachineCollapsedContent
|
||||
description={machine.description}
|
||||
logs={logs}
|
||||
style={{ paddingBottom: "10px" }}
|
||||
style={{ paddingBottom: "0.5rem" }}
|
||||
/>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import React, { useContext, useEffect, useCallback, useState } from "react";
|
||||
import {
|
||||
NetworkStateContext,
|
||||
NetworkDispatchContext
|
||||
} from "../../network/state/contexts";
|
||||
import { NetworkStateContext, NetworkDispatchContext } from "../../network/state/contexts";
|
||||
import MachinesListComponent from "./MachinesListComponent";
|
||||
import PageTitle from "../../../components/common/PageTitle";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
@ -35,19 +32,9 @@ const MachinesContainer = () => {
|
|||
<>
|
||||
<PageTitle
|
||||
text={t("Menu.Machines")}
|
||||
toolBar={
|
||||
<ViewModeSelection
|
||||
callback={setViewMode}
|
||||
initialMode={ViewModes.TABLE}
|
||||
/>
|
||||
}
|
||||
toolBar={<ViewModeSelection callback={setViewMode} initialMode={ViewModes.TABLE} />}
|
||||
/>
|
||||
{viewMode && (
|
||||
<MachinesListComponent
|
||||
machines={state.network.machines}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
)}
|
||||
{viewMode && <MachinesListComponent machines={state.network.machines} viewMode={viewMode} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow
|
||||
} from "@material-ui/core";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import MachineContainer from "./MachineContainer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ViewModes } from "./ViewModeSelection";
|
||||
|
@ -17,11 +10,7 @@ const MachinesList = ({ machines, viewMode }) => {
|
|||
return (
|
||||
<>
|
||||
{machines.map(machine => (
|
||||
<MachineContainer
|
||||
key={`machine-${machine.machineId}`}
|
||||
machine={machine}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
<MachineContainer key={`machine-${machine.machineId}`} machine={machine} viewMode={viewMode} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import TableChartIcon from "@material-ui/icons/TableChart";
|
||||
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
import TableChartIcon from "@mui/icons-material/TableChart";
|
||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||
import { ToggleButtonGroup, ToggleButton } from "@mui/material";
|
||||
import { Tooltip } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const ViewModes = {
|
||||
|
@ -44,17 +43,8 @@ const ViewModeSelection = ({ initialMode, callback }) => {
|
|||
useEffect(() => callback && callback(state.mode), [callback, state.mode]);
|
||||
|
||||
return (
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={state.mode}
|
||||
exclusive
|
||||
onChange={handleViewModeSelection}
|
||||
>
|
||||
<ToggleButton
|
||||
value={ViewModes.TABLE}
|
||||
aria-label="table view mode"
|
||||
disabled={state.mode === ViewModes.TABLE}
|
||||
>
|
||||
<ToggleButtonGroup size="small" value={state.mode} exclusive onChange={handleViewModeSelection}>
|
||||
<ToggleButton value={ViewModes.TABLE} aria-label="table view mode" disabled={state.mode === ViewModes.TABLE}>
|
||||
<Tooltip title={t("ViewModes.Table")}>
|
||||
<TableChartIcon />
|
||||
</Tooltip>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { IconButton, Tooltip } from "@material-ui/core";
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
|
||||
const ActionButton = React.forwardRef((props, _ref) => {
|
||||
const ActionButton = React.forwardRef(props => {
|
||||
const { action, machine, callback, disabled } = props;
|
||||
const id = `machine-item-${machine.machineId}-${action.code}`;
|
||||
const handleActionClick = event => {
|
||||
|
@ -12,14 +12,12 @@ const ActionButton = React.forwardRef((props, _ref) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
id={`machine-item-${machine.machineId}-${action.code}-tooltip`}
|
||||
title={action.tooltip}
|
||||
>
|
||||
<Tooltip id={`machine-item-${machine.machineId}-${action.code}-tooltip`} title={action.tooltip}>
|
||||
<span>
|
||||
<IconButton
|
||||
id={id}
|
||||
size={"small"}
|
||||
sx={{ padding: "0.2rem" }}
|
||||
onFocus={event => event.stopPropagation()}
|
||||
onClick={handleActionClick}
|
||||
disabled={disabled}
|
||||
|
@ -44,4 +42,6 @@ ActionButton.propTypes = {
|
|||
disabled: PropTypes.bool
|
||||
};
|
||||
|
||||
ActionButton.displayName = "ActionButton";
|
||||
|
||||
export default ActionButton;
|
||||
|
|
|
@ -2,8 +2,8 @@ import React, { useMemo, useState } from "react";
|
|||
import PropTypes from "prop-types";
|
||||
import WakeComponent from "./WakeComponent";
|
||||
import ActionButton from "./ActionButton";
|
||||
import { Menu } from "@material-ui/core";
|
||||
import { MoreHoriz } from "@material-ui/icons";
|
||||
import { Menu } from "@mui/material";
|
||||
import { MoreHoriz } from "@mui/icons-material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePermissions } from "../../../../hooks";
|
||||
|
||||
|
@ -13,15 +13,9 @@ const ActionsGroup = ({ machine, actions, addLog }) => {
|
|||
const { t } = useTranslation();
|
||||
const { operateMachines: canOperateMachines } = usePermissions();
|
||||
|
||||
const mainActions = useMemo(
|
||||
() => actions.filter(a => a.main === true),
|
||||
[actions]
|
||||
);
|
||||
const mainActions = useMemo(() => actions.filter(a => a.main === true), [actions]);
|
||||
|
||||
const secondaryActions = useMemo(
|
||||
() => actions.filter(a => a.main === false),
|
||||
[actions]
|
||||
);
|
||||
const secondaryActions = useMemo(() => actions.filter(a => a.main === false), [actions]);
|
||||
|
||||
const handleMenuOpen = (_, event) => {
|
||||
setMenuAnchor(event.currentTarget);
|
||||
|
@ -33,29 +27,33 @@ const ActionsGroup = ({ machine, actions, addLog }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<WakeComponent
|
||||
machine={machine}
|
||||
addLog={addLog}
|
||||
disabled={!canOperateMachines}
|
||||
/>
|
||||
{mainActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
disabled={!canOperateMachines}
|
||||
/>
|
||||
))}
|
||||
<ActionButton
|
||||
action={{
|
||||
code: "more",
|
||||
effect: handleMenuOpen,
|
||||
icon: MoreHoriz,
|
||||
tooltip: t("Machine.Actions.More")
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center"
|
||||
}}
|
||||
machine={machine}
|
||||
/>
|
||||
|
||||
>
|
||||
<WakeComponent machine={machine} addLog={addLog} disabled={!canOperateMachines} />
|
||||
{mainActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
disabled={!canOperateMachines}
|
||||
/>
|
||||
))}
|
||||
<ActionButton
|
||||
action={{
|
||||
code: "more",
|
||||
effect: handleMenuOpen,
|
||||
icon: MoreHoriz,
|
||||
tooltip: t("Machine.Actions.More")
|
||||
}}
|
||||
machine={machine}
|
||||
/>
|
||||
</div>
|
||||
<Menu
|
||||
id="secondary-actions-menu"
|
||||
anchorEl={menuAnchor}
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import MachineLog from "./MachineLog";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useSensitiveInfo } from "../../../../hooks";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
const useStyles = makeStyles(_theme => ({
|
||||
panel: {
|
||||
display: "flex"
|
||||
},
|
||||
label: {
|
||||
marginRight: "4px"
|
||||
}
|
||||
}));
|
||||
|
||||
const MachineDescription = ({ description }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.panel}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
className={classes.label}
|
||||
sx={{
|
||||
marginRight: "4px"
|
||||
}}
|
||||
color="textSecondary"
|
||||
>
|
||||
{"Description:"}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
import React, { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Box } from "@material-ui/core";
|
||||
import { Box } from "@mui/material";
|
||||
import { useSensitiveInfo } from "../../../../hooks";
|
||||
import { LazyLog, ScrollFollow } from "react-lazylog";
|
||||
|
||||
const MachineLog = ({ logs }) => {
|
||||
const { maskElements } = useSensitiveInfo();
|
||||
|
||||
const displayLogs = useMemo(
|
||||
() => (logs.length > 0 ? maskElements(logs).join("\n") : "..."),
|
||||
[logs, maskElements]
|
||||
);
|
||||
const displayLogs = useMemo(() => (logs.length > 0 ? maskElements(logs).join("\n") : "..."), [logs, maskElements]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { IconButton, Tooltip } from "@material-ui/core";
|
||||
import { PowerSettingsNew } from "@material-ui/icons";
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
import { PowerSettingsNew } from "@mui/icons-material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useToast } from "../../../../hooks";
|
||||
import { msToMinAndSec } from "../../../../utils/time";
|
||||
|
@ -18,10 +18,8 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
|||
const { t } = useTranslation();
|
||||
const { success, error } = useToast();
|
||||
|
||||
const pingInterval =
|
||||
process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval;
|
||||
const startingTime =
|
||||
process.env.REACT_APP_MACHINE_STARTING_TIME || defaultStartingTime;
|
||||
const pingInterval = process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval;
|
||||
const startingTime = process.env.REACT_APP_MACHINE_STARTING_TIME || defaultStartingTime;
|
||||
|
||||
const getCurrentDateTime = useCallback(() => {
|
||||
const currentDateTime = Date.now();
|
||||
|
@ -31,10 +29,7 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
|||
return result;
|
||||
}, [t]);
|
||||
|
||||
const log = useCallback(
|
||||
message => addLog(`[${getCurrentDateTime()}] ${message}`),
|
||||
[addLog, getCurrentDateTime]
|
||||
);
|
||||
const log = useCallback(message => addLog(`[${getCurrentDateTime()}] ${message}`), [addLog, getCurrentDateTime]);
|
||||
|
||||
const wakeMachine = useCallback(async () => {
|
||||
await post(
|
||||
|
@ -48,11 +43,7 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
|||
success(result.status);
|
||||
|
||||
//retrigger
|
||||
log(
|
||||
`Periodic ping will be re-triggered in ${startingTime} ms [${msToMinAndSec(
|
||||
startingTime
|
||||
)}]`
|
||||
);
|
||||
log(`Periodic ping will be re-triggered in ${startingTime} ms [${msToMinAndSec(startingTime)}]`);
|
||||
setTimeout(() => {
|
||||
setTrigger(prev => !prev);
|
||||
}, startingTime);
|
||||
|
@ -80,12 +71,16 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
|||
}, pingInterval);
|
||||
}
|
||||
},
|
||||
onError: () => {}
|
||||
onError: () => {
|
||||
// to do: handle error
|
||||
}
|
||||
}
|
||||
);
|
||||
}, [machine, log, pingInterval, disabled]);
|
||||
|
||||
useEffect(pingInLoop, [trigger, pingInLoop]);
|
||||
useEffect(() => {
|
||||
pingInLoop();
|
||||
}, [trigger, pingInLoop]);
|
||||
|
||||
const handleWakeClick = event => {
|
||||
wakeMachine();
|
||||
|
@ -100,6 +95,7 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
|||
size={"small"}
|
||||
disabled={disabled || state.on}
|
||||
onClick={handleWakeClick}
|
||||
sx={{ padding: "0.2rem" }}
|
||||
style={state.on ? { color: "#33cc33" } : undefined}
|
||||
onFocus={event => event.stopPropagation()}
|
||||
>
|
||||
|
|
|
@ -4,10 +4,10 @@ import NetworkStateProvider from "../state/NetworkStateProvider";
|
|||
import { usePermissions } from "../../../hooks";
|
||||
import NotAllowed from "../../../components/common/NotAllowed";
|
||||
|
||||
const NetworkContainer = () => {
|
||||
const NetworkContainer = (): JSX.Element | null => {
|
||||
const { loading, viewMachines } = usePermissions();
|
||||
|
||||
if (loading) return "";
|
||||
if (loading) return null;
|
||||
if (!viewMachines) return <NotAllowed />;
|
||||
|
||||
return (
|
|
@ -6,16 +6,11 @@ import { initialState } from "./initialState";
|
|||
|
||||
const NetworkStateProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const dispatchActions = useMemo(
|
||||
() => reducerDispatchActions(dispatch),
|
||||
[dispatch]
|
||||
);
|
||||
const dispatchActions = useMemo(() => reducerDispatchActions(dispatch), [dispatch]);
|
||||
|
||||
return (
|
||||
<NetworkStateContext.Provider value={state}>
|
||||
<NetworkDispatchContext.Provider value={dispatchActions}>
|
||||
{children}
|
||||
</NetworkDispatchContext.Provider>
|
||||
<NetworkDispatchContext.Provider value={dispatchActions}>{children}</NetworkDispatchContext.Provider>
|
||||
</NetworkStateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,5 @@ export function reducer(state, action) {
|
|||
}
|
||||
|
||||
export const dispatchActions = dispatch => ({
|
||||
onNetworkChange: (prop, value) =>
|
||||
dispatch({ type: "onNetworkChange", payload: { prop, value } })
|
||||
onNetworkChange: (prop, value) => dispatch({ type: "onNetworkChange", payload: { prop, value } })
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useMemo } from "react";
|
||||
import BubbleChartIcon from "@material-ui/icons/BubbleChart";
|
||||
import BrushIcon from "@material-ui/icons/Brush";
|
||||
import NotificationsIcon from "@material-ui/icons/Notifications";
|
||||
import BubbleChartIcon from "@mui/icons-material/BubbleChart";
|
||||
import BrushIcon from "@mui/icons-material/Brush";
|
||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PageTitle from "../../components/common/PageTitle";
|
||||
import NavigationButtons from "../../components/common/NavigationButtons";
|
||||
|
@ -34,19 +34,11 @@ const SettingsContainer = () => {
|
|||
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigationTabs = useMemo(
|
||||
() => tabs.map(z => ({ ...z, tooltip: t(z.code) })),
|
||||
[t]
|
||||
);
|
||||
const navigationTabs = useMemo(() => tabs.map(z => ({ ...z, tooltip: t(z.code) })), [t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle
|
||||
text={t(tab)}
|
||||
navigation={
|
||||
<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />
|
||||
}
|
||||
/>
|
||||
<PageTitle text={t(tab)} navigation={<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />} />
|
||||
{tab === NavigationTabs.SYSTEM && <SystemContainer />}
|
||||
{tab === NavigationTabs.APPEARANCE && <AppearanceContainer />}
|
||||
{tab === NavigationTabs.NOTIFICATIONS && <NotificationsContainer />}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import React from "react";
|
||||
import { useApplicationTheme } from "../../../providers/ThemeProvider";
|
||||
import { Grid, Paper, FormControlLabel, Switch } from "@material-ui/core";
|
||||
import { Grid, Paper, FormControlLabel, Switch, Box } from "@mui/material";
|
||||
import LanguageContainer from "./language/LanguageContainer";
|
||||
import { PaperTitle } from "components/common";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
const styles = {
|
||||
language: {
|
||||
paddingLeft: theme.spacing(1)
|
||||
paddingLeft: 1
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const AppearanceComponent = () => {
|
||||
const { isDark, onDarkModeChanged } = useApplicationTheme();
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
|
||||
const handleChange = event => {
|
||||
const { checked } = event.target;
|
||||
|
@ -29,14 +27,7 @@ const AppearanceComponent = () => {
|
|||
<Grid item xs={12} sm={6} md={4} lg={3}>
|
||||
<FormControlLabel
|
||||
value="start"
|
||||
control={
|
||||
<Switch
|
||||
checked={isDark}
|
||||
onChange={handleChange}
|
||||
color="secondary"
|
||||
name="dark-mode-switch"
|
||||
/>
|
||||
}
|
||||
control={<Switch checked={isDark} onChange={handleChange} color="secondary" name="dark-mode-switch" />}
|
||||
label="Dark mode:"
|
||||
labelPlacement="start"
|
||||
/>
|
||||
|
@ -45,9 +36,9 @@ const AppearanceComponent = () => {
|
|||
<FormControlLabel
|
||||
value="start"
|
||||
control={
|
||||
<div className={classes.language}>
|
||||
<Box sx={styles.language}>
|
||||
<LanguageContainer />
|
||||
</div>
|
||||
</Box>
|
||||
}
|
||||
label="Language:"
|
||||
labelPlacement="start"
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Flag from "react-flags";
|
||||
import { IconButton, Menu, MenuItem } from "@material-ui/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const LanguageComponent = ({
|
||||
languageIsSet,
|
||||
anchorEl,
|
||||
onMenuOpen,
|
||||
onLanguageChange,
|
||||
onClose,
|
||||
flag,
|
||||
flagsPath
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
aria-controls="language-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={onMenuOpen}
|
||||
color="inherit"
|
||||
size="small"
|
||||
>
|
||||
{languageIsSet && (
|
||||
<Flag
|
||||
name={flag.name}
|
||||
format="png"
|
||||
pngSize={32}
|
||||
shiny={true}
|
||||
basePath={flagsPath}
|
||||
alt={flag.alt}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="language-menu"
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right"
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left"
|
||||
}}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
>
|
||||
<MenuItem onClick={onLanguageChange("ro")}>
|
||||
{t("Language.Romanian")}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={onLanguageChange("en")}>
|
||||
{t("Language.English")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
LanguageComponent.propTypes = {
|
||||
languageIsSet: PropTypes.bool.isRequired,
|
||||
anchorEl: PropTypes.object,
|
||||
onMenuOpen: PropTypes.func.isRequired,
|
||||
onLanguageChange: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
flag: PropTypes.object.isRequired,
|
||||
flagsPath: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default LanguageComponent;
|
|
@ -0,0 +1,54 @@
|
|||
import React, { MouseEvent, ReactElement } from "react";
|
||||
import { IconButton, Menu, MenuItem } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FlagIcon } from "components/common";
|
||||
|
||||
interface LanguageComponentProps {
|
||||
languageIsSet: boolean;
|
||||
anchorEl: null | HTMLElement;
|
||||
onMenuOpen: (event: MouseEvent<HTMLElement>) => void;
|
||||
onLanguageChange: (lang: string) => () => void;
|
||||
onClose: () => void;
|
||||
flagCode: string;
|
||||
flagsPath: string;
|
||||
}
|
||||
|
||||
const LanguageComponent: React.FC<LanguageComponentProps> = ({
|
||||
languageIsSet,
|
||||
anchorEl,
|
||||
onMenuOpen,
|
||||
onLanguageChange,
|
||||
onClose,
|
||||
flagCode
|
||||
}): ReactElement => {
|
||||
const { t } = useTranslation();
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton aria-controls="language-menu" aria-haspopup="true" onClick={onMenuOpen} color="inherit" size="small">
|
||||
{languageIsSet && <FlagIcon code={flagCode} />}
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="language-menu"
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right"
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left"
|
||||
}}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
>
|
||||
<MenuItem onClick={onLanguageChange("ro")}>{t("Language.Romanian")}</MenuItem>
|
||||
<MenuItem onClick={onLanguageChange("en")}>{t("Language.English")}</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageComponent;
|
|
@ -2,25 +2,15 @@ import React, { useState, useEffect } from "react";
|
|||
import { useTranslation } from "react-i18next";
|
||||
import LanguageComponent from "./LanguageComponent";
|
||||
|
||||
const flagsPath = process.env.PUBLIC_URL
|
||||
? `${process.env.PUBLIC_URL}/flags`
|
||||
: "flags";
|
||||
|
||||
const LanguageContainer = () => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const { i18n } = useTranslation();
|
||||
const [flag, setFlag] = useState({
|
||||
name: "RO",
|
||||
alt: "-"
|
||||
});
|
||||
const [flagCode, setFlagCode] = useState("RO");
|
||||
|
||||
useEffect(() => {
|
||||
if (!i18n.language) return;
|
||||
setFlag({
|
||||
name: i18n.language === "en" ? "GB" : i18n.language.toUpperCase(),
|
||||
alt: i18n.language
|
||||
});
|
||||
setFlagCode(i18n.language === "en" ? "GB" : i18n.language.toUpperCase());
|
||||
}, [i18n.language]);
|
||||
|
||||
const handleMenuOpen = event => {
|
||||
|
@ -45,8 +35,7 @@ const LanguageContainer = () => {
|
|||
onMenuOpen={handleMenuOpen}
|
||||
onLanguageChange={handleLanguageChange}
|
||||
onClose={handleClose}
|
||||
flag={flag}
|
||||
flagsPath={flagsPath}
|
||||
flagCode={flagCode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,9 +3,8 @@ import React from "react";
|
|||
const NotificationsContainer = () => {
|
||||
return (
|
||||
<div>
|
||||
Enable/Disable email notifications (for each one separately - when
|
||||
starting the machine, when stopping) You can go even further and have an
|
||||
advanced site where you can configure each individual machine.
|
||||
Enable/Disable email notifications (for each one separately - when starting the machine, when stopping) You can go
|
||||
even further and have an advanced site where you can configure each individual machine.
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,37 +1,22 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Paper, Button } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Paper, Button, Box } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PaperTitle } from "components/common";
|
||||
import { usePermissions } from "hooks";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
content: {
|
||||
"& > *": {
|
||||
margin: theme.spacing(1)
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const CacheSettingsComponent = ({ onResetCache }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const { sysAdmin } = usePermissions();
|
||||
|
||||
return (
|
||||
<Paper variant="outlined">
|
||||
<PaperTitle text={t("Settings.Cache.Title")} />
|
||||
<div className={classes.content}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
disabled={!sysAdmin}
|
||||
onClick={onResetCache}
|
||||
>
|
||||
<Box sx={{ margin: 1 }}>
|
||||
<Button variant="outlined" color="secondary" disabled={!sysAdmin} onClick={onResetCache}>
|
||||
{t("Settings.Cache.Reset")}
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useMemo } from "react";
|
||||
import CategoryIcon from "@material-ui/icons/Category";
|
||||
import GrainIcon from "@material-ui/icons/Grain";
|
||||
import CategoryIcon from "@mui/icons-material/Category";
|
||||
import GrainIcon from "@mui/icons-material/Grain";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PageTitle from "../../components/common/PageTitle";
|
||||
import NavigationButtons from "../../components/common/NavigationButtons";
|
||||
|
@ -27,19 +27,11 @@ const SystemContainer = () => {
|
|||
const [tab, setTab] = useState(NavigationTabs.MAIN_SERVICES);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigationTabs = useMemo(
|
||||
() => tabs.map(z => ({ ...z, tooltip: t(z.code) })),
|
||||
[t]
|
||||
);
|
||||
const navigationTabs = useMemo(() => tabs.map(z => ({ ...z, tooltip: t(z.code) })), [t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle
|
||||
text={t(tab)}
|
||||
navigation={
|
||||
<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />
|
||||
}
|
||||
/>
|
||||
<PageTitle text={t(tab)} navigation={<NavigationButtons tabs={navigationTabs} onTabChange={setTab} />} />
|
||||
{tab === NavigationTabs.MAIN_SERVICES && <MainServicesContainer />}
|
||||
{tab === NavigationTabs.AGENTS && <AgentsContainer />}
|
||||
</>
|
||||
|
|
|
@ -2,19 +2,15 @@ import React, { useMemo } from "react";
|
|||
import PropTypes from "prop-types";
|
||||
import UserProfilePicture from "./UserProfilePicture";
|
||||
import ContactOptions from "../contact/ContactOptions";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import styles from "../styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { getStyles } from "../styles";
|
||||
|
||||
const UserProfileCardContent = ({ userData }) => {
|
||||
const { profilePictureUrl } = userData;
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
const userName = useMemo(
|
||||
() => `${userData.firstName} ${userData.lastName}`,
|
||||
[userData.firstName, userData.lastName]
|
||||
);
|
||||
const userName = useMemo(() => `${userData.firstName} ${userData.lastName}`, [userData.firstName, userData.lastName]);
|
||||
const _contactOptions = useMemo(
|
||||
() =>
|
||||
profilePictureUrl
|
||||
|
@ -31,7 +27,7 @@ const UserProfileCardContent = ({ userData }) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={classes.panel}>
|
||||
<div style={styles.panel}>
|
||||
<UserProfilePicture pictureUrl={userData.profilePictureUrl} />
|
||||
<ContactOptions contactOptions={_contactOptions} userName={userName} />
|
||||
</div>
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
import React, { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardHeader, CardContent } from "@material-ui/core";
|
||||
import { Card, CardHeader, CardContent } from "@mui/material";
|
||||
import PageTitle from "../../../../components/common/PageTitle";
|
||||
import UserProfileCardContent from "./UserProfileCardContent";
|
||||
import SecurityComponent from "../security/SecurityComponent";
|
||||
import styles from "../styles";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
import { getStyles } from "../styles";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
const UserProfileComponent = ({ userData }) => {
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
const userLoginDate = useMemo(
|
||||
() =>
|
||||
|
@ -29,9 +28,7 @@ const UserProfileComponent = ({ userData }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<PageTitle
|
||||
text={t("User.Profile.Hello", { userName: userData.firstName })}
|
||||
/>
|
||||
<PageTitle text={t("User.Profile.Hello", { userName: userData.firstName })} />
|
||||
<Card>
|
||||
<CardHeader title={userData.userName} subheader={userDescription} />
|
||||
<CardContent>
|
||||
|
@ -39,11 +36,8 @@ const UserProfileComponent = ({ userData }) => {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className={classes.section}>
|
||||
<SecurityComponent
|
||||
userGroups={userData.userGroups}
|
||||
userRoles={userData.userRoles}
|
||||
/>
|
||||
<div style={styles.section}>
|
||||
<SecurityComponent userGroups={userData.userGroups} userRoles={userData.userRoles} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { useTuitioUserInfo } from "@flare/tuitio-client-react";
|
||||
import UserProfileComponent from "./UserProfileComponent";
|
||||
|
||||
const UserProfileContainer = () => {
|
||||
const UserProfileContainer: React.FC = () => {
|
||||
const { userInfo } = useTuitioUserInfo();
|
||||
return <>{userInfo && <UserProfileComponent userData={userInfo} />}</>;
|
||||
};
|
|
@ -1,16 +1,15 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import style from "../styles";
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { getStyles } from "../styles";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import DefaultUserProfilePicture from "../../../../assets/images/DefaultUserProfilePicture.png";
|
||||
|
||||
const useStyles = makeStyles(style);
|
||||
|
||||
const UserProfilePicture = ({ pictureUrl }) => {
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
const url = pictureUrl ?? DefaultUserProfilePicture;
|
||||
return <Avatar src={url} alt="..." className={classes.profilePicture} />;
|
||||
return <Avatar src={url} alt="..." sx={styles.profilePicture} />;
|
||||
};
|
||||
|
||||
UserProfilePicture.propTypes = {
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
Link,
|
||||
Tooltip,
|
||||
IconButton
|
||||
} from "@material-ui/core";
|
||||
import { ListItem, ListItemText, ListItemIcon, Link, Tooltip, IconButton } from "@mui/material";
|
||||
|
||||
const ContactIcon = ({ onIconClick, iconTooltip, ...props }) => {
|
||||
if (!onIconClick) return <props.icon />;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { List } from "@material-ui/core";
|
||||
import { List } from "@mui/material";
|
||||
import ContactOption from "./ContactOption";
|
||||
|
||||
const ContactOptionList = ({ options }) => {
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import React, { useCallback, useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { Grid } from "@mui/material";
|
||||
import ContactOptionList from "./ContactOptionList";
|
||||
import BusinessCenterIcon from "@material-ui/icons/BusinessCenter";
|
||||
import EmailIcon from "@material-ui/icons/Email";
|
||||
import PhoneAndroidIcon from "@material-ui/icons/PhoneAndroid";
|
||||
import LanguageIcon from "@material-ui/icons/Language";
|
||||
import LinkedInIcon from "@material-ui/icons/LinkedIn";
|
||||
import GitHubIcon from "@material-ui/icons/GitHub";
|
||||
import RedditIcon from "@material-ui/icons/Reddit";
|
||||
import BookIcon from "@material-ui/icons/Book";
|
||||
import MenuBookIcon from "@material-ui/icons/MenuBook";
|
||||
import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined";
|
||||
import BusinessCenterIcon from "@mui/icons-material/BusinessCenter";
|
||||
import EmailIcon from "@mui/icons-material/Email";
|
||||
import PhoneAndroidIcon from "@mui/icons-material/PhoneAndroid";
|
||||
import LanguageIcon from "@mui/icons-material/Language";
|
||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import RedditIcon from "@mui/icons-material/Reddit";
|
||||
import BookIcon from "@mui/icons-material/Book";
|
||||
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
||||
import FileCopyOutlinedIcon from "@mui/icons-material/FileCopyOutlined";
|
||||
import { useClipboard } from "../../../../hooks";
|
||||
|
||||
const icons = {
|
||||
|
@ -62,8 +62,7 @@ const getTooltip = contactOption => {
|
|||
};
|
||||
|
||||
const getOrderNumber = contactOption => {
|
||||
const orderNo =
|
||||
orderNumbers[contactOption.contactTypeCode] || orderNumbers.DEFAULT;
|
||||
const orderNo = orderNumbers[contactOption.contactTypeCode] || orderNumbers.DEFAULT;
|
||||
return orderNo;
|
||||
};
|
||||
|
||||
|
@ -152,10 +151,7 @@ const ContactOptions = ({ contactOptions, userName }) => {
|
|||
[contactOptions, getOnClickEvent, getIconClickEvent, t, userName]
|
||||
);
|
||||
|
||||
const sorted = useMemo(
|
||||
() => enrichedContactOptions.sort((a, b) => a.orderNo - b.orderNo),
|
||||
[enrichedContactOptions]
|
||||
);
|
||||
const sorted = useMemo(() => enrichedContactOptions.sort((a, b) => a.orderNo - b.orderNo), [enrichedContactOptions]);
|
||||
|
||||
const chunks = useMemo(() => sliceContactOptions(sorted), [sorted]);
|
||||
|
||||
|
|
|
@ -1,43 +1,37 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Paper, Grid, Chip, Typography } from "@material-ui/core";
|
||||
import styles from "../styles";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { Paper, Grid, Chip, Typography } from "@mui/material";
|
||||
import { getStyles } from "../styles";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const SecurityComponent = ({ userGroups, userRoles }) => {
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme);
|
||||
return (
|
||||
<Paper>
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={6}>
|
||||
<div className={classes.paper}>
|
||||
<div style={styles.paper}>
|
||||
<Typography gutterBottom variant="body1">
|
||||
{t("User.Profile.Security.UserGroups")}
|
||||
</Typography>
|
||||
<div>
|
||||
{userGroups.map(g => (
|
||||
<Chip key={g.code} className={classes.chip} label={g.name} />
|
||||
<Chip key={g.code} style={styles.chip} label={g.name} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<div className={classes.paper}>
|
||||
<div style={styles.paper}>
|
||||
<Typography gutterBottom variant="body1">
|
||||
{t("User.Profile.Security.UserRoles")}
|
||||
</Typography>
|
||||
<div>
|
||||
{userRoles.map(r => (
|
||||
<Chip
|
||||
key={r.code}
|
||||
className={classes.chip}
|
||||
color="primary"
|
||||
label={r.name}
|
||||
/>
|
||||
<Chip key={r.code} style={styles.chip} color="primary" label={r.name} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const style = theme => {
|
||||
const getStyles = theme => {
|
||||
return {
|
||||
section: {
|
||||
marginTop: theme.spacing(2)
|
||||
|
@ -6,7 +6,7 @@ const style = theme => {
|
|||
panel: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
"@media (max-width: 600px)": {
|
||||
"@media (maxWidth: 600px)": {
|
||||
flexDirection: "column" // change direction for small screens
|
||||
}
|
||||
},
|
||||
|
@ -25,4 +25,4 @@ const style = theme => {
|
|||
};
|
||||
};
|
||||
|
||||
export default style;
|
||||
export { getStyles };
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import React, { Suspense } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import ThemeProvider from "./providers/ThemeProvider";
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import AppRouter from "./components/AppRouter";
|
||||
import { TuitioProvider } from "@flare/tuitio-client-react";
|
||||
import { ToastProvider } from "./providers";
|
||||
import env from "./utils/env";
|
||||
import "./utils/i18n";
|
||||
|
||||
ReactDOM.render(
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
|
||||
root.render(
|
||||
<TuitioProvider tuitioUrl={env.REACT_APP_TUITIO_URL}>
|
||||
<ThemeProvider>
|
||||
<CssBaseline />
|
||||
|
@ -18,6 +20,5 @@ ReactDOM.render(
|
|||
</ToastProvider>
|
||||
</Suspense>
|
||||
</ThemeProvider>
|
||||
</TuitioProvider>,
|
||||
document.getElementById("root")
|
||||
</TuitioProvider>
|
||||
);
|
|
@ -23,8 +23,7 @@ const reducer = (state = initialState, action) => {
|
|||
};
|
||||
|
||||
const dispatchActions = dispatch => ({
|
||||
onSensitiveInfoEnabled: enabled =>
|
||||
dispatch({ type: "onSensitiveInfoEnabled", payload: { enabled } })
|
||||
onSensitiveInfoEnabled: enabled => dispatch({ type: "onSensitiveInfoEnabled", payload: { enabled } })
|
||||
});
|
||||
|
||||
const useSensitiveInfo = () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useReducer, useMemo, useContext } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ThemeProvider as MuiThemeProvider } from "@material-ui/styles";
|
||||
import { ThemeProvider as MuiThemeProvider } from "@mui/material/styles";
|
||||
import { localStorage } from "@flare/js-utils";
|
||||
import { getThemes } from "../themes";
|
||||
|
||||
|
@ -13,13 +13,10 @@ const COLOR_SCHEME = {
|
|||
};
|
||||
|
||||
const colorScheme = localStorage.getItem(LOCAL_STORAGE_COLOR_SCHEME_KEY);
|
||||
const prefersDarkMode = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)"
|
||||
).matches;
|
||||
const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
|
||||
const initialState = {
|
||||
scheme:
|
||||
colorScheme ?? (prefersDarkMode ? COLOR_SCHEME.DARK : COLOR_SCHEME.LIGHT)
|
||||
scheme: colorScheme ?? (prefersDarkMode ? COLOR_SCHEME.DARK : COLOR_SCHEME.LIGHT)
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action) => {
|
||||
|
@ -48,8 +45,7 @@ const useApplicationTheme = () => {
|
|||
const { onColorSchemeChanged } = actions;
|
||||
|
||||
const { scheme } = state;
|
||||
const onDarkModeChanged = active =>
|
||||
onColorSchemeChanged(active ? COLOR_SCHEME.DARK : COLOR_SCHEME.LIGHT);
|
||||
const onDarkModeChanged = active => onColorSchemeChanged(active ? COLOR_SCHEME.DARK : COLOR_SCHEME.LIGHT);
|
||||
|
||||
return { isDark: scheme === COLOR_SCHEME.DARK, onDarkModeChanged };
|
||||
};
|
||||
|
@ -57,10 +53,7 @@ const useApplicationTheme = () => {
|
|||
const ThemeProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const actions = useMemo(() => dispatchActions(dispatch), [dispatch]);
|
||||
const themes = useMemo(
|
||||
() => getThemes(state.scheme === COLOR_SCHEME.DARK),
|
||||
[state.scheme]
|
||||
);
|
||||
const themes = useMemo(() => getThemes(state.scheme === COLOR_SCHEME.DARK), [state.scheme]);
|
||||
|
||||
return (
|
||||
<ApplicationThemeContext.Provider
|
||||
|
|
|
@ -19,22 +19,14 @@ const initialState = {
|
|||
};
|
||||
|
||||
const getPermissionFlags = permissions => {
|
||||
const viewDashboard =
|
||||
permissions.includes(permissionCodes.VIEW_DASHBOARD) ?? false;
|
||||
const manageUsers =
|
||||
permissions.includes(permissionCodes.MANAGE_USERS) ?? false;
|
||||
const manageSettings =
|
||||
permissions.includes(permissionCodes.MANAGE_SETTINGS) ?? false;
|
||||
const viewMachines =
|
||||
permissions.includes(permissionCodes.VIEW_MACHINES) ?? false;
|
||||
const manageMachines =
|
||||
permissions.includes(permissionCodes.MANAGE_MACHINES) ?? false;
|
||||
const operateMachines =
|
||||
permissions.includes(permissionCodes.OPERATE_MACHINES) ?? false;
|
||||
const guestAccess =
|
||||
permissions.includes(permissionCodes.GUEST_ACCESS) ?? false;
|
||||
const sysAdmin =
|
||||
permissions.includes(permissionCodes.SYSTEM_ADMINISTRATION) ?? false;
|
||||
const viewDashboard = permissions.includes(permissionCodes.VIEW_DASHBOARD) ?? false;
|
||||
const manageUsers = permissions.includes(permissionCodes.MANAGE_USERS) ?? false;
|
||||
const manageSettings = permissions.includes(permissionCodes.MANAGE_SETTINGS) ?? false;
|
||||
const viewMachines = permissions.includes(permissionCodes.VIEW_MACHINES) ?? false;
|
||||
const manageMachines = permissions.includes(permissionCodes.MANAGE_MACHINES) ?? false;
|
||||
const operateMachines = permissions.includes(permissionCodes.OPERATE_MACHINES) ?? false;
|
||||
const guestAccess = permissions.includes(permissionCodes.GUEST_ACCESS) ?? false;
|
||||
const sysAdmin = permissions.includes(permissionCodes.SYSTEM_ADMINISTRATION) ?? false;
|
||||
|
||||
const flags = {
|
||||
viewDashboard,
|
||||
|
@ -74,11 +66,7 @@ const UserPermissionsProvider = ({ children }) => {
|
|||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<UserPermissionsContext.Provider value={permissions}>
|
||||
{children}
|
||||
</UserPermissionsContext.Provider>
|
||||
);
|
||||
return <UserPermissionsContext.Provider value={permissions}>{children}</UserPermissionsContext.Provider>;
|
||||
};
|
||||
|
||||
UserPermissionsProvider.propTypes = {
|
||||
|
|
|
@ -3,9 +3,4 @@ import ToastProvider from "./ToastProvider";
|
|||
import SensitiveInfoProvider from "./SensitiveInfoProvider";
|
||||
import UserPermissionsProvider from "./UserPermissionsProvider";
|
||||
|
||||
export {
|
||||
ThemeProvider,
|
||||
ToastProvider,
|
||||
SensitiveInfoProvider,
|
||||
UserPermissionsProvider
|
||||
};
|
||||
export { ThemeProvider, ToastProvider, SensitiveInfoProvider, UserPermissionsProvider };
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
const primary = "#00695C";
|
||||
const secondary = "#DC7633";
|
||||
const warning = "#ff9800";
|
||||
const success = "#4caf50";
|
||||
const info = "#2196f3";
|
||||
|
||||
const defaultTheme = {
|
||||
palette: {
|
||||
primary: {
|
||||
main: primary
|
||||
},
|
||||
secondary: {
|
||||
main: secondary
|
||||
// contrastText: "#ffcc00"
|
||||
},
|
||||
warning: {
|
||||
main: warning
|
||||
},
|
||||
success: {
|
||||
main: success
|
||||
},
|
||||
info: {
|
||||
main: info
|
||||
}
|
||||
},
|
||||
overrides: {
|
||||
MuiBackdrop: {
|
||||
root: {
|
||||
backgroundColor: "#4A4A4A1A"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default defaultTheme;
|
|
@ -0,0 +1,69 @@
|
|||
import { ThemeOptions } from "@mui/material/styles";
|
||||
|
||||
const primary = "#00695C";
|
||||
const secondary = "#DC7633";
|
||||
const warning = "#ff9800";
|
||||
const success = "#4caf50";
|
||||
const info = "#2196f3";
|
||||
|
||||
// background_default: "#f4f5fd",
|
||||
|
||||
export const commonTheme: ThemeOptions = {
|
||||
palette: {
|
||||
primary: {
|
||||
main: primary
|
||||
},
|
||||
secondary: {
|
||||
main: secondary
|
||||
},
|
||||
warning: {
|
||||
main: warning
|
||||
},
|
||||
success: {
|
||||
main: success
|
||||
},
|
||||
info: {
|
||||
main: info
|
||||
}
|
||||
},
|
||||
components: {
|
||||
MuiAppBar: {
|
||||
defaultProps: {
|
||||
enableColorOnDark: true
|
||||
}
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundImage: "unset" // The gradient is part of how MUI implements the elevation of the Paper component in dark mode. If you don't want this gradient, you can override it with "unset".
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const lightThemeOptions: ThemeOptions = {
|
||||
...commonTheme,
|
||||
palette: {
|
||||
...commonTheme.palette,
|
||||
mode: "light",
|
||||
background: {
|
||||
default: "#fafafa",
|
||||
paper: "#fff"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const darkThemeOptions: ThemeOptions = {
|
||||
...commonTheme,
|
||||
palette: {
|
||||
...commonTheme.palette,
|
||||
mode: "dark",
|
||||
background: {
|
||||
default: "#303030",
|
||||
paper: "#424242"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { lightThemeOptions, darkThemeOptions };
|
|
@ -1,39 +0,0 @@
|
|||
import defaultTheme from "./default";
|
||||
import { createTheme } from "@material-ui/core/styles";
|
||||
|
||||
const overrides = {
|
||||
typography: {
|
||||
h1: {
|
||||
fontSize: "3rem"
|
||||
},
|
||||
h2: {
|
||||
fontSize: "2rem"
|
||||
},
|
||||
h3: {
|
||||
fontSize: "1.64rem"
|
||||
},
|
||||
h4: {
|
||||
fontSize: "1.5rem"
|
||||
},
|
||||
h5: {
|
||||
fontSize: "1.285rem"
|
||||
},
|
||||
h6: {
|
||||
fontSize: "1.142rem"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getThemes = darkMode => {
|
||||
const type = darkMode ? "dark" : "light";
|
||||
|
||||
return {
|
||||
default: createTheme({
|
||||
...defaultTheme,
|
||||
...overrides,
|
||||
palette: { ...defaultTheme.palette, type }
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
export { getThemes };
|
|
@ -0,0 +1,43 @@
|
|||
import { lightThemeOptions, darkThemeOptions } from "./defaults";
|
||||
import { createTheme, Theme } from "@mui/material/styles";
|
||||
|
||||
type Themes = {
|
||||
default: Theme;
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
typography: {
|
||||
h1: {
|
||||
fontSize: "3rem"
|
||||
},
|
||||
h2: {
|
||||
fontSize: "2rem"
|
||||
},
|
||||
h3: {
|
||||
fontSize: "1.64rem"
|
||||
},
|
||||
h4: {
|
||||
fontSize: "1.5rem"
|
||||
},
|
||||
h5: {
|
||||
fontSize: "1.285rem"
|
||||
},
|
||||
h6: {
|
||||
fontSize: "1.142rem"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getThemes = (darkMode: boolean): Themes => {
|
||||
const defaultTheme = darkMode ? darkThemeOptions : lightThemeOptions;
|
||||
const dTheme = createTheme({
|
||||
...defaultTheme,
|
||||
...overrides
|
||||
});
|
||||
|
||||
return {
|
||||
default: dTheme
|
||||
};
|
||||
};
|
||||
|
||||
export { getThemes };
|
|
@ -0,0 +1,5 @@
|
|||
import * as models from "./models";
|
||||
|
||||
export * from "./models";
|
||||
|
||||
export { models };
|
|
@ -0,0 +1,8 @@
|
|||
export type Machine = {
|
||||
machineId: number;
|
||||
machineName: string;
|
||||
fullMachineName: string;
|
||||
macAddress: string;
|
||||
iPv4Address?: string;
|
||||
description?: string;
|
||||
};
|
|
@ -38,7 +38,7 @@ const handleError = err => {
|
|||
};
|
||||
|
||||
const defaultOptions = {
|
||||
onCompleted: () => {},
|
||||
onCompleted: () => null,
|
||||
onError: handleError
|
||||
};
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue