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