diff --git a/private/Notes.txt b/private/Notes.txt
index 72db9b2..170bf0d 100644
--- a/private/Notes.txt
+++ b/private/Notes.txt
@@ -12,4 +12,9 @@ Log-ul va fi popup iar continutul lui poate fi ultima componenta de aici https:/
https://medium.com/@tacomanator/environments-with-create-react-app-7b645312c09d
https://create-react-app.dev/docs/adding-custom-environment-variables/
-https://stackoverflow.com/questions/55690143/what-is-the-difference-between-env-local-and-env-development-local
\ No newline at end of file
+https://stackoverflow.com/questions/55690143/what-is-the-difference-between-env-local-and-env-development-local
+
+
+REACT v4:
+https://v4.mui.com/getting-started/installation/
+https://v4.mui.com/components/material-icons/
\ No newline at end of file
diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json
index 096dc71..6e31cfb 100644
--- a/public/locales/en/translations.json
+++ b/public/locales/en/translations.json
@@ -36,7 +36,11 @@
"PoweredOn": "Powered on",
"Actions": {
"Wake": "Wake",
- "Ping": "Ping"
+ "Ping": "Ping",
+ "More": "More",
+ "Shutdown": "Shutdown",
+ "Restart": "Restart",
+ "Advanced": "Advanced"
}
}
}
diff --git a/public/locales/ro/translations.json b/public/locales/ro/translations.json
index 4cd80b4..8b25ec5 100644
--- a/public/locales/ro/translations.json
+++ b/public/locales/ro/translations.json
@@ -27,7 +27,11 @@
"PoweredOn": "Pornit",
"Actions": {
"Wake": "Pornește",
- "Ping": "Ping"
+ "Ping": "Ping",
+ "More": "Mai mult",
+ "Shutdown": "Oprește",
+ "Restart": "Repornește",
+ "Advanced": "Avansat"
}
}
}
diff --git a/src/api/useApi.js b/src/api/useApi.js
index 06ddacd..d430b46 100644
--- a/src/api/useApi.js
+++ b/src/api/useApi.js
@@ -25,7 +25,10 @@ const useApi = () => {
error(message);
};
- const defaultOptions = { onCompleted: () => {}, onError: handleError };
+ const defaultOptions = {
+ onCompleted: () => {},
+ onError: handleError
+ };
const call = async (request, options) => {
const internalOptions = { ...defaultOptions, ...options };
@@ -63,7 +66,39 @@ const useApi = () => {
return promise;
};
- return { readMachines, wakeMachine, pingMachine };
+ const shutdownMachine = (
+ machineId,
+ delay,
+ force,
+ options = defaultOptions
+ ) => {
+ const promise = call(
+ () => post(`${powerActionsRoute}/shutdown`, { machineId, delay, force }),
+ options
+ );
+ return promise;
+ };
+
+ const restartMachine = (
+ machineId,
+ delay,
+ force,
+ options = defaultOptions
+ ) => {
+ const promise = call(
+ () => post(`${powerActionsRoute}/restart`, { machineId, delay, force }),
+ options
+ );
+ return promise;
+ };
+
+ return {
+ readMachines,
+ wakeMachine,
+ pingMachine,
+ shutdownMachine,
+ restartMachine
+ };
};
export default useApi;
diff --git a/src/features/machines/components/Machine.js b/src/features/machines/components/Machine.js
index 6e163e7..946adac 100644
--- a/src/features/machines/components/Machine.js
+++ b/src/features/machines/components/Machine.js
@@ -1,11 +1,12 @@
-import React from "react";
+import React, { useMemo } from "react";
import PropTypes from "prop-types";
import {
TableCell,
TableRow,
IconButton,
Collapse,
- Tooltip
+ Tooltip,
+ Menu
} from "@material-ui/core";
import { KeyboardArrowDown, KeyboardArrowUp } from "@material-ui/icons";
import { makeStyles } from "@material-ui/core/styles";
@@ -20,10 +21,46 @@ const useRowStyles = makeStyles({
}
});
-const Machine = ({ machine, actions, logs, addLog }) => {
+const ActionButton = React.forwardRef((props, _ref) => {
+ const { action, machine } = props;
+ return (
+
+
+
+
+
+
+
+ );
+});
+
+const Machine = ({
+ machine,
+ actions,
+ logs,
+ addLog,
+ secondaryActionsMenuProps
+}) => {
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
+ const topActions = useMemo(
+ () => actions.filter(a => a.top === true),
+ [actions]
+ );
+
+ const secondaryActions = useMemo(
+ () => actions.filter(a => a.top === false),
+ [actions]
+ );
+
return (
@@ -44,22 +81,28 @@ const Machine = ({ machine, actions, logs, addLog }) => {
{machine.macAddress}
- {actions.map(action => (
-
-
-
-
-
-
-
+ {topActions.map(action => (
+
))}
+
@@ -84,7 +127,8 @@ Machine.propTypes = {
}).isRequired,
actions: PropTypes.array.isRequired,
logs: PropTypes.array.isRequired,
- addLog: PropTypes.func.isRequired
+ addLog: PropTypes.func.isRequired,
+ secondaryActionsMenuProps: PropTypes.object.isRequired
};
export default Machine;
diff --git a/src/features/machines/components/MachineContainer.js b/src/features/machines/components/MachineContainer.js
index c9c0cce..9c74b27 100644
--- a/src/features/machines/components/MachineContainer.js
+++ b/src/features/machines/components/MachineContainer.js
@@ -2,12 +2,21 @@ import React, { useState, useCallback } from "react";
import PropTypes from "prop-types";
import Machine from "./Machine";
import { useToast } from "../../../hooks";
-import { LastPage } from "@material-ui/icons";
+import {
+ LastPage,
+ MoreHoriz,
+ RotateLeft,
+ Launch,
+ Stop
+} from "@material-ui/icons";
import { useTranslation } from "react-i18next";
import useApi from "../../../api";
const MachineContainer = ({ machine }) => {
const [logs, setLogs] = useState([]);
+ const [secondaryActionsAnchor, setSecondaryActionsAnchor] =
+ React.useState(null);
+
const { success, error } = useToast();
const { t } = useTranslation();
@@ -20,20 +29,58 @@ const MachineContainer = ({ machine }) => {
[setLogs]
);
+ const manageActionResponse = useCallback(
+ response => {
+ addLog(`Success: ${response.success}. Status: ${response.status}`);
+ if (response.success) {
+ success(response.status);
+ } else {
+ error(response.status);
+ }
+ },
+ [error, success, addLog]
+ );
+
const pingMachine = useCallback(
machine => async () => {
await api.pingMachine(machine.machineId, {
- onCompleted: result => {
- addLog(`Success: ${result.success}. Status: ${result.status}`);
- if (result.success) {
- success(result.status);
- } else {
- error(result.status);
- }
- }
+ onCompleted: manageActionResponse
});
},
- [error, success, addLog, api]
+ [manageActionResponse, api]
+ );
+
+ const handleOpenSecondaryActions = event => {
+ setSecondaryActionsAnchor(event.currentTarget);
+ };
+
+ const handleCloseSecondaryActions = () => {
+ setSecondaryActionsAnchor(null);
+ };
+
+ const secondaryActionsMenuProps = {
+ anchor: secondaryActionsAnchor,
+ onCloseSecondaryActions: handleCloseSecondaryActions
+ };
+
+ const shutdownMachine = useCallback(
+ machine => async () => {
+ await api.shutdownMachine(machine.machineId, 0, false, {
+ onCompleted: manageActionResponse
+ });
+ handleCloseSecondaryActions();
+ },
+ [manageActionResponse, api]
+ );
+
+ const restartMachine = useCallback(
+ machine => async () => {
+ await api.restartMachine(machine.machineId, 0, false, {
+ onCompleted: manageActionResponse
+ });
+ handleCloseSecondaryActions();
+ },
+ [manageActionResponse, api]
);
const actions = [
@@ -41,12 +88,48 @@ const MachineContainer = ({ machine }) => {
code: "ping",
effect: pingMachine,
icon: LastPage,
- tooltip: t("Machine.Actions.Ping")
+ tooltip: t("Machine.Actions.Ping"),
+ top: true
+ },
+ {
+ code: "more",
+ effect: handleOpenSecondaryActions,
+ icon: MoreHoriz,
+ tooltip: t("Machine.Actions.More"),
+ top: true,
+ system: true
+ },
+ {
+ code: "shutdown",
+ effect: shutdownMachine,
+ icon: Stop,
+ tooltip: t("Machine.Actions.Shutdown"),
+ top: false
+ },
+ {
+ code: "restart",
+ effect: restartMachine,
+ icon: RotateLeft,
+ tooltip: t("Machine.Actions.Restart"),
+ top: false
+ },
+ {
+ code: "advanced",
+ effect: () => {},
+ icon: Launch,
+ tooltip: t("Machine.Actions.Advanced"),
+ top: false
}
];
return (
-
+
);
};