machines view mode option
parent
234778cc43
commit
3cc5d8f6f3
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 (
|
||||
<div className={classes.box}>
|
||||
<Typography className={classes.title} variant="h3" size="sm">
|
||||
<div className={classes.title}>
|
||||
<Typography className={classes.titleText} variant="h3" size="sm">
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
{toolBar}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PageTitle.propTypes = {
|
||||
text: PropTypes.string.isRequired
|
||||
text: PropTypes.string.isRequired,
|
||||
toolBar: PropTypes.node
|
||||
};
|
||||
|
||||
export default PageTitle;
|
||||
|
|
|
@ -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
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
<AccountBoxIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="inherit">{t("User.Profile.Label")}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={logout}>
|
||||
<ListItemIcon className={classes.menuItemIcon}>
|
||||
<ExitToAppIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography variant="inherit">{t("User.Logout")}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={logout}>Logout</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -62,6 +62,9 @@ const styles = theme => ({
|
|||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(2)
|
||||
},
|
||||
menuItemIcon: {
|
||||
minWidth: "26px"
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<div className={classes.root}>
|
||||
{machines.map(machine => (
|
||||
<Accordion key={`machine-${machine.machineId}`}>
|
||||
<IconLeftAccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-label="Expand"
|
||||
aria-controls="additional-actions1-content"
|
||||
id="additional-actions1-header"
|
||||
IconButtonProps={{ edge: "start" }}
|
||||
>
|
||||
<FormControlLabel
|
||||
aria-label="Acknowledge"
|
||||
onClick={event => event.stopPropagation()}
|
||||
onFocus={event => event.stopPropagation()}
|
||||
control={<Checkbox />}
|
||||
label="I acknowledge that I should stop the click event propagation"
|
||||
/>
|
||||
</IconLeftAccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography color="textSecondary">
|
||||
The click event of the nested action will propagate up and expand
|
||||
the accordion unless you explicitly stop it.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MachinesAccordionList.propTypes = {
|
||||
machines: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
export default MachinesAccordionList;
|
|
@ -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 (
|
||||
<>
|
||||
<PageTitle text={t("Menu.Machines")} />
|
||||
<MachinesList dense={true} machines={state.network.machines} />
|
||||
<PageTitle
|
||||
text={t("Menu.Machines")}
|
||||
toolBar={<ViewModeSelection callback={setViewMode} />}
|
||||
/>
|
||||
{viewMode === ViewModes.TABLE && (
|
||||
<MachinesTableList dense={true} machines={state.network.machines} />
|
||||
)}
|
||||
{viewMode === ViewModes.ACCORDION && (
|
||||
<MachinesAccordionList machines={state.network.machines} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 (
|
||||
<TableContainer component={Paper}>
|
||||
|
@ -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;
|
|
@ -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 (
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={viewMode}
|
||||
exclusive
|
||||
onChange={handleViewModeSelection}
|
||||
>
|
||||
<ToggleButton value={ViewModes.TABLE} aria-label="table view mode">
|
||||
<TableChartIcon />
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
value={ViewModes.ACCORDION}
|
||||
aria-label="accordion view mode"
|
||||
>
|
||||
<ViewListIcon />
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
ViewModeSelection.propTypes = {
|
||||
initialMode: PropTypes.oneOf([ViewModes.TABLE, ViewModes.ACCORDION]),
|
||||
callback: PropTypes.func
|
||||
};
|
||||
|
||||
export default ViewModeSelection;
|
|
@ -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 (
|
||||
<div className={classes.root}>
|
||||
<NetworkStateProvider>
|
||||
<MachinesContainer />
|
||||
</div>
|
||||
</NetworkStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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 (
|
||||
<NetworkStateContext.Provider value={state}>
|
||||
<NetworkDispatchContext.Provider value={dispatchActions}>
|
||||
{children}
|
||||
</NetworkDispatchContext.Provider>
|
||||
</NetworkStateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
NetworkStateProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
export default NetworkStateProvider;
|
|
@ -0,0 +1,4 @@
|
|||
import React from "react";
|
||||
|
||||
export const NetworkStateContext = React.createContext();
|
||||
export const NetworkDispatchContext = React.createContext();
|
|
@ -1,7 +0,0 @@
|
|||
const styles = () => ({
|
||||
root: {
|
||||
margin: "15px"
|
||||
}
|
||||
});
|
||||
|
||||
export default styles;
|
|
@ -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(
|
|||
<ThemeProvider>
|
||||
<CssBaseline />
|
||||
<SensitiveInfoProvider>
|
||||
<ApplicationStateProvider>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<ToastProvider>
|
||||
<App />
|
||||
</ToastProvider>
|
||||
</Suspense>
|
||||
</ApplicationStateProvider>
|
||||
</SensitiveInfoProvider>
|
||||
</ThemeProvider>
|
||||
</TuitioProvider>,
|
||||
|
|
|
@ -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 (
|
||||
<ApplicationStateContext.Provider value={state}>
|
||||
<ApplicationDispatchContext.Provider value={dispatchActions}>
|
||||
{children}
|
||||
</ApplicationDispatchContext.Provider>
|
||||
</ApplicationStateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ApplicationStateProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
export default ApplicationStateProvider;
|
|
@ -1,4 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
export const ApplicationStateContext = React.createContext();
|
||||
export const ApplicationDispatchContext = React.createContext();
|
Loading…
Reference in New Issue