From 3cc5d8f6f35b671f2d25e587b75dabec19e452d8 Mon Sep 17 00:00:00 2001 From: Tudor Stanciu Date: Tue, 21 Mar 2023 18:14:32 +0200 Subject: [PATCH] machines view mode option --- package-lock.json | 17 +++++ package.json | 2 + public/locales/en/translations.json | 5 +- public/locales/ro/translations.json | 5 +- src/components/common/PageTitle.js | 24 ++++--- src/components/layout/ProfileButton.js | 29 ++++++++- src/components/layout/styles.js | 3 + .../components/MachinesAccordionList.js | 63 +++++++++++++++++++ .../machines/components/MachinesContainer.js | 30 ++++++--- .../{MachinesList.js => MachinesTableList.js} | 6 +- .../machines/components/ViewModeSelection.js | 60 ++++++++++++++++++ .../network/components/NetworkContainer.js | 11 +--- .../network/state/NetworkStateProvider.js | 27 ++++++++ src/features/network/state/contexts.js | 4 ++ .../network}/state/initialState.js | 0 src/{ => features/network}/state/reducer.js | 0 src/features/network/styles.js | 7 --- src/index.js | 13 ++-- src/providers/ApplicationStateProvider.js | 33 ---------- src/state/contexts.js | 4 -- 20 files changed, 257 insertions(+), 86 deletions(-) create mode 100644 src/features/machines/components/MachinesAccordionList.js rename src/features/machines/components/{MachinesList.js => MachinesTableList.js} (90%) create mode 100644 src/features/machines/components/ViewModeSelection.js create mode 100644 src/features/network/state/NetworkStateProvider.js create mode 100644 src/features/network/state/contexts.js rename src/{ => features/network}/state/initialState.js (100%) rename src/{ => features/network}/state/reducer.js (100%) delete mode 100644 src/features/network/styles.js delete mode 100644 src/providers/ApplicationStateProvider.js delete mode 100644 src/state/contexts.js diff --git a/package-lock.json b/package-lock.json index ec8c4d4..1e18c67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1315,6 +1315,11 @@ "resolved": "https://lab.code-rove.com/public-node-registry/@flare/js-utils/-/js-utils-1.0.3.tgz", "integrity": "sha512-VgXQHoQEVZ/71B6YQHQP8/Yd/w1smGD+kCCiNvJKZ1xMD3nkN9mjoHxIqbOJMZ2q5PZlV6gXYT7eVol8Wm+D0A==" }, + "@flare/react-hooks": { + "version": "1.0.1", + "resolved": "https://lab.code-rove.com/public-node-registry/@flare/react-hooks/-/react-hooks-1.0.1.tgz", + "integrity": "sha512-mU6zkdETonWv0SnxT1qF/lch6ND1yph6bAw+BaYxl/jj8tasOune+KKfyGcLFR/Kso3q8PlVPT8x4CiLiO6woQ==" + }, "@flare/tuitio-client": { "version": "1.1.0", "resolved": "https://lab.code-rove.com/public-node-registry/@flare/tuitio-client/-/tuitio-client-1.1.0.tgz", @@ -1980,6 +1985,18 @@ "@babel/runtime": "^7.4.4" } }, + "@material-ui/lab": { + "version": "4.0.0-alpha.61", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.61.tgz", + "integrity": "sha512-rSzm+XKiNUjKegj8bzt5+pygZeckNLOr+IjykH8sYdVk7dE9y2ZuUSofiMV2bJk3qU+JHwexmw+q0RyNZB9ugg==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@material-ui/styles": { "version": "4.11.5", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", diff --git a/package.json b/package.json index 29f8965..793212c 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ }, "private": true, "dependencies": { + "@flare/react-hooks": "^1.0.1", "@flare/js-utils": "^1.0.3", "@flare/tuitio-client-react": "^1.1.1", "@material-ui/core": "^4.11.2", "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^4.0.0-alpha.61", "axios": "^1.3.4", "i18next": "^19.4.4", "i18next-browser-languagedetector": "^4.1.1", diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index 0deb909..caad19d 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -30,15 +30,16 @@ "Username": "Username", "Password": "Password", "Label": "Login", - "Logout": "Logout", "IncorrectCredentials": "Incorrect credentials." }, "User": { "Profile": { + "Label": "Profile", "Hello": "Hi, {{userName}}", "Description": "{{userName}}, authenticated on {{loginDate}}", "OpenPortfolio": "Open portfolio" - } + }, + "Logout": "Logout" }, "Machine": { "FullName": "Full machine name", diff --git a/public/locales/ro/translations.json b/public/locales/ro/translations.json index 6bd4cbf..a70fc4f 100644 --- a/public/locales/ro/translations.json +++ b/public/locales/ro/translations.json @@ -21,15 +21,16 @@ "Username": "Utilizator", "Password": "Parolă", "Label": "Autentificare", - "Logout": "Deconectare", "IncorrectCredentials": "Credențiale incorecte." }, "User": { "Profile": { + "Label": "Profil", "Hello": "Salut, {{userName}}", "Description": "{{userName}}, autentificat pe {{loginDate}}", "OpenPortfolio": "Deschide portofoliu" - } + }, + "Logout": "Deconectare" }, "Machine": { "FullName": "Nume intreg masina", diff --git a/src/components/common/PageTitle.js b/src/components/common/PageTitle.js index 474242e..c00ebc9 100644 --- a/src/components/common/PageTitle.js +++ b/src/components/common/PageTitle.js @@ -7,26 +7,36 @@ const useStyles = makeStyles(theme => ({ box: { display: "flex", justifyContent: "space-between", - marginBottom: theme.spacing(2), + marginBottom: theme.spacing(1), marginTop: theme.spacing(0) }, - title: { textTransform: "uppercase" } + title: { + display: "flex", + justifyContent: "center", + flexDirection: "column", + minHeight: "40px" + }, + titleText: { textTransform: "uppercase" } })); -const PageTitle = ({ text }) => { +const PageTitle = ({ text, toolBar }) => { const classes = useStyles(); return (
- - {text} - +
+ + {text} + +
+ {toolBar}
); }; PageTitle.propTypes = { - text: PropTypes.string.isRequired + text: PropTypes.string.isRequired, + toolBar: PropTypes.node }; export default PageTitle; diff --git a/src/components/layout/ProfileButton.js b/src/components/layout/ProfileButton.js index 011aa00..15a53d8 100644 --- a/src/components/layout/ProfileButton.js +++ b/src/components/layout/ProfileButton.js @@ -1,13 +1,28 @@ import React, { useState } from "react"; -import { IconButton, Menu, MenuItem } from "@material-ui/core"; +import { + IconButton, + Menu, + MenuItem, + Typography, + ListItemIcon +} from "@material-ui/core"; import AccountCircle from "@material-ui/icons/AccountCircle"; +import ExitToAppIcon from "@material-ui/icons/ExitToApp"; +import AccountBoxIcon from "@material-ui/icons/AccountBox"; import { useHistory } from "react-router-dom"; import { useTuitioClient } from "@flare/tuitio-client-react"; import { useToast } from "../../hooks"; +import styles from "./styles"; +import { makeStyles } from "@material-ui/core/styles"; +import { useTranslation } from "react-i18next"; + +const useStyles = makeStyles(styles); const ProfileButton = () => { const history = useHistory(); const { error } = useToast(); + const classes = useStyles(); + const { t } = useTranslation(); const { logout } = useTuitioClient({ onLogoutFailed: errorMessage => error(errorMessage), @@ -57,9 +72,17 @@ const ProfileButton = () => { handleClose(); }} > - Profile + + + + {t("User.Profile.Label")} + + + + + + {t("User.Logout")} - Logout ); diff --git a/src/components/layout/styles.js b/src/components/layout/styles.js index 0c06cdf..2bff25a 100644 --- a/src/components/layout/styles.js +++ b/src/components/layout/styles.js @@ -62,6 +62,9 @@ const styles = theme => ({ content: { flexGrow: 1, padding: theme.spacing(2) + }, + menuItemIcon: { + minWidth: "26px" } }); diff --git a/src/features/machines/components/MachinesAccordionList.js b/src/features/machines/components/MachinesAccordionList.js new file mode 100644 index 0000000..eb394f2 --- /dev/null +++ b/src/features/machines/components/MachinesAccordionList.js @@ -0,0 +1,63 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { makeStyles } from "@material-ui/core/styles"; +import Accordion from "@material-ui/core/Accordion"; +import AccordionSummary from "@material-ui/core/AccordionSummary"; +import AccordionDetails from "@material-ui/core/AccordionDetails"; +import Checkbox from "@material-ui/core/Checkbox"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Typography from "@material-ui/core/Typography"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import withStyles from "@material-ui/core/styles/withStyles"; + +const IconLeftAccordionSummary = withStyles({ + expandIcon: { + order: -1 + } +})(AccordionSummary); + +const useStyles = makeStyles({ + root: { + width: "100%" + } +}); + +const MachinesAccordionList = ({ machines }) => { + const classes = useStyles(); + + return ( +
+ {machines.map(machine => ( + + } + aria-label="Expand" + aria-controls="additional-actions1-content" + id="additional-actions1-header" + IconButtonProps={{ edge: "start" }} + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the click event propagation" + /> + + + + The click event of the nested action will propagate up and expand + the accordion unless you explicitly stop it. + + + + ))} +
+ ); +}; + +MachinesAccordionList.propTypes = { + machines: PropTypes.array.isRequired +}; + +export default MachinesAccordionList; diff --git a/src/features/machines/components/MachinesContainer.js b/src/features/machines/components/MachinesContainer.js index efcf509..12bbb84 100644 --- a/src/features/machines/components/MachinesContainer.js +++ b/src/features/machines/components/MachinesContainer.js @@ -1,16 +1,20 @@ -import React, { useContext, useEffect, useCallback } from "react"; +import React, { useContext, useEffect, useCallback, useState } from "react"; import { - ApplicationStateContext, - ApplicationDispatchContext -} from "../../../state/contexts"; + NetworkStateContext, + NetworkDispatchContext +} from "../../network/state/contexts"; import useApi from "../../../api"; -import MachinesList from "./MachinesList"; +import MachinesTableList from "./MachinesTableList"; +import MachinesAccordionList from "./MachinesAccordionList"; import PageTitle from "../../../components/common/PageTitle"; import { useTranslation } from "react-i18next"; +import ViewModeSelection, { ViewModes } from "./ViewModeSelection"; const MachinesContainer = () => { - const state = useContext(ApplicationStateContext); - const dispatchActions = useContext(ApplicationDispatchContext); + const [viewMode, setViewMode] = useState(ViewModes.TABLE); + + const state = useContext(NetworkStateContext); + const dispatchActions = useContext(NetworkDispatchContext); const { t } = useTranslation(); const api = useApi(); @@ -32,8 +36,16 @@ const MachinesContainer = () => { return ( <> - - + } + /> + {viewMode === ViewModes.TABLE && ( + + )} + {viewMode === ViewModes.ACCORDION && ( + + )} ); }; diff --git a/src/features/machines/components/MachinesList.js b/src/features/machines/components/MachinesTableList.js similarity index 90% rename from src/features/machines/components/MachinesList.js rename to src/features/machines/components/MachinesTableList.js index 82aab66..2fb8311 100644 --- a/src/features/machines/components/MachinesList.js +++ b/src/features/machines/components/MachinesTableList.js @@ -12,7 +12,7 @@ import Paper from "@material-ui/core/Paper"; import MachineContainer from "./MachineContainer"; import { useTranslation } from "react-i18next"; -const MachinesList = ({ dense, machines }) => { +const MachinesTableList = ({ dense, machines }) => { const { t } = useTranslation(); return ( @@ -40,9 +40,9 @@ const MachinesList = ({ dense, machines }) => { ); }; -MachinesList.propTypes = { +MachinesTableList.propTypes = { dense: PropTypes.bool.isRequired, machines: PropTypes.array.isRequired }; -export default MachinesList; +export default MachinesTableList; diff --git a/src/features/machines/components/ViewModeSelection.js b/src/features/machines/components/ViewModeSelection.js new file mode 100644 index 0000000..89e7d6c --- /dev/null +++ b/src/features/machines/components/ViewModeSelection.js @@ -0,0 +1,60 @@ +import React, { useState, useEffect, useCallback } from "react"; +import PropTypes from "prop-types"; +import TableChartIcon from "@material-ui/icons/TableChart"; +import ViewListIcon from "@material-ui/icons/ViewList"; +import ToggleButton from "@material-ui/lab/ToggleButton"; +import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup"; +import { useWindowSize } from "@flare/react-hooks"; + +export const ViewModes = { + TABLE: "table", + ACCORDION: "accordion" +}; + +const ViewModeSelection = ({ callback }) => { + const [viewMode, setViewMode] = useState(ViewModes.TABLE); + const { isMobile } = useWindowSize(); + + const handleViewModeSelection = useCallback( + (event, mode) => { + setViewMode(mode); + callback && callback(mode); + }, + [callback] + ); + + useEffect( + () => + handleViewModeSelection( + null, + isMobile ? ViewModes.ACCORDION : ViewModes.TABLE + ), + [handleViewModeSelection, isMobile] + ); + + return ( + + + + + + + + + ); +}; + +ViewModeSelection.propTypes = { + initialMode: PropTypes.oneOf([ViewModes.TABLE, ViewModes.ACCORDION]), + callback: PropTypes.func +}; + +export default ViewModeSelection; diff --git a/src/features/network/components/NetworkContainer.js b/src/features/network/components/NetworkContainer.js index 9866e45..38d03ee 100644 --- a/src/features/network/components/NetworkContainer.js +++ b/src/features/network/components/NetworkContainer.js @@ -1,17 +1,12 @@ import React from "react"; import MachinesContainer from "../../machines/components/MachinesContainer"; -import { makeStyles } from "@material-ui/core/styles"; -import styles from "../styles"; - -const useStyles = makeStyles(styles); +import NetworkStateProvider from "../state/NetworkStateProvider"; const NetworkContainer = () => { - const classes = useStyles(); - return ( -
+ -
+ ); }; diff --git a/src/features/network/state/NetworkStateProvider.js b/src/features/network/state/NetworkStateProvider.js new file mode 100644 index 0000000..cd3a58f --- /dev/null +++ b/src/features/network/state/NetworkStateProvider.js @@ -0,0 +1,27 @@ +import React, { useReducer, useMemo } from "react"; +import PropTypes from "prop-types"; +import { NetworkStateContext, NetworkDispatchContext } from "./contexts"; +import { reducer, dispatchActions as reducerDispatchActions } from "./reducer"; +import { initialState } from "./initialState"; + +const NetworkStateProvider = ({ children }) => { + const [state, dispatch] = useReducer(reducer, initialState); + const dispatchActions = useMemo( + () => reducerDispatchActions(dispatch), + [dispatch] + ); + + return ( + + + {children} + + + ); +}; + +NetworkStateProvider.propTypes = { + children: PropTypes.node.isRequired +}; + +export default NetworkStateProvider; diff --git a/src/features/network/state/contexts.js b/src/features/network/state/contexts.js new file mode 100644 index 0000000..39a4687 --- /dev/null +++ b/src/features/network/state/contexts.js @@ -0,0 +1,4 @@ +import React from "react"; + +export const NetworkStateContext = React.createContext(); +export const NetworkDispatchContext = React.createContext(); diff --git a/src/state/initialState.js b/src/features/network/state/initialState.js similarity index 100% rename from src/state/initialState.js rename to src/features/network/state/initialState.js diff --git a/src/state/reducer.js b/src/features/network/state/reducer.js similarity index 100% rename from src/state/reducer.js rename to src/features/network/state/reducer.js diff --git a/src/features/network/styles.js b/src/features/network/styles.js deleted file mode 100644 index ec844e3..0000000 --- a/src/features/network/styles.js +++ /dev/null @@ -1,7 +0,0 @@ -const styles = () => ({ - root: { - margin: "15px" - } -}); - -export default styles; diff --git a/src/index.js b/src/index.js index 2706ab8..1797fb6 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,6 @@ import ThemeProvider from "./providers/ThemeProvider"; import CssBaseline from "@material-ui/core/CssBaseline"; import App from "./components/App"; import { TuitioProvider } from "@flare/tuitio-client-react"; -import ApplicationStateProvider from "./providers/ApplicationStateProvider"; import ToastProvider from "./providers/ToastProvider"; import SensitiveInfoProvider from "./providers/SensitiveInfoProvider"; import "./utils/i18n"; @@ -14,13 +13,11 @@ ReactDOM.render( - - Loading...}> - - - - - + Loading...}> + + + + , diff --git a/src/providers/ApplicationStateProvider.js b/src/providers/ApplicationStateProvider.js deleted file mode 100644 index 071541f..0000000 --- a/src/providers/ApplicationStateProvider.js +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useReducer, useMemo } from "react"; -import PropTypes from "prop-types"; -import { - ApplicationStateContext, - ApplicationDispatchContext -} from "../state/contexts"; -import { - reducer, - dispatchActions as reducerDispatchActions -} from "../state/reducer"; -import { initialState } from "../state/initialState"; - -const ApplicationStateProvider = ({ children }) => { - const [state, dispatch] = useReducer(reducer, initialState); - const dispatchActions = useMemo( - () => reducerDispatchActions(dispatch), - [dispatch] - ); - - return ( - - - {children} - - - ); -}; - -ApplicationStateProvider.propTypes = { - children: PropTypes.node.isRequired -}; - -export default ApplicationStateProvider; diff --git a/src/state/contexts.js b/src/state/contexts.js deleted file mode 100644 index 229f959..0000000 --- a/src/state/contexts.js +++ /dev/null @@ -1,4 +0,0 @@ -import React from "react"; - -export const ApplicationStateContext = React.createContext(); -export const ApplicationDispatchContext = React.createContext();