machine view modes update

master
Tudor Stanciu 2023-03-24 00:43:28 +02:00
parent 6ad7abf3d1
commit 64684674ba
11 changed files with 250 additions and 126 deletions

View File

@ -26,6 +26,10 @@
"Settings": "Settings",
"About": "About"
},
"ViewModes": {
"Table": "Table",
"List": "List"
},
"Login": {
"Username": "Username",
"Password": "Password",

View File

@ -17,6 +17,10 @@
"Settings": "Setări",
"About": "Despre"
},
"ViewModes": {
"Table": "Tabel",
"List": "Lista"
},
"Login": {
"Username": "Utilizator",
"Password": "Parolă",

View File

@ -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;

View File

@ -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;

View File

@ -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>
);

View File

@ -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>

View File

@ -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;

View File

@ -13,7 +13,7 @@ const MachineLog = ({ logs }) => {
);
return (
<Box margin={1}>
<Box width="100%">
<div style={{ height: 200 }}>
<ScrollFollow
startFollowing={true}

View File

@ -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>

View File

@ -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>
);
};

View File

@ -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",