machine view modes update
parent
6ad7abf3d1
commit
64684674ba
|
@ -26,6 +26,10 @@
|
|||
"Settings": "Settings",
|
||||
"About": "About"
|
||||
},
|
||||
"ViewModes": {
|
||||
"Table": "Table",
|
||||
"List": "List"
|
||||
},
|
||||
"Login": {
|
||||
"Username": "Username",
|
||||
"Password": "Password",
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
"Settings": "Setări",
|
||||
"About": "Despre"
|
||||
},
|
||||
"ViewModes": {
|
||||
"Table": "Tabel",
|
||||
"List": "Lista"
|
||||
},
|
||||
"Login": {
|
||||
"Username": "Utilizator",
|
||||
"Password": "Parolă",
|
||||
|
|
|
@ -10,6 +10,17 @@ import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
|||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import MachineLog from "./common/MachineLog";
|
||||
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({
|
||||
expandIcon: {
|
||||
|
@ -17,7 +28,29 @@ const IconLeftAccordionSummary = withStyles({
|
|||
}
|
||||
})(AccordionSummary);
|
||||
|
||||
const MachineAccordion = ({ machine, logs }) => {
|
||||
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,
|
||||
secondaryActionsMenuProps
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Accordion>
|
||||
<IconLeftAccordionSummary
|
||||
|
@ -27,11 +60,25 @@ const MachineAccordion = ({ machine, logs }) => {
|
|||
id="additional-actions1-header"
|
||||
IconButtonProps={{ edge: "start" }}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={6}>
|
||||
<DataLabel
|
||||
label={"Full machine name"}
|
||||
data={machine.fullMachineName}
|
||||
<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}
|
||||
secondaryActionsMenuProps={secondaryActionsMenuProps}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -44,8 +91,18 @@ const MachineAccordion = ({ machine, logs }) => {
|
|||
};
|
||||
|
||||
MachineAccordion.propTypes = {
|
||||
machine: PropTypes.object.isRequired,
|
||||
logs: PropTypes.array.isRequired
|
||||
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,
|
||||
secondaryActionsMenuProps: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default MachineAccordion;
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
TableCell,
|
||||
TableRow,
|
||||
IconButton,
|
||||
Collapse,
|
||||
Menu
|
||||
} from "@material-ui/core";
|
||||
import { TableCell, TableRow, IconButton, Collapse } from "@material-ui/core";
|
||||
import { KeyboardArrowDown, KeyboardArrowUp } from "@material-ui/icons";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import MachineLog from "./common/MachineLog";
|
||||
import WakeComponent from "./common/WakeComponent";
|
||||
import ActionButton from "./common/ActionButton";
|
||||
import { useSensitiveInfo } from "../../../hooks";
|
||||
import ActionsGroup from "./common/ActionsGroup";
|
||||
|
||||
const useRowStyles = makeStyles({
|
||||
root: {
|
||||
|
@ -22,7 +15,7 @@ const useRowStyles = makeStyles({
|
|||
}
|
||||
});
|
||||
|
||||
const Machine = ({
|
||||
const MachineTableRow = ({
|
||||
machine,
|
||||
actions,
|
||||
logs,
|
||||
|
@ -33,16 +26,6 @@ const Machine = ({
|
|||
const classes = useRowStyles();
|
||||
const { mask } = useSensitiveInfo();
|
||||
|
||||
const topActions = useMemo(
|
||||
() => actions.filter(a => a.top === true),
|
||||
[actions]
|
||||
);
|
||||
|
||||
const secondaryActions = useMemo(
|
||||
() => actions.filter(a => a.top === false),
|
||||
[actions]
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow className={classes.root}>
|
||||
|
@ -62,29 +45,12 @@ const Machine = ({
|
|||
<TableCell>{mask(machine.iPv4Address)}</TableCell>
|
||||
<TableCell>{mask(machine.macAddress)}</TableCell>
|
||||
<TableCell align="right">
|
||||
<WakeComponent machine={machine} addLog={addLog} />
|
||||
{topActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
/>
|
||||
))}
|
||||
<Menu
|
||||
id="secondary-actions-menu"
|
||||
anchorEl={secondaryActionsMenuProps.anchor}
|
||||
keepMounted
|
||||
open={Boolean(secondaryActionsMenuProps.anchor)}
|
||||
onClose={secondaryActionsMenuProps.onCloseSecondaryActions}
|
||||
>
|
||||
{secondaryActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
<ActionsGroup
|
||||
machine={machine}
|
||||
actions={actions}
|
||||
addLog={addLog}
|
||||
secondaryActionsMenuProps={secondaryActionsMenuProps}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
@ -98,7 +64,7 @@ const Machine = ({
|
|||
);
|
||||
};
|
||||
|
||||
Machine.propTypes = {
|
||||
MachineTableRow.propTypes = {
|
||||
machine: PropTypes.shape({
|
||||
machineId: PropTypes.number.isRequired,
|
||||
machineName: PropTypes.string.isRequired,
|
||||
|
@ -113,4 +79,4 @@ Machine.propTypes = {
|
|||
secondaryActionsMenuProps: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default Machine;
|
||||
export default MachineTableRow;
|
||||
|
|
|
@ -4,7 +4,9 @@ import TableChartIcon from "@material-ui/icons/TableChart";
|
|||
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||
import ToggleButton from "@material-ui/lab/ToggleButton";
|
||||
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
import { useWindowSize } from "@flare/react-hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const ViewModes = {
|
||||
TABLE: "table",
|
||||
|
@ -12,41 +14,47 @@ export const ViewModes = {
|
|||
};
|
||||
|
||||
const ViewModeSelection = ({ callback }) => {
|
||||
const [viewMode, setViewMode] = useState(ViewModes.TABLE);
|
||||
const [state, setState] = useState({
|
||||
mode: ViewModes.TABLE,
|
||||
manual: false
|
||||
});
|
||||
const { isMobile } = useWindowSize();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleViewModeSelection = useCallback(
|
||||
(event, mode) => {
|
||||
setViewMode(mode);
|
||||
setState({ mode, manual: true });
|
||||
callback && callback(mode);
|
||||
},
|
||||
[callback]
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
handleViewModeSelection(
|
||||
null,
|
||||
isMobile ? ViewModes.ACCORDION : ViewModes.TABLE
|
||||
),
|
||||
[handleViewModeSelection, isMobile]
|
||||
);
|
||||
useEffect(() => {
|
||||
if (state.manual === true) return;
|
||||
const mode = isMobile ? ViewModes.ACCORDION : ViewModes.TABLE;
|
||||
setState({ mode, manual: false });
|
||||
callback && callback(mode);
|
||||
}, [callback, isMobile, state.manual]);
|
||||
|
||||
return (
|
||||
<ToggleButtonGroup
|
||||
size="small"
|
||||
value={viewMode}
|
||||
value={state.mode}
|
||||
exclusive
|
||||
onChange={handleViewModeSelection}
|
||||
>
|
||||
<ToggleButton value={ViewModes.TABLE} aria-label="table view mode">
|
||||
<TableChartIcon />
|
||||
<Tooltip title={t("ViewModes.Table")}>
|
||||
<TableChartIcon />
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
value={ViewModes.ACCORDION}
|
||||
aria-label="accordion view mode"
|
||||
>
|
||||
<ViewListIcon />
|
||||
<Tooltip title={t("ViewModes.List")}>
|
||||
<ViewListIcon />
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
);
|
||||
|
|
|
@ -4,6 +4,13 @@ import { IconButton, Tooltip } from "@material-ui/core";
|
|||
|
||||
const ActionButton = React.forwardRef((props, _ref) => {
|
||||
const { action, machine } = props;
|
||||
const id = `machine-item-${machine.machineId}-${action.code}`;
|
||||
const handleActionClick = event => {
|
||||
if (action.system) action.effect();
|
||||
else action.effect(machine);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
id={`machine-item-${machine.machineId}-${action.code}-tooltip`}
|
||||
|
@ -11,9 +18,10 @@ const ActionButton = React.forwardRef((props, _ref) => {
|
|||
>
|
||||
<span>
|
||||
<IconButton
|
||||
id={`machine-item-${machine.machineId}-${action.code}`}
|
||||
id={id}
|
||||
size={"small"}
|
||||
onClick={action.system ? action.effect : action.effect(machine)}
|
||||
onFocus={event => event.stopPropagation()}
|
||||
onClick={handleActionClick}
|
||||
>
|
||||
<action.icon />
|
||||
</IconButton>
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import React, { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import WakeComponent from "./WakeComponent";
|
||||
import ActionButton from "./ActionButton";
|
||||
import { Menu } from "@material-ui/core";
|
||||
|
||||
const ActionsGroup = ({
|
||||
machine,
|
||||
actions,
|
||||
addLog,
|
||||
secondaryActionsMenuProps
|
||||
}) => {
|
||||
const topActions = useMemo(
|
||||
() => actions.filter(a => a.top === true),
|
||||
[actions]
|
||||
);
|
||||
|
||||
const secondaryActions = useMemo(
|
||||
() => actions.filter(a => a.top === false),
|
||||
[actions]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WakeComponent machine={machine} addLog={addLog} />
|
||||
{topActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
/>
|
||||
))}
|
||||
<Menu
|
||||
id="secondary-actions-menu"
|
||||
anchorEl={secondaryActionsMenuProps.anchor}
|
||||
keepMounted
|
||||
open={Boolean(secondaryActionsMenuProps.anchor)}
|
||||
onClose={secondaryActionsMenuProps.onCloseSecondaryActions}
|
||||
>
|
||||
{secondaryActions.map(action => (
|
||||
<ActionButton
|
||||
key={`machine-item-${machine.machineId}-${action.code}`}
|
||||
action={action}
|
||||
machine={machine}
|
||||
/>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ActionsGroup.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,
|
||||
addLog: PropTypes.func.isRequired,
|
||||
secondaryActionsMenuProps: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default ActionsGroup;
|
|
@ -13,7 +13,7 @@ const MachineLog = ({ logs }) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<Box margin={1}>
|
||||
<Box width="100%">
|
||||
<div style={{ height: 200 }}>
|
||||
<ScrollFollow
|
||||
startFollowing={true}
|
||||
|
|
|
@ -80,6 +80,11 @@ const WakeComponent = ({ machine, addLog }) => {
|
|||
|
||||
useEffect(pingInLoop, [trigger, pingInLoop]);
|
||||
|
||||
const handleWakeClick = event => {
|
||||
wakeMachine();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={t(state.on ? "Machine.PoweredOn" : "Machine.Actions.Wake")}>
|
||||
<span>
|
||||
|
@ -87,8 +92,9 @@ const WakeComponent = ({ machine, addLog }) => {
|
|||
id={`machine-${machine.machineId}-wake`}
|
||||
size={"small"}
|
||||
disabled={state.on}
|
||||
onClick={wakeMachine}
|
||||
onClick={handleWakeClick}
|
||||
style={state.on ? { color: "#33cc33" } : {}}
|
||||
onFocus={event => event.stopPropagation()}
|
||||
>
|
||||
<PowerSettingsNew />
|
||||
</IconButton>
|
||||
|
|
|
@ -16,11 +16,16 @@ import { FileCopyOutlined } from "@material-ui/icons";
|
|||
import EmailIcon from "@material-ui/icons/Email";
|
||||
import { useToast } from "../../../../hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import styles from "../styles";
|
||||
|
||||
const useStyles = makeStyles(styles);
|
||||
|
||||
const UserProfileCardContent = ({ userData }) => {
|
||||
const { email, profilePictureUrl } = userData;
|
||||
const { t } = useTranslation();
|
||||
const { info } = useToast();
|
||||
const classes = useStyles();
|
||||
|
||||
const handleCopyToClipboard = url => () => {
|
||||
navigator.clipboard.writeText(url);
|
||||
|
@ -35,73 +40,66 @@ const UserProfileCardContent = ({ userData }) => {
|
|||
const userName = `${userData.firstName} ${userData.lastName}`;
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={4} lg={2}>
|
||||
<UserProfilePicture userData={userData} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={8} lg={10}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List>
|
||||
<div className={classes.panel}>
|
||||
<UserProfilePicture userData={userData} />
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List>
|
||||
<ListItem dense>
|
||||
<ListItemIcon>
|
||||
<BusinessCenterIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip title={t("User.Profile.OpenPortfolio")}>
|
||||
<Link href="https://lab.code-rove.com/tsp/" target="_blank">
|
||||
{userName}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem dense>
|
||||
<ListItemIcon>
|
||||
<EmailIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip title={t("Generic.SendEmail")}>
|
||||
<Link href="#" onClick={handleEmailSending}>
|
||||
{email}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
{profilePictureUrl && (
|
||||
<ListItem dense>
|
||||
<ListItemIcon>
|
||||
<BusinessCenterIcon />
|
||||
<Tooltip title={t("Generic.Copy")}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleCopyToClipboard(profilePictureUrl)}
|
||||
>
|
||||
<FileCopyOutlined />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip title={t("User.Profile.OpenPortfolio")}>
|
||||
<Link
|
||||
href="https://lab.code-rove.com/tsp/"
|
||||
target="_blank"
|
||||
>
|
||||
{userName}
|
||||
<Tooltip title={t("Generic.OpenInNewTab")}>
|
||||
<Link href={profilePictureUrl} target="_blank">
|
||||
{profilePictureUrl}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem dense>
|
||||
<ListItemIcon>
|
||||
<EmailIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip title={t("Generic.SendEmail")}>
|
||||
<Link href="#" onClick={handleEmailSending}>
|
||||
{email}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
{profilePictureUrl && (
|
||||
<ListItem dense>
|
||||
<ListItemIcon>
|
||||
<Tooltip title={t("Generic.Copy")}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleCopyToClipboard(profilePictureUrl)}
|
||||
>
|
||||
<FileCopyOutlined />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Tooltip title={t("Generic.OpenInNewTab")}>
|
||||
<Link href={profilePictureUrl} target="_blank">
|
||||
{profilePictureUrl}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
const style = theme => {
|
||||
return {
|
||||
panel: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
"@media (max-width: 600px)": {
|
||||
flexDirection: "column" // change direction for small screens
|
||||
}
|
||||
},
|
||||
profilePicture: {
|
||||
margin: "auto",
|
||||
display: "block",
|
||||
|
|
Loading…
Reference in New Issue