Compare commits
No commits in common. "e80d246fc05ae304951b88ed245285b8dd8530e0" and "08f1360f21915d2a6a334b3f1a269339ab3485e1" have entirely different histories.
e80d246fc0
...
08f1360f21
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
<Import Project="dependencies.props" />
|
<Import Project="dependencies.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.3.0</Version>
|
<Version>1.2.7</Version>
|
||||||
<Authors>Tudor Stanciu</Authors>
|
<Authors>Tudor Stanciu</Authors>
|
||||||
<Company>STA</Company>
|
<Company>STA</Company>
|
||||||
<PackageTags>NetworkResurrector</PackageTags>
|
<PackageTags>NetworkResurrector</PackageTags>
|
||||||
|
|
|
@ -201,15 +201,4 @@
|
||||||
• Updated menu component to permanently display the selected item.
|
• Updated menu component to permanently display the selected item.
|
||||||
</Content>
|
</Content>
|
||||||
</Note>
|
</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>
|
</ReleaseNotes>
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"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": "^_" }]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"printWidth": 120,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"singleQuote": false,
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
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;
|
|
||||||
};
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "network-resurrector-frontend",
|
"name": "network-resurrector-frontend",
|
||||||
"version": "1.3.0",
|
"version": "1.2.7",
|
||||||
"description": "Frontend component of Network resurrector system",
|
"description": "Frontend component of Network resurrector system",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Tudor Stanciu",
|
"name": "Tudor Stanciu",
|
||||||
|
@ -9,49 +9,41 @@
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector"
|
"url": "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector-frontend"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@flare/js-utils": "^1.1.0",
|
"@flare/js-utils": "^1.1.0",
|
||||||
"@flare/tuitio-client-react": "^1.2.10",
|
"@flare/tuitio-client-react": "^1.2.6",
|
||||||
"@mui/icons-material": "^5.14.16",
|
"@material-ui/core": "^4.11.2",
|
||||||
"@mui/lab": "^5.0.0-alpha.169",
|
"@material-ui/icons": "^4.11.2",
|
||||||
"@mui/material": "^5.14.16",
|
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.3.4",
|
||||||
"i18next": "^22.4.15",
|
"i18next": "^19.4.4",
|
||||||
"i18next-browser-languagedetector": "^7.0.1",
|
"i18next-browser-languagedetector": "^4.1.1",
|
||||||
"i18next-http-backend": "^2.2.0",
|
"i18next-http-backend": "^1.4.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.3",
|
||||||
"react": "^18.2.0",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^17.0.1",
|
||||||
"react-i18next": "^12.2.2",
|
"react-flags": "^0.1.18",
|
||||||
|
"react-i18next": "^11.4.0",
|
||||||
"react-lazylog": "^4.5.3",
|
"react-lazylog": "^4.5.3",
|
||||||
"react-router-dom": "^6.10.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-toastify": "^9.1.3",
|
"react-scripts": "4.0.1",
|
||||||
"react-world-flags": "^1.6.0"
|
"react-toastify": "^6.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/eslint-parser": "^7.16.5",
|
||||||
"@types/react": "^18.2.33",
|
"@testing-library/jest-dom": "^5.11.6",
|
||||||
"@types/react-dom": "^18.2.14",
|
"@testing-library/react": "^11.2.2",
|
||||||
"@types/react-world-flags": "^1.4.5",
|
"@testing-library/user-event": "^12.5.0",
|
||||||
"eslint": "^8.34.0",
|
"prettier": "^2.5.1"
|
||||||
"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": {
|
"scripts": {
|
||||||
"start": "react-app-rewired start",
|
"start": "react-scripts start",
|
||||||
"build": "react-app-rewired build",
|
"build": "react-scripts build",
|
||||||
"test": "react-app-rewired test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-app-rewired eject"
|
"eject": "react-scripts eject"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
@ -61,14 +53,17 @@
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.3%",
|
||||||
"not dead",
|
"not dead",
|
||||||
"not op_mini all"
|
"not op_mini all"
|
||||||
],
|
],
|
||||||
"development": [
|
"development": [
|
||||||
"last 1 chrome version",
|
"last 1 chrome version",
|
||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version",
|
||||||
|
">0.3%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
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.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 937 B |
|
@ -0,0 +1,73 @@
|
||||||
|
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;
|
|
@ -1,70 +0,0 @@
|
||||||
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 React, { useMemo } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
const getStyles = theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
panel: {
|
panel: {
|
||||||
display: "flex"
|
display: "flex"
|
||||||
},
|
},
|
||||||
|
@ -13,19 +13,21 @@ const getStyles = theme => ({
|
||||||
data: {
|
data: {
|
||||||
fontWeight: theme.typography.fontWeightMedium
|
fontWeight: theme.typography.fontWeightMedium
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
const DataLabel = ({ label, data }) => {
|
const DataLabel = ({ label, data }) => {
|
||||||
const theme = useTheme();
|
const classes = useStyles();
|
||||||
const lbl = useMemo(() => (label.endsWith(":") ? label : `${label}:`), [label]);
|
const lbl = useMemo(
|
||||||
const styles = getStyles(theme);
|
() => (label.endsWith(":") ? label : `${label}:`),
|
||||||
|
[label]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.panel}>
|
<div className={classes.panel}>
|
||||||
<Typography variant="body2" sx={styles.label}>
|
<Typography variant="body2" className={classes.label}>
|
||||||
{lbl}
|
{lbl}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={styles.data}>
|
<Typography variant="body2" className={classes.data}>
|
||||||
{data}
|
{data}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
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,7 +1,8 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { ToggleButtonGroup, ToggleButton } from "@mui/material";
|
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||||
import { Tooltip } from "@mui/material";
|
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||||
|
import { Tooltip } from "@material-ui/core";
|
||||||
|
|
||||||
const NavigationButtons = ({ tabs, onTabChange }) => {
|
const NavigationButtons = ({ tabs, onTabChange }) => {
|
||||||
const [selected, setSelected] = useState(tabs[0].code);
|
const [selected, setSelected] = useState(tabs[0].code);
|
||||||
|
@ -12,9 +13,19 @@ const NavigationButtons = ({ tabs, onTabChange }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButtonGroup size="small" value={selected} exclusive onChange={handleTabSelection}>
|
<ToggleButtonGroup
|
||||||
|
size="small"
|
||||||
|
value={selected}
|
||||||
|
exclusive
|
||||||
|
onChange={handleTabSelection}
|
||||||
|
>
|
||||||
{tabs.map(tab => (
|
{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}>
|
<Tooltip title={tab.tooltip}>
|
||||||
<tab.icon color="primary" />
|
<tab.icon color="primary" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -25,7 +36,9 @@ const NavigationButtons = ({ tabs, onTabChange }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
NavigationButtons.propTypes = {
|
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
|
onTabChange: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Alert, AlertTitle, Box } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
alert: {
|
||||||
|
width: "100%",
|
||||||
|
"& > * + *": {
|
||||||
|
marginTop: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const NotAllowed = () => {
|
const NotAllowed = () => {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<div className={classes.alert}>
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
"& > * + *": {
|
|
||||||
marginTop: 1
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Alert variant="outlined" severity="error">
|
<Alert variant="outlined" severity="error">
|
||||||
<AlertTitle>{t("Announcements.NotAllowed.Title")}</AlertTitle>
|
<AlertTitle>{t("Announcements.NotAllowed.Title")}</AlertTitle>
|
||||||
{t("Announcements.NotAllowed.Message")}
|
{t("Announcements.NotAllowed.Message")}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Typography, Box } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
import { Typography } from "@material-ui/core";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => ({
|
||||||
box: {
|
box: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
marginBottom: 1,
|
marginBottom: theme.spacing(1),
|
||||||
marginTop: 0
|
marginTop: theme.spacing(0)
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -15,22 +16,22 @@ const styles = {
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
minHeight: "40px"
|
minHeight: "40px"
|
||||||
},
|
},
|
||||||
titleText: {
|
titleText: { textTransform: "uppercase" }
|
||||||
textTransform: "uppercase"
|
}));
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const PageTitle = ({ text, toolBar, navigation }) => {
|
const PageTitle = ({ text, toolBar, navigation }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={styles.box}>
|
<div className={classes.box}>
|
||||||
{navigation && navigation}
|
{navigation && navigation}
|
||||||
<Box sx={styles.title}>
|
<div className={classes.title}>
|
||||||
<Typography sx={styles.titleText} variant="h3" size="sm">
|
<Typography className={classes.titleText} variant="h3" size="sm">
|
||||||
{text}
|
{text}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</div>
|
||||||
{toolBar && toolBar}
|
{toolBar && toolBar}
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Typography, Box } from "@mui/material";
|
import { Typography } from "@material-ui/core";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => ({
|
||||||
paper: {
|
paper: {
|
||||||
margin: 1
|
margin: theme.spacing(1)
|
||||||
}
|
}
|
||||||
};
|
}));
|
||||||
|
|
||||||
const PaperTitle = ({ text }) => {
|
const PaperTitle = ({ text }) => {
|
||||||
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<Box sx={styles.paper}>
|
<div className={classes.paper}>
|
||||||
<Typography variant="h5" gutterBottom>
|
<Typography variant="h5" gutterBottom>
|
||||||
{text}
|
{text}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import DataLabel from "./DataLabel";
|
import DataLabel from "./DataLabel";
|
||||||
import PaperTitle from "./PaperTitle";
|
import PaperTitle from "./PaperTitle";
|
||||||
import FlagIcon from "./FlagIcon";
|
|
||||||
|
|
||||||
export { DataLabel, PaperTitle, FlagIcon };
|
export { DataLabel, PaperTitle };
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { InputAdornment, TextField, IconButton } from "@mui/material";
|
import {
|
||||||
import { Visibility, VisibilityOff, LockOutlined } from "@mui/icons-material";
|
InputAdornment,
|
||||||
|
TextField,
|
||||||
|
makeStyles,
|
||||||
|
IconButton
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import { Visibility, VisibilityOff, LockOutlined } from "@material-ui/icons";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => ({
|
||||||
margin: {
|
margin: {
|
||||||
margin: 1
|
margin: theme.spacing(1)
|
||||||
}
|
}
|
||||||
};
|
}));
|
||||||
|
|
||||||
const PasswordField = ({ label, ...rest }) => {
|
const PasswordField = ({ label, ...rest }) => {
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
const handleClickShowPassword = () => {
|
const handleClickShowPassword = () => {
|
||||||
setShowPassword(!showPassword);
|
setShowPassword(!showPassword);
|
||||||
|
@ -22,7 +28,7 @@ const PasswordField = ({ label, ...rest }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
sx={styles.margin}
|
className={classes.margin}
|
||||||
label={label || "Password"}
|
label={label || "Password"}
|
||||||
{...rest}
|
{...rest}
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
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;
|
|
|
@ -1,4 +0,0 @@
|
||||||
import DynamicIcon from "./DynamicIcon";
|
|
||||||
|
|
||||||
export * from "./list";
|
|
||||||
export { DynamicIcon };
|
|
|
@ -1 +0,0 @@
|
||||||
export { Home, Dashboard, Dns, DeviceHub, Build, Settings, FeaturedPlayList, Info } from "@mui/icons-material";
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
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;
|
|
@ -1,54 +0,0 @@
|
||||||
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 React from "react";
|
||||||
import { Route, Routes } from "react-router-dom";
|
import { Route, Switch } from "react-router-dom";
|
||||||
import PageNotFound from "./PageNotFound";
|
import PageNotFound from "./PageNotFound";
|
||||||
import NetworkContainer from "../../features/network/components/NetworkContainer";
|
import NetworkContainer from "../../features/network/components/NetworkContainer";
|
||||||
import SystemContainer from "../../features/system/SystemContainer";
|
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 UserProfileContainer from "../../features/user/profile/card/UserProfileContainer";
|
||||||
import AboutContainer from "../../features/about/AboutContainer";
|
import AboutContainer from "../../features/about/AboutContainer";
|
||||||
|
|
||||||
const AppRoutes: React.FC = () => {
|
const AppRoutes = () => {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Switch>
|
||||||
<Route path="/dashboard" element={<DashboardContainer />} />
|
<Route exact path="/dashboard" component={DashboardContainer} />
|
||||||
<Route path="/user-profile" element={<UserProfileContainer />} />
|
<Route exact path="/user-profile" component={UserProfileContainer} />
|
||||||
<Route path="/machines" element={<NetworkContainer />} />
|
<Route exact path="/machines" component={NetworkContainer} />
|
||||||
<Route path="/system" element={<SystemContainer />} />
|
<Route exact path="/system" component={SystemContainer} />
|
||||||
<Route path="/settings" element={<SettingsContainer />} />
|
<Route exact path="/settings" component={SettingsContainer} />
|
||||||
<Route path="/about" element={<AboutContainer />} />
|
<Route exact path="/about" component={AboutContainer} />
|
||||||
<Route path="/*" element={<PageNotFound />} />
|
<Route component={PageNotFound} />
|
||||||
</Routes>
|
</Switch>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IconButton } from "@mui/material";
|
import { IconButton } from "@material-ui/core";
|
||||||
import { Brightness2 as MoonIcon, WbSunny as SunIcon } from "@mui/icons-material";
|
import {
|
||||||
|
Brightness2 as MoonIcon,
|
||||||
|
WbSunny as SunIcon
|
||||||
|
} from "@material-ui/icons";
|
||||||
import { useApplicationTheme } from "../../providers/ThemeProvider";
|
import { useApplicationTheme } from "../../providers/ThemeProvider";
|
||||||
|
|
||||||
const LightDarkToggle = () => {
|
const LightDarkToggle = () => {
|
||||||
|
@ -9,7 +12,11 @@ const LightDarkToggle = () => {
|
||||||
const handleChange = () => onDarkModeChanged(!isDark);
|
const handleChange = () => onDarkModeChanged(!isDark);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton aria-label="light-dark-toggle" color="inherit" onClick={handleChange}>
|
<IconButton
|
||||||
|
aria-label="light-dark-toggle"
|
||||||
|
color="inherit"
|
||||||
|
onClick={handleChange}
|
||||||
|
>
|
||||||
{isDark ? <SunIcon /> : <MoonIcon />}
|
{isDark ? <SunIcon /> : <MoonIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
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,17 +1,28 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { IconButton, Menu, MenuItem, Typography, ListItemIcon } from "@mui/material";
|
import {
|
||||||
import AccountCircle from "@mui/icons-material/AccountCircle";
|
IconButton,
|
||||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
Menu,
|
||||||
import AccountBoxIcon from "@mui/icons-material/AccountBox";
|
MenuItem,
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
Typography,
|
||||||
import { useNavigate } from "react-router-dom";
|
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 { useTuitioClient } from "@flare/tuitio-client-react";
|
import { useTuitioClient } from "@flare/tuitio-client-react";
|
||||||
import { useToast } from "../../hooks";
|
import { useToast } from "../../hooks";
|
||||||
|
import styles from "./styles";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const ProfileButton = () => {
|
const ProfileButton = () => {
|
||||||
const navigate = useNavigate();
|
const history = useHistory();
|
||||||
const { error } = useToast();
|
const { error } = useToast();
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { logout } = useTuitioClient({
|
const { logout } = useTuitioClient({
|
||||||
|
@ -58,28 +69,28 @@ const ProfileButton = () => {
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/user-profile");
|
history.push("/user-profile");
|
||||||
handleClose();
|
handleClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
<ListItemIcon className={classes.menuItemIcon}>
|
||||||
<AccountBoxIcon fontSize="small" />
|
<AccountBoxIcon fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<Typography variant="inherit">{t("User.Profile.Label")}</Typography>
|
<Typography variant="inherit">{t("User.Profile.Label")}</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/settings");
|
history.push("/settings");
|
||||||
handleClose();
|
handleClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
<ListItemIcon className={classes.menuItemIcon}>
|
||||||
<SettingsIcon fontSize="small" />
|
<SettingsIcon fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<Typography variant="inherit">{t("User.Settings")}</Typography>
|
<Typography variant="inherit">{t("User.Settings")}</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={logout}>
|
<MenuItem onClick={logout}>
|
||||||
<ListItemIcon sx={{ minWidth: "26px" }}>
|
<ListItemIcon className={classes.menuItemIcon}>
|
||||||
<ExitToAppIcon fontSize="small" />
|
<ExitToAppIcon fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<Typography variant="inherit">{t("User.Logout")}</Typography>
|
<Typography variant="inherit">{t("User.Logout")}</Typography>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IconButton } from "@mui/material";
|
import { IconButton } from "@material-ui/core";
|
||||||
import { Visibility as VisibilityIcon, VisibilityOff as VisibilityOffIcon } from "@mui/icons-material";
|
import {
|
||||||
|
Visibility as VisibilityIcon,
|
||||||
|
VisibilityOff as VisibilityOffIcon
|
||||||
|
} from "@material-ui/icons";
|
||||||
import { useSensitiveInfo } from "../../hooks";
|
import { useSensitiveInfo } from "../../hooks";
|
||||||
|
|
||||||
const SensitiveInfoToggle = () => {
|
const SensitiveInfoToggle = () => {
|
||||||
|
@ -9,7 +12,11 @@ const SensitiveInfoToggle = () => {
|
||||||
const handleChange = () => onSensitiveInfoEnabled(!enabled);
|
const handleChange = () => onSensitiveInfoEnabled(!enabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton aria-label="sensitive-info-toggle" color="inherit" onClick={handleChange}>
|
<IconButton
|
||||||
|
aria-label="sensitive-info-toggle"
|
||||||
|
color="inherit"
|
||||||
|
onClick={handleChange}
|
||||||
|
>
|
||||||
{enabled ? <VisibilityOffIcon /> : <VisibilityIcon />}
|
{enabled ? <VisibilityOffIcon /> : <VisibilityIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
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;
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
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;
|
|
@ -0,0 +1,52 @@
|
||||||
|
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;
|
|
@ -1,72 +0,0 @@
|
||||||
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;
|
|
|
@ -1,3 +0,0 @@
|
||||||
export * from "./menu";
|
|
||||||
|
|
||||||
export const drawerWidth = 240;
|
|
|
@ -1,98 +0,0 @@
|
||||||
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;
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
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 React, { useState, useMemo } from "react";
|
||||||
import PageTitle from "../../components/common/PageTitle";
|
import PageTitle from "../../components/common/PageTitle";
|
||||||
import BubbleChartIcon from "@mui/icons-material/BubbleChart";
|
import BubbleChartIcon from "@material-ui/icons/BubbleChart";
|
||||||
import NotesIcon from "@mui/icons-material/Notes";
|
import NotesIcon from "@material-ui/icons/Notes";
|
||||||
import TimelineIcon from "@mui/icons-material/Timeline";
|
import TimelineIcon from "@material-ui/icons/Timeline";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import AboutSystemContainer from "./system/AboutSystemContainer";
|
import AboutSystemContainer from "./system/AboutSystemContainer";
|
||||||
import ReleaseNotesContainer from "./releaseNotes/ReleaseNotesContainer";
|
import ReleaseNotesContainer from "./releaseNotes/ReleaseNotesContainer";
|
||||||
|
@ -33,14 +33,24 @@ const AboutContainer = () => {
|
||||||
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
||||||
const { t } = useTranslation();
|
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 (
|
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.SYSTEM && <AboutSystemContainer />}
|
||||||
{tab === NavigationTabs.RELEASE_NOTES && <ReleaseNotesContainer />}
|
{tab === NavigationTabs.RELEASE_NOTES && <ReleaseNotesContainer />}
|
||||||
{tab === NavigationTabs.TIMELINE && <ReleaseNotesContainer view="timeline" />}
|
{tab === NavigationTabs.TIMELINE && (
|
||||||
|
<ReleaseNotesContainer view="timeline" />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
|
||||||
const ReleaseNote = ({ releaseNote }) => {
|
const ReleaseNote = ({ releaseNote }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{releaseNote.notes.map(note => {
|
{releaseNote.notes.map(note => {
|
||||||
return (
|
return (
|
||||||
<Typography key={releaseNote.notes.indexOf(note)} variant="body2" gutterBottom>
|
<Typography
|
||||||
|
key={releaseNote.notes.indexOf(note)}
|
||||||
|
variant="body2"
|
||||||
|
gutterBottom
|
||||||
|
>
|
||||||
{note}
|
{note}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Grid, Typography } from "@mui/material";
|
import { Grid, Typography } from "@material-ui/core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const ReleaseNoteSummary = ({ releaseNote, collapsed }) => {
|
const ReleaseNoteSummary = ({ releaseNote, collapsed }) => {
|
||||||
|
|
|
@ -4,7 +4,8 @@ import ReleaseNotesList from "./ReleaseNotesList";
|
||||||
import TimelineComponent from "../timeline/TimelineComponent";
|
import TimelineComponent from "../timeline/TimelineComponent";
|
||||||
import { routes, get } from "../../../utils/api";
|
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 ReleaseNotesContainer = ({ view }) => {
|
||||||
const [state, setState] = useState({ data: [], loaded: false });
|
const [state, setState] = useState({ data: [], loaded: false });
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
|
import {
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
Accordion,
|
||||||
|
AccordionSummary,
|
||||||
|
AccordionDetails
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||||
import ReleaseNoteSummary from "./ReleaseNoteSummary";
|
import ReleaseNoteSummary from "./ReleaseNoteSummary";
|
||||||
import ReleaseNote from "./ReleaseNote";
|
import ReleaseNote from "./ReleaseNote";
|
||||||
|
|
||||||
|
@ -25,9 +29,18 @@ const ReleaseNotesList = ({ releases }) => {
|
||||||
<>
|
<>
|
||||||
{releases.map(release => {
|
{releases.map(release => {
|
||||||
return (
|
return (
|
||||||
<Accordion key={release.version} onChange={handleToggle(release.version)}>
|
<Accordion
|
||||||
<AccordionSummary expandIcon={<ExpandMoreIcon />} id={`panel-${release.version}-header`}>
|
key={release.version}
|
||||||
<ReleaseNoteSummary releaseNote={release} collapsed={isCollapsed(release.version)} />
|
onChange={handleToggle(release.version)}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMoreIcon />}
|
||||||
|
id={`panel-${release.version}-header`}
|
||||||
|
>
|
||||||
|
<ReleaseNoteSummary
|
||||||
|
releaseNote={release}
|
||||||
|
collapsed={isCollapsed(release.version)}
|
||||||
|
/>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<ReleaseNote releaseNote={release} />
|
<ReleaseNote releaseNote={release} />
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import Card from "@mui/material/Card";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import CardActions from "@mui/material/CardActions";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@mui/material/CardContent";
|
import CardActions from "@material-ui/core/CardActions";
|
||||||
import Button from "@mui/material/Button";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import Typography from "@mui/material/Typography";
|
import Button from "@material-ui/core/Button";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
import OpenInNewIcon from "@material-ui/icons/OpenInNew";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => ({
|
||||||
bullet: {
|
bullet: {
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
margin: "0 4px",
|
margin: "0 4px",
|
||||||
transform: "scale(1.5)"
|
transform: "scale(1.5)"
|
||||||
},
|
},
|
||||||
service: {
|
service: {
|
||||||
marginTop: 1
|
marginTop: theme.spacing(1)
|
||||||
}
|
}
|
||||||
};
|
}));
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
code: "About.System.Services.Frontend",
|
code: "About.System.Services.Frontend",
|
||||||
url: "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector/src/branch/master/frontend"
|
url: "https://lab.code-rove.com/gitea/tudor.stanciu/network-resurrector-frontend"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: "About.System.Services.Api",
|
code: "About.System.Services.Api",
|
||||||
|
@ -39,9 +40,10 @@ const buttons = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const AboutSystemComponent = ({ handleOpenInNewTab }) => {
|
const AboutSystemComponent = ({ handleOpenInNewTab }) => {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const bullet = <span style={styles.bullet}>•</span>;
|
const bullet = <span className={classes.bullet}>•</span>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
|
@ -49,26 +51,30 @@ const AboutSystemComponent = ({ handleOpenInNewTab }) => {
|
||||||
<Typography variant="h5" gutterBottom>
|
<Typography variant="h5" gutterBottom>
|
||||||
{t("About.System.Description.Title")}
|
{t("About.System.Description.Title")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color="textSecondary">{t("About.System.Description.FirstPhrase")}</Typography>
|
<Typography color="textSecondary">
|
||||||
<Typography color="textSecondary">{t("About.System.Description.SecondPhrase")}</Typography>
|
{t("About.System.Description.FirstPhrase")}
|
||||||
|
</Typography>
|
||||||
|
<Typography color="textSecondary">
|
||||||
|
{t("About.System.Description.SecondPhrase")}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
<Typography sx={styles.service} color="textSecondary">
|
<Typography className={classes.service} color="textSecondary">
|
||||||
{bullet}
|
{bullet}
|
||||||
{t("About.System.Description.Frontend")}
|
{t("About.System.Description.Frontend")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={styles.service} color="textSecondary">
|
<Typography className={classes.service} color="textSecondary">
|
||||||
{bullet}
|
{bullet}
|
||||||
{t("About.System.Description.Api")}
|
{t("About.System.Description.Api")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={styles.service} color="textSecondary">
|
<Typography className={classes.service} color="textSecondary">
|
||||||
{bullet}
|
{bullet}
|
||||||
{t("About.System.Description.Server")}
|
{t("About.System.Description.Server")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={styles.service} color="textSecondary">
|
<Typography className={classes.service} color="textSecondary">
|
||||||
{bullet}
|
{bullet}
|
||||||
{t("About.System.Description.Agent")}
|
{t("About.System.Description.Agent")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={styles.service} color="textSecondary">
|
<Typography className={classes.service} color="textSecondary">
|
||||||
{bullet}
|
{bullet}
|
||||||
{t("About.System.Description.Tuitio")}
|
{t("About.System.Description.Tuitio")}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Box } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import AboutSystemComponent from "./AboutSystemComponent";
|
import AboutSystemComponent from "./AboutSystemComponent";
|
||||||
import SystemVersionContainer from "./SystemVersionContainer";
|
import SystemVersionContainer from "./SystemVersionContainer";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => {
|
||||||
page: {
|
return {
|
||||||
display: "flex",
|
page: {
|
||||||
flexDirection: "column"
|
display: "flex",
|
||||||
},
|
flexDirection: "column"
|
||||||
element: {
|
},
|
||||||
marginTop: 1
|
element: {
|
||||||
}
|
marginTop: theme.spacing(1)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const AboutSystemContainer = () => {
|
const AboutSystemContainer = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
const handleOpenInNewTab = url => event => {
|
const handleOpenInNewTab = url => event => {
|
||||||
window.open(url, "_blank");
|
window.open(url, "_blank");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={styles.page}>
|
<div className={classes.page}>
|
||||||
<AboutSystemComponent handleOpenInNewTab={handleOpenInNewTab} />
|
<AboutSystemComponent handleOpenInNewTab={handleOpenInNewTab} />
|
||||||
<Box sx={styles.element}>
|
<div className={classes.element}>
|
||||||
<SystemVersionContainer />
|
<SystemVersionContainer />
|
||||||
</Box>
|
</div>
|
||||||
</Box>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AboutSystemContainer;
|
export default AboutSystemContainer;
|
||||||
|
|
|
@ -1,44 +1,50 @@
|
||||||
import React, { useMemo, useEffect, useState } from "react";
|
import React, { useMemo, useEffect, useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { List, ListItem, ListItemText, ListItemAvatar } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import {
|
||||||
import WebAssetIcon from "@mui/icons-material/WebAsset";
|
List,
|
||||||
import DeveloperBoardIcon from "@mui/icons-material/DeveloperBoard";
|
ListItem,
|
||||||
import SettingsInputSvideoIcon from "@mui/icons-material/SettingsInputSvideo";
|
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 { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import packageData from "../../../../package.json";
|
import packageData from "../../../../package.json";
|
||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import { useTheme } from "@mui/material/styles";
|
|
||||||
|
|
||||||
const getStyles = theme => ({
|
const useStyles = makeStyles(theme => {
|
||||||
horizontally: {
|
return {
|
||||||
display: "flex",
|
horizontally: {
|
||||||
flexDirection: "row",
|
display: "flex",
|
||||||
padding: 0
|
flexDirection: "row",
|
||||||
},
|
padding: 0
|
||||||
vertical: {
|
},
|
||||||
width: "100%"
|
vertical: {
|
||||||
},
|
width: "100%"
|
||||||
value: {
|
},
|
||||||
fontSize: "0.9rem",
|
value: {
|
||||||
fontWeight: theme.typography.fontWeightMedium
|
fontSize: "0.9rem",
|
||||||
},
|
fontWeight: theme.typography.fontWeightMedium
|
||||||
versionAvatar: {
|
},
|
||||||
backgroundColor: theme.palette.secondary.main
|
versionAvatar: {
|
||||||
}
|
backgroundColor: theme.palette.secondary.main
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const SystemVersionComponent = ({ data }) => {
|
const SystemVersionComponent = ({ data }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [listClass, setListClass] = useState(classes.horizontally);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
|
||||||
const styles = getStyles(theme);
|
|
||||||
const [listClass, setListClass] = useState(styles.horizontally);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mediaQuery = window.matchMedia("(max-width: 800px)");
|
const mediaQuery = window.matchMedia("(max-width: 800px)");
|
||||||
|
|
||||||
function handleMatches(event) {
|
function handleMatches(event) {
|
||||||
const cssClass = event.matches ? styles.vertical : styles.horizontally;
|
const cssClass = event.matches ? classes.vertical : classes.horizontally;
|
||||||
setListClass(cssClass);
|
setListClass(cssClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +54,7 @@ const SystemVersionComponent = ({ data }) => {
|
||||||
return () => {
|
return () => {
|
||||||
mediaQuery.removeListener(handleMatches);
|
mediaQuery.removeListener(handleMatches);
|
||||||
};
|
};
|
||||||
}, [styles.horizontally, styles.vertical]);
|
}, [classes.horizontally, classes.vertical]);
|
||||||
|
|
||||||
const lastReleaseDate = useMemo(() => {
|
const lastReleaseDate = useMemo(() => {
|
||||||
const format = "DD-MM-YYYY HH:mm:ss";
|
const format = "DD-MM-YYYY HH:mm:ss";
|
||||||
|
@ -78,16 +84,16 @@ const SystemVersionComponent = ({ data }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper variant="outlined">
|
<Paper variant="outlined">
|
||||||
<List sx={listClass}>
|
<List className={listClass}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar sx={styles.versionAvatar}>
|
<Avatar className={classes.versionAvatar}>
|
||||||
<DeveloperBoardIcon />
|
<DeveloperBoardIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<span style={styles.value}>
|
<span className={classes.value}>
|
||||||
{t("About.System.Version.Server", {
|
{t("About.System.Version.Server", {
|
||||||
version: data.server.version
|
version: data.server.version
|
||||||
})}
|
})}
|
||||||
|
@ -100,13 +106,13 @@ const SystemVersionComponent = ({ data }) => {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar sx={styles.versionAvatar}>
|
<Avatar className={classes.versionAvatar}>
|
||||||
<SettingsInputSvideoIcon />
|
<SettingsInputSvideoIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<span style={styles.value}>
|
<span className={classes.value}>
|
||||||
{t("About.System.Version.Api", {
|
{t("About.System.Version.Api", {
|
||||||
version: data.api.version
|
version: data.api.version
|
||||||
})}
|
})}
|
||||||
|
@ -119,13 +125,13 @@ const SystemVersionComponent = ({ data }) => {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar sx={styles.versionAvatar}>
|
<Avatar className={classes.versionAvatar}>
|
||||||
<WebAssetIcon />
|
<WebAssetIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<span style={styles.value}>
|
<span className={classes.value}>
|
||||||
{t("About.System.Version.Frontend", {
|
{t("About.System.Version.Frontend", {
|
||||||
version: process.env.APP_VERSION ?? packageData.version
|
version: process.env.APP_VERSION ?? packageData.version
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import Timeline from "@mui/lab/Timeline";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import TimelineItem from "@mui/lab/TimelineItem";
|
import Timeline from "@material-ui/lab/Timeline";
|
||||||
import TimelineSeparator from "@mui/lab/TimelineSeparator";
|
import TimelineItem from "@material-ui/lab/TimelineItem";
|
||||||
import TimelineConnector from "@mui/lab/TimelineConnector";
|
import TimelineSeparator from "@material-ui/lab/TimelineSeparator";
|
||||||
import TimelineContent from "@mui/lab/TimelineContent";
|
import TimelineConnector from "@material-ui/lab/TimelineConnector";
|
||||||
import TimelineOppositeContent from "@mui/lab/TimelineOppositeContent";
|
import TimelineContent from "@material-ui/lab/TimelineContent";
|
||||||
import TimelineDot from "@mui/lab/TimelineDot";
|
import TimelineOppositeContent from "@material-ui/lab/TimelineOppositeContent";
|
||||||
import Paper from "@mui/material/Paper";
|
import TimelineDot from "@material-ui/lab/TimelineDot";
|
||||||
import Typography from "@mui/material/Typography";
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getRandomElement } from "../../../utils";
|
import { getRandomElement } from "../../../utils";
|
||||||
import {
|
import {
|
||||||
Announcement,
|
Announcement,
|
||||||
Book,
|
AmpStories,
|
||||||
Apps,
|
Apps,
|
||||||
BugReport,
|
BugReport,
|
||||||
DeviceHub,
|
DeviceHub,
|
||||||
|
@ -28,11 +29,11 @@ import {
|
||||||
Star,
|
Star,
|
||||||
Whatshot,
|
Whatshot,
|
||||||
Widgets
|
Widgets
|
||||||
} from "@mui/icons-material";
|
} from "@material-ui/icons";
|
||||||
|
|
||||||
const timelineIcons = [
|
const timelineIcons = [
|
||||||
Announcement,
|
Announcement,
|
||||||
Book,
|
AmpStories,
|
||||||
Apps,
|
Apps,
|
||||||
BugReport,
|
BugReport,
|
||||||
DeviceHub,
|
DeviceHub,
|
||||||
|
@ -54,7 +55,14 @@ const timelineDotVariants = [
|
||||||
{ color: "secondary", variant: "outlined" }
|
{ color: "secondary", variant: "outlined" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() => ({
|
||||||
|
paper: {
|
||||||
|
padding: "6px 16px"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const TimelineComponent = ({ releases }) => {
|
const TimelineComponent = ({ releases }) => {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const _releases = releases.map((release, index) => {
|
const _releases = releases.map((release, index) => {
|
||||||
|
@ -65,7 +73,7 @@ const TimelineComponent = ({ releases }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Timeline position="alternate">
|
<Timeline align="alternate">
|
||||||
{_releases.map(release => (
|
{_releases.map(release => (
|
||||||
<TimelineItem key={release.version}>
|
<TimelineItem key={release.version}>
|
||||||
<TimelineOppositeContent>
|
<TimelineOppositeContent>
|
||||||
|
@ -76,18 +84,16 @@ const TimelineComponent = ({ releases }) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
</TimelineOppositeContent>
|
</TimelineOppositeContent>
|
||||||
<TimelineSeparator>
|
<TimelineSeparator>
|
||||||
<TimelineDot color={release.dot.color} variant={release.dot.variant}>
|
<TimelineDot
|
||||||
|
color={release.dot.color}
|
||||||
|
variant={release.dot.variant}
|
||||||
|
>
|
||||||
<release.icon />
|
<release.icon />
|
||||||
</TimelineDot>
|
</TimelineDot>
|
||||||
{!release.isLast && <TimelineConnector />}
|
{!release.isLast && <TimelineConnector />}
|
||||||
</TimelineSeparator>
|
</TimelineSeparator>
|
||||||
<TimelineContent>
|
<TimelineContent>
|
||||||
<Paper
|
<Paper elevation={3} className={classes.paper}>
|
||||||
elevation={3}
|
|
||||||
sx={{
|
|
||||||
padding: "6px 16px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="h6" component="h1">
|
<Typography variant="h6" component="h1">
|
||||||
{release.notes[0]}
|
{release.notes[0]}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Alert, AlertTitle } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { getStyles } from "../styles";
|
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||||
|
import styles from "../styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useTheme } from "@mui/material/styles";
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
export default function GuestAnnouncement() {
|
export default function GuestAnnouncement() {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
|
||||||
const styles = getStyles(theme);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.alert}>
|
<div className={classes.alert}>
|
||||||
<Alert variant="outlined" severity="warning">
|
<Alert variant="outlined" severity="warning">
|
||||||
<AlertTitle>{t("Dashboard.Announcements.Guest.Title")}</AlertTitle>
|
<AlertTitle>{t("Dashboard.Announcements.Guest.Title")}</AlertTitle>
|
||||||
{t("Dashboard.Announcements.Guest.Message")}
|
{t("Dashboard.Announcements.Guest.Message")}
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Alert, AlertTitle } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { getStyles } from "../styles";
|
import { Alert, AlertTitle } from "@material-ui/lab";
|
||||||
|
import styles from "../styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useTuitioUser } from "@flare/tuitio-client-react";
|
import { useTuitioUser } from "@flare/tuitio-client-react";
|
||||||
import { useTheme } from "@mui/material/styles";
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
export default function UserAnnouncement() {
|
export default function UserAnnouncement() {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userName } = useTuitioUser();
|
const { userName } = useTuitioUser();
|
||||||
const theme = useTheme();
|
|
||||||
const styles = getStyles(theme);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.alert}>
|
<div className={classes.alert}>
|
||||||
<Alert variant="outlined" severity="info">
|
<Alert variant="outlined" severity="info">
|
||||||
<AlertTitle>
|
<AlertTitle>
|
||||||
{t("Dashboard.Announcements.User.Title", {
|
{t("Dashboard.Announcements.User.Title", {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const getStyles = theme => ({
|
const styles = theme => ({
|
||||||
alert: {
|
alert: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
"& > * + *": {
|
"& > * + *": {
|
||||||
|
@ -7,4 +7,4 @@ const getStyles = theme => ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export { getStyles };
|
export default styles;
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Card } from "@mui/material";
|
import { Card } from "@material-ui/core";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import styles from "../styles";
|
import styles from "../styles";
|
||||||
import LoginComponent from "./LoginComponent";
|
import LoginComponent from "./LoginComponent";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const LoginCard = ({ credentials, onChange, onLogin }) => {
|
const LoginCard = ({ credentials, onChange, onLogin }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.appLogin}>
|
<div className={classes.appLogin}>
|
||||||
<Card variant="outlined">
|
<Card variant="outlined">
|
||||||
<LoginComponent credentials={credentials} onChange={onChange} onLogin={onLogin} />
|
<LoginComponent
|
||||||
|
credentials={credentials}
|
||||||
|
onChange={onChange}
|
||||||
|
onLogin={onLogin}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,19 +1,29 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { TextField, InputAdornment, Button, CardActions, CardContent } from "@mui/material";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { AccountCircleOutlined } from "@mui/icons-material";
|
import {
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
|
Button,
|
||||||
|
CardActions,
|
||||||
|
CardContent
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import { AccountCircleOutlined } from "@material-ui/icons";
|
||||||
import PasswordField from "../../../components/common/inputs/PasswordField";
|
import PasswordField from "../../../components/common/inputs/PasswordField";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import styles from "../styles";
|
import styles from "../styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<TextField
|
<TextField
|
||||||
sx={styles.field}
|
className={classes.field}
|
||||||
id="username"
|
id="username"
|
||||||
label={t("Login.Username")}
|
label={t("Login.Username")}
|
||||||
onChange={onChange("userName")}
|
onChange={onChange("userName")}
|
||||||
|
@ -30,7 +40,7 @@ const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
||||||
<PasswordField
|
<PasswordField
|
||||||
id="password"
|
id="password"
|
||||||
label={t("Login.Password")}
|
label={t("Login.Password")}
|
||||||
sx={styles.field}
|
className={classes.field}
|
||||||
onChange={onChange("password")}
|
onChange={onChange("password")}
|
||||||
value={credentials.password}
|
value={credentials.password}
|
||||||
onKeyDown={e => {
|
onKeyDown={e => {
|
||||||
|
@ -38,8 +48,13 @@ const LoginComponent = ({ credentials, onChange, onLogin }) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions sx={styles.actions}>
|
<CardActions className={classes.actions}>
|
||||||
<Button sx={styles.onRight} variant="contained" color="primary" onClick={onLogin}>
|
<Button
|
||||||
|
className={classes.onRight}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={onLogin}
|
||||||
|
>
|
||||||
{t("Login.Label")}
|
{t("Login.Label")}
|
||||||
</Button>
|
</Button>
|
||||||
</CardActions>
|
</CardActions>
|
||||||
|
|
|
@ -26,7 +26,13 @@ const LoginContainer = () => {
|
||||||
return login(userName, password);
|
return login(userName, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <LoginCard credentials={credentials} onChange={handleChange} onLogin={handleLogin} />;
|
return (
|
||||||
|
<LoginCard
|
||||||
|
credentials={credentials}
|
||||||
|
onChange={handleChange}
|
||||||
|
onLogin={handleLogin}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginContainer;
|
export default LoginContainer;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const styles = {
|
const styles = theme => ({
|
||||||
onRight: {
|
onRight: {
|
||||||
marginLeft: "auto"
|
marginLeft: "auto"
|
||||||
},
|
},
|
||||||
|
@ -9,13 +9,13 @@ const styles = {
|
||||||
alignItems: "center"
|
alignItems: "center"
|
||||||
},
|
},
|
||||||
field: {
|
field: {
|
||||||
margin: 1,
|
margin: theme.spacing(1),
|
||||||
width: "300px"
|
width: "300px"
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
paddingRight: "16px",
|
paddingRight: "16px",
|
||||||
paddingLeft: "16px"
|
paddingLeft: "16px"
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
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;
|
|
@ -1,106 +0,0 @@
|
||||||
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 MachineAccordion from "./MachineAccordion";
|
||||||
import { ViewModes } from "./ViewModeSelection";
|
import { ViewModes } from "./ViewModeSelection";
|
||||||
import { useToast } from "../../../hooks";
|
import { useToast } from "../../../hooks";
|
||||||
import { LastPage, RotateLeft, Launch, Stop } from "@mui/icons-material";
|
import { LastPage, RotateLeft, Launch, Stop } from "@material-ui/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { routes, post } from "../../../utils/api";
|
import { routes, post } from "../../../utils/api";
|
||||||
|
|
||||||
|
@ -96,9 +96,7 @@ const MachineContainer = ({ machine, viewMode }) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: "advanced",
|
code: "advanced",
|
||||||
effect: () => {
|
effect: () => {},
|
||||||
// to do: implement
|
|
||||||
},
|
|
||||||
icon: Launch,
|
icon: Launch,
|
||||||
tooltip: t("Machine.Actions.Advanced"),
|
tooltip: t("Machine.Actions.Advanced"),
|
||||||
main: false
|
main: false
|
||||||
|
@ -108,10 +106,20 @@ const MachineContainer = ({ machine, viewMode }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{viewMode === ViewModes.TABLE && (
|
{viewMode === ViewModes.TABLE && (
|
||||||
<MachineTableRow machine={machine} actions={actions} logs={logs} addLog={addLog} />
|
<MachineTableRow
|
||||||
|
machine={machine}
|
||||||
|
actions={actions}
|
||||||
|
logs={logs}
|
||||||
|
addLog={addLog}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{viewMode === ViewModes.ACCORDION && (
|
{viewMode === ViewModes.ACCORDION && (
|
||||||
<MachineAccordion machine={machine} actions={actions} logs={logs} addLog={addLog} />
|
<MachineAccordion
|
||||||
|
machine={machine}
|
||||||
|
actions={actions}
|
||||||
|
logs={logs}
|
||||||
|
addLog={addLog}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,26 +1,34 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { TableCell, TableRow, IconButton, Collapse } from "@mui/material";
|
import { TableCell, TableRow, IconButton, Collapse } from "@material-ui/core";
|
||||||
import { KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material";
|
import { KeyboardArrowDown, KeyboardArrowUp } from "@material-ui/icons";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import MachineCollapsedContent from "./common/MachineCollapsedContent";
|
import MachineCollapsedContent from "./common/MachineCollapsedContent";
|
||||||
import { useSensitiveInfo } from "../../../hooks";
|
import { useSensitiveInfo } from "../../../hooks";
|
||||||
import ActionsGroup from "./common/ActionsGroup";
|
import ActionsGroup from "./common/ActionsGroup";
|
||||||
|
|
||||||
|
const useRowStyles = makeStyles({
|
||||||
|
root: {
|
||||||
|
"& > *": {
|
||||||
|
borderBottom: "unset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const MachineTableRow = ({ machine, actions, logs, addLog }) => {
|
const MachineTableRow = ({ machine, actions, logs, addLog }) => {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const classes = useRowStyles();
|
||||||
const { mask } = useSensitiveInfo();
|
const { mask } = useSensitiveInfo();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<TableRow
|
<TableRow className={classes.root}>
|
||||||
sx={{
|
|
||||||
"& .MuiTableCell-root": {
|
|
||||||
borderBottom: "unset"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
|
<IconButton
|
||||||
|
aria-label="expand row"
|
||||||
|
size="small"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
{open ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
|
{open ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -40,7 +48,7 @@ const MachineTableRow = ({ machine, actions, logs, addLog }) => {
|
||||||
<MachineCollapsedContent
|
<MachineCollapsedContent
|
||||||
description={machine.description}
|
description={machine.description}
|
||||||
logs={logs}
|
logs={logs}
|
||||||
style={{ paddingBottom: "0.5rem" }}
|
style={{ paddingBottom: "10px" }}
|
||||||
/>
|
/>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import React, { useContext, useEffect, useCallback, useState } from "react";
|
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 MachinesListComponent from "./MachinesListComponent";
|
||||||
import PageTitle from "../../../components/common/PageTitle";
|
import PageTitle from "../../../components/common/PageTitle";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
@ -32,9 +35,19 @@ const MachinesContainer = () => {
|
||||||
<>
|
<>
|
||||||
<PageTitle
|
<PageTitle
|
||||||
text={t("Menu.Machines")}
|
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,7 +1,14 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
|
import {
|
||||||
import Paper from "@mui/material/Paper";
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TableRow
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import Paper from "@material-ui/core/Paper";
|
||||||
import MachineContainer from "./MachineContainer";
|
import MachineContainer from "./MachineContainer";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ViewModes } from "./ViewModeSelection";
|
import { ViewModes } from "./ViewModeSelection";
|
||||||
|
@ -10,7 +17,11 @@ const MachinesList = ({ machines, viewMode }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{machines.map(machine => (
|
{machines.map(machine => (
|
||||||
<MachineContainer key={`machine-${machine.machineId}`} machine={machine} viewMode={viewMode} />
|
<MachineContainer
|
||||||
|
key={`machine-${machine.machineId}`}
|
||||||
|
machine={machine}
|
||||||
|
viewMode={viewMode}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import TableChartIcon from "@mui/icons-material/TableChart";
|
import TableChartIcon from "@material-ui/icons/TableChart";
|
||||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||||
import { ToggleButtonGroup, ToggleButton } from "@mui/material";
|
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||||
import { Tooltip } from "@mui/material";
|
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||||
|
import { Tooltip } from "@material-ui/core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export const ViewModes = {
|
export const ViewModes = {
|
||||||
|
@ -43,8 +44,17 @@ const ViewModeSelection = ({ initialMode, callback }) => {
|
||||||
useEffect(() => callback && callback(state.mode), [callback, state.mode]);
|
useEffect(() => callback && callback(state.mode), [callback, state.mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButtonGroup size="small" value={state.mode} exclusive onChange={handleViewModeSelection}>
|
<ToggleButtonGroup
|
||||||
<ToggleButton value={ViewModes.TABLE} aria-label="table view mode" disabled={state.mode === ViewModes.TABLE}>
|
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")}>
|
<Tooltip title={t("ViewModes.Table")}>
|
||||||
<TableChartIcon />
|
<TableChartIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { IconButton, Tooltip } from "@mui/material";
|
import { IconButton, Tooltip } from "@material-ui/core";
|
||||||
|
|
||||||
const ActionButton = React.forwardRef(props => {
|
const ActionButton = React.forwardRef((props, _ref) => {
|
||||||
const { action, machine, callback, disabled } = props;
|
const { action, machine, callback, disabled } = props;
|
||||||
const id = `machine-item-${machine.machineId}-${action.code}`;
|
const id = `machine-item-${machine.machineId}-${action.code}`;
|
||||||
const handleActionClick = event => {
|
const handleActionClick = event => {
|
||||||
|
@ -12,12 +12,14 @@ const ActionButton = React.forwardRef(props => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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>
|
<span>
|
||||||
<IconButton
|
<IconButton
|
||||||
id={id}
|
id={id}
|
||||||
size={"small"}
|
size={"small"}
|
||||||
sx={{ padding: "0.2rem" }}
|
|
||||||
onFocus={event => event.stopPropagation()}
|
onFocus={event => event.stopPropagation()}
|
||||||
onClick={handleActionClick}
|
onClick={handleActionClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -42,6 +44,4 @@ ActionButton.propTypes = {
|
||||||
disabled: PropTypes.bool
|
disabled: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionButton.displayName = "ActionButton";
|
|
||||||
|
|
||||||
export default ActionButton;
|
export default ActionButton;
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React, { useMemo, useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import WakeComponent from "./WakeComponent";
|
import WakeComponent from "./WakeComponent";
|
||||||
import ActionButton from "./ActionButton";
|
import ActionButton from "./ActionButton";
|
||||||
import { Menu } from "@mui/material";
|
import { Menu } from "@material-ui/core";
|
||||||
import { MoreHoriz } from "@mui/icons-material";
|
import { MoreHoriz } from "@material-ui/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { usePermissions } from "../../../../hooks";
|
import { usePermissions } from "../../../../hooks";
|
||||||
|
|
||||||
|
@ -13,9 +13,15 @@ const ActionsGroup = ({ machine, actions, addLog }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { operateMachines: canOperateMachines } = usePermissions();
|
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) => {
|
const handleMenuOpen = (_, event) => {
|
||||||
setMenuAnchor(event.currentTarget);
|
setMenuAnchor(event.currentTarget);
|
||||||
|
@ -27,33 +33,29 @@ const ActionsGroup = ({ machine, actions, addLog }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<WakeComponent
|
||||||
style={{
|
machine={machine}
|
||||||
display: "flex",
|
addLog={addLog}
|
||||||
flexDirection: "row",
|
disabled={!canOperateMachines}
|
||||||
justifyContent: "flex-end",
|
/>
|
||||||
alignItems: "center"
|
{mainActions.map(action => (
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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
|
<ActionButton
|
||||||
action={{
|
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||||
code: "more",
|
action={action}
|
||||||
effect: handleMenuOpen,
|
|
||||||
icon: MoreHoriz,
|
|
||||||
tooltip: t("Machine.Actions.More")
|
|
||||||
}}
|
|
||||||
machine={machine}
|
machine={machine}
|
||||||
|
disabled={!canOperateMachines}
|
||||||
/>
|
/>
|
||||||
</div>
|
))}
|
||||||
|
<ActionButton
|
||||||
|
action={{
|
||||||
|
code: "more",
|
||||||
|
effect: handleMenuOpen,
|
||||||
|
icon: MoreHoriz,
|
||||||
|
tooltip: t("Machine.Actions.More")
|
||||||
|
}}
|
||||||
|
machine={machine}
|
||||||
|
/>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
id="secondary-actions-menu"
|
id="secondary-actions-menu"
|
||||||
anchorEl={menuAnchor}
|
anchorEl={menuAnchor}
|
||||||
|
|
|
@ -1,21 +1,27 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import MachineLog from "./MachineLog";
|
import MachineLog from "./MachineLog";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { useSensitiveInfo } from "../../../../hooks";
|
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 MachineDescription = ({ description }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={classes.panel}>
|
||||||
style={{
|
|
||||||
display: "flex"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{
|
className={classes.label}
|
||||||
marginRight: "4px"
|
|
||||||
}}
|
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
>
|
>
|
||||||
{"Description:"}
|
{"Description:"}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@material-ui/core";
|
||||||
import { useSensitiveInfo } from "../../../../hooks";
|
import { useSensitiveInfo } from "../../../../hooks";
|
||||||
import { LazyLog, ScrollFollow } from "react-lazylog";
|
import { LazyLog, ScrollFollow } from "react-lazylog";
|
||||||
|
|
||||||
const MachineLog = ({ logs }) => {
|
const MachineLog = ({ logs }) => {
|
||||||
const { maskElements } = useSensitiveInfo();
|
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 (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { IconButton, Tooltip } from "@mui/material";
|
import { IconButton, Tooltip } from "@material-ui/core";
|
||||||
import { PowerSettingsNew } from "@mui/icons-material";
|
import { PowerSettingsNew } from "@material-ui/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useToast } from "../../../../hooks";
|
import { useToast } from "../../../../hooks";
|
||||||
import { msToMinAndSec } from "../../../../utils/time";
|
import { msToMinAndSec } from "../../../../utils/time";
|
||||||
|
@ -18,8 +18,10 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { success, error } = useToast();
|
const { success, error } = useToast();
|
||||||
|
|
||||||
const pingInterval = process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval;
|
const pingInterval =
|
||||||
const startingTime = process.env.REACT_APP_MACHINE_STARTING_TIME || defaultStartingTime;
|
process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval;
|
||||||
|
const startingTime =
|
||||||
|
process.env.REACT_APP_MACHINE_STARTING_TIME || defaultStartingTime;
|
||||||
|
|
||||||
const getCurrentDateTime = useCallback(() => {
|
const getCurrentDateTime = useCallback(() => {
|
||||||
const currentDateTime = Date.now();
|
const currentDateTime = Date.now();
|
||||||
|
@ -29,7 +31,10 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
||||||
return result;
|
return result;
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
const log = useCallback(message => addLog(`[${getCurrentDateTime()}] ${message}`), [addLog, getCurrentDateTime]);
|
const log = useCallback(
|
||||||
|
message => addLog(`[${getCurrentDateTime()}] ${message}`),
|
||||||
|
[addLog, getCurrentDateTime]
|
||||||
|
);
|
||||||
|
|
||||||
const wakeMachine = useCallback(async () => {
|
const wakeMachine = useCallback(async () => {
|
||||||
await post(
|
await post(
|
||||||
|
@ -43,7 +48,11 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
||||||
success(result.status);
|
success(result.status);
|
||||||
|
|
||||||
//retrigger
|
//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(() => {
|
setTimeout(() => {
|
||||||
setTrigger(prev => !prev);
|
setTrigger(prev => !prev);
|
||||||
}, startingTime);
|
}, startingTime);
|
||||||
|
@ -71,16 +80,12 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
||||||
}, pingInterval);
|
}, pingInterval);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {}
|
||||||
// to do: handle error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [machine, log, pingInterval, disabled]);
|
}, [machine, log, pingInterval, disabled]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(pingInLoop, [trigger, pingInLoop]);
|
||||||
pingInLoop();
|
|
||||||
}, [trigger, pingInLoop]);
|
|
||||||
|
|
||||||
const handleWakeClick = event => {
|
const handleWakeClick = event => {
|
||||||
wakeMachine();
|
wakeMachine();
|
||||||
|
@ -95,7 +100,6 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
|
||||||
size={"small"}
|
size={"small"}
|
||||||
disabled={disabled || state.on}
|
disabled={disabled || state.on}
|
||||||
onClick={handleWakeClick}
|
onClick={handleWakeClick}
|
||||||
sx={{ padding: "0.2rem" }}
|
|
||||||
style={state.on ? { color: "#33cc33" } : undefined}
|
style={state.on ? { color: "#33cc33" } : undefined}
|
||||||
onFocus={event => event.stopPropagation()}
|
onFocus={event => event.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
|
|
@ -4,10 +4,10 @@ import NetworkStateProvider from "../state/NetworkStateProvider";
|
||||||
import { usePermissions } from "../../../hooks";
|
import { usePermissions } from "../../../hooks";
|
||||||
import NotAllowed from "../../../components/common/NotAllowed";
|
import NotAllowed from "../../../components/common/NotAllowed";
|
||||||
|
|
||||||
const NetworkContainer = (): JSX.Element | null => {
|
const NetworkContainer = () => {
|
||||||
const { loading, viewMachines } = usePermissions();
|
const { loading, viewMachines } = usePermissions();
|
||||||
|
|
||||||
if (loading) return null;
|
if (loading) return "";
|
||||||
if (!viewMachines) return <NotAllowed />;
|
if (!viewMachines) return <NotAllowed />;
|
||||||
|
|
||||||
return (
|
return (
|
|
@ -6,11 +6,16 @@ import { initialState } from "./initialState";
|
||||||
|
|
||||||
const NetworkStateProvider = ({ children }) => {
|
const NetworkStateProvider = ({ children }) => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
const dispatchActions = useMemo(() => reducerDispatchActions(dispatch), [dispatch]);
|
const dispatchActions = useMemo(
|
||||||
|
() => reducerDispatchActions(dispatch),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NetworkStateContext.Provider value={state}>
|
<NetworkStateContext.Provider value={state}>
|
||||||
<NetworkDispatchContext.Provider value={dispatchActions}>{children}</NetworkDispatchContext.Provider>
|
<NetworkDispatchContext.Provider value={dispatchActions}>
|
||||||
|
{children}
|
||||||
|
</NetworkDispatchContext.Provider>
|
||||||
</NetworkStateContext.Provider>
|
</NetworkStateContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,5 +17,6 @@ export function reducer(state, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dispatchActions = dispatch => ({
|
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 React, { useState, useMemo } from "react";
|
||||||
import BubbleChartIcon from "@mui/icons-material/BubbleChart";
|
import BubbleChartIcon from "@material-ui/icons/BubbleChart";
|
||||||
import BrushIcon from "@mui/icons-material/Brush";
|
import BrushIcon from "@material-ui/icons/Brush";
|
||||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
import NotificationsIcon from "@material-ui/icons/Notifications";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import PageTitle from "../../components/common/PageTitle";
|
import PageTitle from "../../components/common/PageTitle";
|
||||||
import NavigationButtons from "../../components/common/NavigationButtons";
|
import NavigationButtons from "../../components/common/NavigationButtons";
|
||||||
|
@ -34,11 +34,19 @@ const SettingsContainer = () => {
|
||||||
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
const [tab, setTab] = useState(NavigationTabs.SYSTEM);
|
||||||
const { t } = useTranslation();
|
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 (
|
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.SYSTEM && <SystemContainer />}
|
||||||
{tab === NavigationTabs.APPEARANCE && <AppearanceContainer />}
|
{tab === NavigationTabs.APPEARANCE && <AppearanceContainer />}
|
||||||
{tab === NavigationTabs.NOTIFICATIONS && <NotificationsContainer />}
|
{tab === NavigationTabs.NOTIFICATIONS && <NotificationsContainer />}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useApplicationTheme } from "../../../providers/ThemeProvider";
|
import { useApplicationTheme } from "../../../providers/ThemeProvider";
|
||||||
import { Grid, Paper, FormControlLabel, Switch, Box } from "@mui/material";
|
import { Grid, Paper, FormControlLabel, Switch } from "@material-ui/core";
|
||||||
import LanguageContainer from "./language/LanguageContainer";
|
import LanguageContainer from "./language/LanguageContainer";
|
||||||
import { PaperTitle } from "components/common";
|
import { PaperTitle } from "components/common";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const styles = {
|
const useStyles = makeStyles(theme => ({
|
||||||
language: {
|
language: {
|
||||||
paddingLeft: 1
|
paddingLeft: theme.spacing(1)
|
||||||
}
|
}
|
||||||
};
|
}));
|
||||||
|
|
||||||
const AppearanceComponent = () => {
|
const AppearanceComponent = () => {
|
||||||
const { isDark, onDarkModeChanged } = useApplicationTheme();
|
const { isDark, onDarkModeChanged } = useApplicationTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
const handleChange = event => {
|
const handleChange = event => {
|
||||||
const { checked } = event.target;
|
const { checked } = event.target;
|
||||||
|
@ -27,7 +29,14 @@ const AppearanceComponent = () => {
|
||||||
<Grid item xs={12} sm={6} md={4} lg={3}>
|
<Grid item xs={12} sm={6} md={4} lg={3}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="start"
|
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:"
|
label="Dark mode:"
|
||||||
labelPlacement="start"
|
labelPlacement="start"
|
||||||
/>
|
/>
|
||||||
|
@ -36,9 +45,9 @@ const AppearanceComponent = () => {
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="start"
|
value="start"
|
||||||
control={
|
control={
|
||||||
<Box sx={styles.language}>
|
<div className={classes.language}>
|
||||||
<LanguageContainer />
|
<LanguageContainer />
|
||||||
</Box>
|
</div>
|
||||||
}
|
}
|
||||||
label="Language:"
|
label="Language:"
|
||||||
labelPlacement="start"
|
labelPlacement="start"
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
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;
|
|
@ -1,54 +0,0 @@
|
||||||
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,15 +2,25 @@ import React, { useState, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import LanguageComponent from "./LanguageComponent";
|
import LanguageComponent from "./LanguageComponent";
|
||||||
|
|
||||||
|
const flagsPath = process.env.PUBLIC_URL
|
||||||
|
? `${process.env.PUBLIC_URL}/flags`
|
||||||
|
: "flags";
|
||||||
|
|
||||||
const LanguageContainer = () => {
|
const LanguageContainer = () => {
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
|
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const [flagCode, setFlagCode] = useState("RO");
|
const [flag, setFlag] = useState({
|
||||||
|
name: "RO",
|
||||||
|
alt: "-"
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!i18n.language) return;
|
if (!i18n.language) return;
|
||||||
setFlagCode(i18n.language === "en" ? "GB" : i18n.language.toUpperCase());
|
setFlag({
|
||||||
|
name: i18n.language === "en" ? "GB" : i18n.language.toUpperCase(),
|
||||||
|
alt: i18n.language
|
||||||
|
});
|
||||||
}, [i18n.language]);
|
}, [i18n.language]);
|
||||||
|
|
||||||
const handleMenuOpen = event => {
|
const handleMenuOpen = event => {
|
||||||
|
@ -35,7 +45,8 @@ const LanguageContainer = () => {
|
||||||
onMenuOpen={handleMenuOpen}
|
onMenuOpen={handleMenuOpen}
|
||||||
onLanguageChange={handleLanguageChange}
|
onLanguageChange={handleLanguageChange}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
flagCode={flagCode}
|
flag={flag}
|
||||||
|
flagsPath={flagsPath}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,9 @@ import React from "react";
|
||||||
const NotificationsContainer = () => {
|
const NotificationsContainer = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Enable/Disable email notifications (for each one separately - when starting the machine, when stopping) You can go
|
Enable/Disable email notifications (for each one separately - when
|
||||||
even further and have an advanced site where you can configure each individual machine.
|
starting the machine, when stopping) You can go even further and have an
|
||||||
|
advanced site where you can configure each individual machine.
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,37 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Paper, Button, Box } from "@mui/material";
|
import { Paper, Button } from "@material-ui/core";
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PaperTitle } from "components/common";
|
import { PaperTitle } from "components/common";
|
||||||
import { usePermissions } from "hooks";
|
import { usePermissions } from "hooks";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
content: {
|
||||||
|
"& > *": {
|
||||||
|
margin: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const CacheSettingsComponent = ({ onResetCache }) => {
|
const CacheSettingsComponent = ({ onResetCache }) => {
|
||||||
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sysAdmin } = usePermissions();
|
const { sysAdmin } = usePermissions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper variant="outlined">
|
<Paper variant="outlined">
|
||||||
<PaperTitle text={t("Settings.Cache.Title")} />
|
<PaperTitle text={t("Settings.Cache.Title")} />
|
||||||
<Box sx={{ margin: 1 }}>
|
<div className={classes.content}>
|
||||||
<Button variant="outlined" color="secondary" disabled={!sysAdmin} onClick={onResetCache}>
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
disabled={!sysAdmin}
|
||||||
|
onClick={onResetCache}
|
||||||
|
>
|
||||||
{t("Settings.Cache.Reset")}
|
{t("Settings.Cache.Reset")}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useMemo } from "react";
|
import React, { useState, useMemo } from "react";
|
||||||
import CategoryIcon from "@mui/icons-material/Category";
|
import CategoryIcon from "@material-ui/icons/Category";
|
||||||
import GrainIcon from "@mui/icons-material/Grain";
|
import GrainIcon from "@material-ui/icons/Grain";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import PageTitle from "../../components/common/PageTitle";
|
import PageTitle from "../../components/common/PageTitle";
|
||||||
import NavigationButtons from "../../components/common/NavigationButtons";
|
import NavigationButtons from "../../components/common/NavigationButtons";
|
||||||
|
@ -27,11 +27,19 @@ const SystemContainer = () => {
|
||||||
const [tab, setTab] = useState(NavigationTabs.MAIN_SERVICES);
|
const [tab, setTab] = useState(NavigationTabs.MAIN_SERVICES);
|
||||||
const { t } = useTranslation();
|
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 (
|
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.MAIN_SERVICES && <MainServicesContainer />}
|
||||||
{tab === NavigationTabs.AGENTS && <AgentsContainer />}
|
{tab === NavigationTabs.AGENTS && <AgentsContainer />}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -2,15 +2,19 @@ import React, { useMemo } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import UserProfilePicture from "./UserProfilePicture";
|
import UserProfilePicture from "./UserProfilePicture";
|
||||||
import ContactOptions from "../contact/ContactOptions";
|
import ContactOptions from "../contact/ContactOptions";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { getStyles } from "../styles";
|
import styles from "../styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const UserProfileCardContent = ({ userData }) => {
|
const UserProfileCardContent = ({ userData }) => {
|
||||||
const { profilePictureUrl } = userData;
|
const { profilePictureUrl } = userData;
|
||||||
const theme = useTheme();
|
const classes = useStyles();
|
||||||
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(
|
const _contactOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
profilePictureUrl
|
profilePictureUrl
|
||||||
|
@ -27,7 +31,7 @@ const UserProfileCardContent = ({ userData }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.panel}>
|
<div className={classes.panel}>
|
||||||
<UserProfilePicture pictureUrl={userData.profilePictureUrl} />
|
<UserProfilePicture pictureUrl={userData.profilePictureUrl} />
|
||||||
<ContactOptions contactOptions={_contactOptions} userName={userName} />
|
<ContactOptions contactOptions={_contactOptions} userName={userName} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardHeader, CardContent } from "@mui/material";
|
import { Card, CardHeader, CardContent } from "@material-ui/core";
|
||||||
import PageTitle from "../../../../components/common/PageTitle";
|
import PageTitle from "../../../../components/common/PageTitle";
|
||||||
import UserProfileCardContent from "./UserProfileCardContent";
|
import UserProfileCardContent from "./UserProfileCardContent";
|
||||||
import SecurityComponent from "../security/SecurityComponent";
|
import SecurityComponent from "../security/SecurityComponent";
|
||||||
import { getStyles } from "../styles";
|
import styles from "../styles";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const UserProfileComponent = ({ userData }) => {
|
const UserProfileComponent = ({ userData }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const classes = useStyles();
|
||||||
const styles = getStyles(theme);
|
|
||||||
|
|
||||||
const userLoginDate = useMemo(
|
const userLoginDate = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -28,7 +29,9 @@ const UserProfileComponent = ({ userData }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageTitle text={t("User.Profile.Hello", { userName: userData.firstName })} />
|
<PageTitle
|
||||||
|
text={t("User.Profile.Hello", { userName: userData.firstName })}
|
||||||
|
/>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title={userData.userName} subheader={userDescription} />
|
<CardHeader title={userData.userName} subheader={userDescription} />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@ -36,8 +39,11 @@ const UserProfileComponent = ({ userData }) => {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div style={styles.section}>
|
<div className={classes.section}>
|
||||||
<SecurityComponent userGroups={userData.userGroups} userRoles={userData.userRoles} />
|
<SecurityComponent
|
||||||
|
userGroups={userData.userGroups}
|
||||||
|
userRoles={userData.userRoles}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import { useTuitioUserInfo } from "@flare/tuitio-client-react";
|
import { useTuitioUserInfo } from "@flare/tuitio-client-react";
|
||||||
import UserProfileComponent from "./UserProfileComponent";
|
import UserProfileComponent from "./UserProfileComponent";
|
||||||
|
|
||||||
const UserProfileContainer: React.FC = () => {
|
const UserProfileContainer = () => {
|
||||||
const { userInfo } = useTuitioUserInfo();
|
const { userInfo } = useTuitioUserInfo();
|
||||||
return <>{userInfo && <UserProfileComponent userData={userInfo} />}</>;
|
return <>{userInfo && <UserProfileComponent userData={userInfo} />}</>;
|
||||||
};
|
};
|
|
@ -1,15 +1,16 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { getStyles } from "../styles";
|
import style from "../styles";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@material-ui/core/Avatar";
|
||||||
import DefaultUserProfilePicture from "../../../../assets/images/DefaultUserProfilePicture.png";
|
import DefaultUserProfilePicture from "../../../../assets/images/DefaultUserProfilePicture.png";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(style);
|
||||||
|
|
||||||
const UserProfilePicture = ({ pictureUrl }) => {
|
const UserProfilePicture = ({ pictureUrl }) => {
|
||||||
const theme = useTheme();
|
const classes = useStyles();
|
||||||
const styles = getStyles(theme);
|
|
||||||
const url = pictureUrl ?? DefaultUserProfilePicture;
|
const url = pictureUrl ?? DefaultUserProfilePicture;
|
||||||
return <Avatar src={url} alt="..." sx={styles.profilePicture} />;
|
return <Avatar src={url} alt="..." className={classes.profilePicture} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
UserProfilePicture.propTypes = {
|
UserProfilePicture.propTypes = {
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { ListItem, ListItemText, ListItemIcon, Link, Tooltip, IconButton } from "@mui/material";
|
import {
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
ListItemIcon,
|
||||||
|
Link,
|
||||||
|
Tooltip,
|
||||||
|
IconButton
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
const ContactIcon = ({ onIconClick, iconTooltip, ...props }) => {
|
const ContactIcon = ({ onIconClick, iconTooltip, ...props }) => {
|
||||||
if (!onIconClick) return <props.icon />;
|
if (!onIconClick) return <props.icon />;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { List } from "@mui/material";
|
import { List } from "@material-ui/core";
|
||||||
import ContactOption from "./ContactOption";
|
import ContactOption from "./ContactOption";
|
||||||
|
|
||||||
const ContactOptionList = ({ options }) => {
|
const ContactOptionList = ({ options }) => {
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@material-ui/core";
|
||||||
import ContactOptionList from "./ContactOptionList";
|
import ContactOptionList from "./ContactOptionList";
|
||||||
import BusinessCenterIcon from "@mui/icons-material/BusinessCenter";
|
import BusinessCenterIcon from "@material-ui/icons/BusinessCenter";
|
||||||
import EmailIcon from "@mui/icons-material/Email";
|
import EmailIcon from "@material-ui/icons/Email";
|
||||||
import PhoneAndroidIcon from "@mui/icons-material/PhoneAndroid";
|
import PhoneAndroidIcon from "@material-ui/icons/PhoneAndroid";
|
||||||
import LanguageIcon from "@mui/icons-material/Language";
|
import LanguageIcon from "@material-ui/icons/Language";
|
||||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
import LinkedInIcon from "@material-ui/icons/LinkedIn";
|
||||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
import GitHubIcon from "@material-ui/icons/GitHub";
|
||||||
import RedditIcon from "@mui/icons-material/Reddit";
|
import RedditIcon from "@material-ui/icons/Reddit";
|
||||||
import BookIcon from "@mui/icons-material/Book";
|
import BookIcon from "@material-ui/icons/Book";
|
||||||
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
import MenuBookIcon from "@material-ui/icons/MenuBook";
|
||||||
import FileCopyOutlinedIcon from "@mui/icons-material/FileCopyOutlined";
|
import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined";
|
||||||
import { useClipboard } from "../../../../hooks";
|
import { useClipboard } from "../../../../hooks";
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
|
@ -62,7 +62,8 @@ const getTooltip = contactOption => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrderNumber = contactOption => {
|
const getOrderNumber = contactOption => {
|
||||||
const orderNo = orderNumbers[contactOption.contactTypeCode] || orderNumbers.DEFAULT;
|
const orderNo =
|
||||||
|
orderNumbers[contactOption.contactTypeCode] || orderNumbers.DEFAULT;
|
||||||
return orderNo;
|
return orderNo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,7 +152,10 @@ const ContactOptions = ({ contactOptions, userName }) => {
|
||||||
[contactOptions, getOnClickEvent, getIconClickEvent, t, 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]);
|
const chunks = useMemo(() => sliceContactOptions(sorted), [sorted]);
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,43 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Paper, Grid, Chip, Typography } from "@mui/material";
|
import { Paper, Grid, Chip, Typography } from "@material-ui/core";
|
||||||
import { getStyles } from "../styles";
|
import styles from "../styles";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles);
|
||||||
|
|
||||||
const SecurityComponent = ({ userGroups, userRoles }) => {
|
const SecurityComponent = ({ userGroups, userRoles }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const classes = useStyles();
|
||||||
const styles = getStyles(theme);
|
|
||||||
return (
|
return (
|
||||||
<Paper>
|
<Paper>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<div style={styles.paper}>
|
<div className={classes.paper}>
|
||||||
<Typography gutterBottom variant="body1">
|
<Typography gutterBottom variant="body1">
|
||||||
{t("User.Profile.Security.UserGroups")}
|
{t("User.Profile.Security.UserGroups")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<div>
|
<div>
|
||||||
{userGroups.map(g => (
|
{userGroups.map(g => (
|
||||||
<Chip key={g.code} style={styles.chip} label={g.name} />
|
<Chip key={g.code} className={classes.chip} label={g.name} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<div style={styles.paper}>
|
<div className={classes.paper}>
|
||||||
<Typography gutterBottom variant="body1">
|
<Typography gutterBottom variant="body1">
|
||||||
{t("User.Profile.Security.UserRoles")}
|
{t("User.Profile.Security.UserRoles")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<div>
|
<div>
|
||||||
{userRoles.map(r => (
|
{userRoles.map(r => (
|
||||||
<Chip key={r.code} style={styles.chip} color="primary" label={r.name} />
|
<Chip
|
||||||
|
key={r.code}
|
||||||
|
className={classes.chip}
|
||||||
|
color="primary"
|
||||||
|
label={r.name}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const getStyles = theme => {
|
const style = theme => {
|
||||||
return {
|
return {
|
||||||
section: {
|
section: {
|
||||||
marginTop: theme.spacing(2)
|
marginTop: theme.spacing(2)
|
||||||
|
@ -6,7 +6,7 @@ const getStyles = theme => {
|
||||||
panel: {
|
panel: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
"@media (maxWidth: 600px)": {
|
"@media (max-width: 600px)": {
|
||||||
flexDirection: "column" // change direction for small screens
|
flexDirection: "column" // change direction for small screens
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -25,4 +25,4 @@ const getStyles = theme => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getStyles };
|
export default style;
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense } from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom";
|
||||||
import ThemeProvider from "./providers/ThemeProvider";
|
import ThemeProvider from "./providers/ThemeProvider";
|
||||||
import { CssBaseline } from "@mui/material";
|
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||||
import AppRouter from "./components/AppRouter";
|
import AppRouter from "./components/AppRouter";
|
||||||
import { TuitioProvider } from "@flare/tuitio-client-react";
|
import { TuitioProvider } from "@flare/tuitio-client-react";
|
||||||
import { ToastProvider } from "./providers";
|
import { ToastProvider } from "./providers";
|
||||||
import env from "./utils/env";
|
import env from "./utils/env";
|
||||||
import "./utils/i18n";
|
import "./utils/i18n";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
ReactDOM.render(
|
||||||
|
|
||||||
root.render(
|
|
||||||
<TuitioProvider tuitioUrl={env.REACT_APP_TUITIO_URL}>
|
<TuitioProvider tuitioUrl={env.REACT_APP_TUITIO_URL}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
@ -20,5 +18,6 @@ root.render(
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</TuitioProvider>
|
</TuitioProvider>,
|
||||||
|
document.getElementById("root")
|
||||||
);
|
);
|
|
@ -23,7 +23,8 @@ const reducer = (state = initialState, action) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const dispatchActions = dispatch => ({
|
const dispatchActions = dispatch => ({
|
||||||
onSensitiveInfoEnabled: enabled => dispatch({ type: "onSensitiveInfoEnabled", payload: { enabled } })
|
onSensitiveInfoEnabled: enabled =>
|
||||||
|
dispatch({ type: "onSensitiveInfoEnabled", payload: { enabled } })
|
||||||
});
|
});
|
||||||
|
|
||||||
const useSensitiveInfo = () => {
|
const useSensitiveInfo = () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useReducer, useMemo, useContext } from "react";
|
import React, { useReducer, useMemo, useContext } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { ThemeProvider as MuiThemeProvider } from "@mui/material/styles";
|
import { ThemeProvider as MuiThemeProvider } from "@material-ui/styles";
|
||||||
import { localStorage } from "@flare/js-utils";
|
import { localStorage } from "@flare/js-utils";
|
||||||
import { getThemes } from "../themes";
|
import { getThemes } from "../themes";
|
||||||
|
|
||||||
|
@ -13,10 +13,13 @@ const COLOR_SCHEME = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const colorScheme = localStorage.getItem(LOCAL_STORAGE_COLOR_SCHEME_KEY);
|
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 = {
|
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) => {
|
const reducer = (state = initialState, action) => {
|
||||||
|
@ -45,7 +48,8 @@ const useApplicationTheme = () => {
|
||||||
const { onColorSchemeChanged } = actions;
|
const { onColorSchemeChanged } = actions;
|
||||||
|
|
||||||
const { scheme } = state;
|
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 };
|
return { isDark: scheme === COLOR_SCHEME.DARK, onDarkModeChanged };
|
||||||
};
|
};
|
||||||
|
@ -53,7 +57,10 @@ const useApplicationTheme = () => {
|
||||||
const ThemeProvider = ({ children }) => {
|
const ThemeProvider = ({ children }) => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
const actions = useMemo(() => dispatchActions(dispatch), [dispatch]);
|
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 (
|
return (
|
||||||
<ApplicationThemeContext.Provider
|
<ApplicationThemeContext.Provider
|
||||||
|
|
|
@ -19,14 +19,22 @@ const initialState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPermissionFlags = permissions => {
|
const getPermissionFlags = permissions => {
|
||||||
const viewDashboard = permissions.includes(permissionCodes.VIEW_DASHBOARD) ?? false;
|
const viewDashboard =
|
||||||
const manageUsers = permissions.includes(permissionCodes.MANAGE_USERS) ?? false;
|
permissions.includes(permissionCodes.VIEW_DASHBOARD) ?? false;
|
||||||
const manageSettings = permissions.includes(permissionCodes.MANAGE_SETTINGS) ?? false;
|
const manageUsers =
|
||||||
const viewMachines = permissions.includes(permissionCodes.VIEW_MACHINES) ?? false;
|
permissions.includes(permissionCodes.MANAGE_USERS) ?? false;
|
||||||
const manageMachines = permissions.includes(permissionCodes.MANAGE_MACHINES) ?? false;
|
const manageSettings =
|
||||||
const operateMachines = permissions.includes(permissionCodes.OPERATE_MACHINES) ?? false;
|
permissions.includes(permissionCodes.MANAGE_SETTINGS) ?? false;
|
||||||
const guestAccess = permissions.includes(permissionCodes.GUEST_ACCESS) ?? false;
|
const viewMachines =
|
||||||
const sysAdmin = permissions.includes(permissionCodes.SYSTEM_ADMINISTRATION) ?? false;
|
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 = {
|
const flags = {
|
||||||
viewDashboard,
|
viewDashboard,
|
||||||
|
@ -66,7 +74,11 @@ const UserPermissionsProvider = ({ children }) => {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <UserPermissionsContext.Provider value={permissions}>{children}</UserPermissionsContext.Provider>;
|
return (
|
||||||
|
<UserPermissionsContext.Provider value={permissions}>
|
||||||
|
{children}
|
||||||
|
</UserPermissionsContext.Provider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
UserPermissionsProvider.propTypes = {
|
UserPermissionsProvider.propTypes = {
|
||||||
|
|
|
@ -3,4 +3,9 @@ import ToastProvider from "./ToastProvider";
|
||||||
import SensitiveInfoProvider from "./SensitiveInfoProvider";
|
import SensitiveInfoProvider from "./SensitiveInfoProvider";
|
||||||
import UserPermissionsProvider from "./UserPermissionsProvider";
|
import UserPermissionsProvider from "./UserPermissionsProvider";
|
||||||
|
|
||||||
export { ThemeProvider, ToastProvider, SensitiveInfoProvider, UserPermissionsProvider };
|
export {
|
||||||
|
ThemeProvider,
|
||||||
|
ToastProvider,
|
||||||
|
SensitiveInfoProvider,
|
||||||
|
UserPermissionsProvider
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
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;
|
|
@ -1,69 +0,0 @@
|
||||||
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 };
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
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 };
|
|
@ -1,43 +0,0 @@
|
||||||
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 };
|
|
|
@ -1,5 +0,0 @@
|
||||||
import * as models from "./models";
|
|
||||||
|
|
||||||
export * from "./models";
|
|
||||||
|
|
||||||
export { models };
|
|
|
@ -1,8 +0,0 @@
|
||||||
export type Machine = {
|
|
||||||
machineId: number;
|
|
||||||
machineName: string;
|
|
||||||
fullMachineName: string;
|
|
||||||
macAddress: string;
|
|
||||||
iPv4Address?: string;
|
|
||||||
description?: string;
|
|
||||||
};
|
|
|
@ -38,7 +38,7 @@ const handleError = err => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
onCompleted: () => null,
|
onCompleted: () => {},
|
||||||
onError: handleError
|
onError: handleError
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue