diff --git a/commit_and_push.sh b/commit_and_push.sh new file mode 100755 index 0000000..554786f --- /dev/null +++ b/commit_and_push.sh @@ -0,0 +1,7 @@ +git add * + +read -p "Commit message: " commit_message + +git commit -m "$commit_message" + +git push -u origin main \ No newline at end of file diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index b17472e..236dff8 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -178,13 +178,31 @@ "firmwareVersion": "Firmware Version", "createdAt": "Erstellt am", "actions": "Maßnahmen" + }, + "status": { + "idle": "Inaktiv", + "processing": "Verarbeitung", + "connecting": "Verbinden", + "error": "Fehler", + "offline": "Offline" + }, + "popconfirmEdit": { + "title": "Sind Sie sicher, dass Sie diesen Roboter bearbeiten wollen?", + "errorNotification": { + "message": "Roboter konnte nicht bearbeitet werden", + "description": "Name bereits vergeben" + } + }, + "popconfirmDisconnect": { + "title": "Sind Sie sicher, dass Sie diesen Roboter trennen wollen?", + "description": "Der Roboter wird getrennt und kann nicht mehr für Aufträge verwendet werden" } }, "unauthorizedRobots": { "header": "Nicht autorisierte Roboter", "popconfirmDeny": { "title": "Sind Sie sicher, dass Sie diesen Roboter ablehnen wollen?", - "description": "Der Roboter wird getrennt und muss sich ernuet verbinden" + "description": "Der Roboter wird getrennt und muss sich erneut verbinden" }, "popconfirmAuthorize": { "title": "Sind Sie sicher, dass Sie diesen Roboter autorisieren wollen?", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 79aee14..850740a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -178,6 +178,24 @@ "firmwareVersion": "Firmware version", "createdAt": "Created At", "actions": "Actions" + }, + "status": { + "idle": "Idle", + "processing": "Processing", + "connecting": "Connecting", + "error": "Error", + "offline": "Offline" + }, + "popconfirmEdit": { + "title": "Are you sure you want to edit this robot?", + "errorNotification": { + "message": "Robot could not be edited", + "description": "Name already taken" + } + }, + "popconfirmDisconnect": { + "title": "Are you sure you want to disconnect this robot?", + "description": "The robot will be disconnected and cannot be longer used for jobs" } }, "unauthorizedRobots": { diff --git a/src/Pages/Robotics/Robots/index.js b/src/Pages/Robotics/Robots/index.js index 27a05b4..6c32ea1 100644 --- a/src/Pages/Robotics/Robots/index.js +++ b/src/Pages/Robotics/Robots/index.js @@ -1,4 +1,12 @@ -import { Badge, Popconfirm, Space, Table, Typography } from "antd"; +import { + Badge, + Input, + Popconfirm, + Space, + Table, + Typography, + notification, +} from "antd"; import { useTranslation } from "react-i18next"; import { useRoboticsRobotContext } from "../../../Contexts/RoboticsRobot"; import { useEffect, useRef, useState } from "react"; @@ -16,6 +24,8 @@ const ReceivedSSECommands = { AddUnauthorizedRobot: 2, AddRobot: 3, RemoveUnauthorizedRobot: 4, + RemoveRobot: 5, + RobotUpdated: 6, }; function getRobotTypeString(type) { @@ -32,27 +42,46 @@ function getRobotTypeString(type) { export default function Robots() { const robotsContext = useRoboticsRobotContext(); const { t } = useTranslation(); + const [notificationApi, notificationContextHolder] = + notification.useNotification(); const [robotsPaginationPage, setRobotsPaginationPage] = useState(1); const [ unauthorizedRobotsPaginationPage, setUnauthorizedRobotsPaginationPage, ] = useState(1); + const [selectedRobotName, setSelectedRobotName] = useState(""); const sseEventSource = useRef(null); const getRobotStatusBadge = (status) => { switch (status) { case 1: - return ; + return ( + + ); case 2: - return ; + return ( + + ); case 3: - return ; + return ( + + ); case 4: - return ; + return ( + + ); case 5: - return ; + return ( + + ); default: return "Unknown"; } @@ -116,8 +145,78 @@ export default function Robots() { key: "actions", render: (_, record) => ( - {t("common.text.edit")} - {t("common.text.disconnect")} + setSelectedRobotName(e.target.value)} + minLength={Constants.GLOBALS.MIN_ROBOTICS_ROBOT_NAME_LENGTH} + maxLength={Constants.GLOBALS.MAX_ROBOTICS_ROBOT_NAME_LENGTH} + /> + } + okButtonProps={{ + disabled: + selectedRobotName.length < + Constants.GLOBALS.MIN_ROBOTICS_ROBOT_NAME_LENGTH || + selectedRobotName.length > + Constants.GLOBALS.MAX_ROBOTICS_ROBOT_NAME_LENGTH, + }} + okText={t("common.button.confirm")} + cancelText={t("common.button.cancel")} + onConfirm={() => + myFetch( + `/robot`, + "PATCH", + { + robotId: record.id, + name: selectedRobotName, + }, + {}, + myFetchContentType.JSON, + Constants.ROBOTICS_API_ADDRESS + ).catch((err) => { + if (err === 422) { + notificationApi["error"]({ + message: t( + "robotics.robots.popconfirmEdit.errorNotification.message" + ), + description: t( + "robotics.robots.popconfirmEdit.errorNotification.description" + ), + }); + } + }) + } + > + setSelectedRobotName(record.name)}> + {t("common.text.edit")} + + + + + myFetch( + `/robot/${record.id}`, + "DELETE", + null, + {}, + myFetchContentType.JSON, + Constants.ROBOTICS_API_ADDRESS + ) + } + > + {t("common.text.disconnect")} + ), }, @@ -129,21 +228,27 @@ export default function Robots() { const getRobotsTableItems = (robots) => { let items = []; - robots.forEach((robot) => { + robots.sort((a, b) => a.Status - b.Status); + + robots.forEach((robot) => items.push({ key: robot.Id, id: robot.Id, type: getRobotTypeString(robot.Type), name: robot.Name, status: getRobotStatusBadge(robot.Status), - currentJob: robot.CurrentJob, + currentJob: + robot.CurrentJobId === "" + ? Constants.TEXT_EMPTY_PLACEHOLDER + : robot.CurrentJobId, jobsWaiting: robot.JobsWaitingCount, address: robot.Address, firmwareVersion: robot.FirmwareVersion, connectedAt: FormatDatetime(robot.ConnectedAt), + createdAt: FormatDatetime(robot.CreatedAt), actions: robot.Actions, - }); - }); + }) + ); return items; }; @@ -170,6 +275,11 @@ export default function Robots() { dataIndex: "connectedAt", key: "connectedAt", }, + { + title: t("robotics.robots.column.firmwareVersion"), + dataIndex: "firmwareVersion", + key: "firmwareVersion", + }, { title: t("robotics.robots.column.actions"), dataIndex: "actions", @@ -192,9 +302,7 @@ export default function Robots() { {}, myFetchContentType.JSON, Constants.ROBOTICS_API_ADDRESS - ).then((data) => { - console.log("data", data); - }) + ) } > {t("common.text.deny")} @@ -216,9 +324,7 @@ export default function Robots() { {}, myFetchContentType.JSON, Constants.ROBOTICS_API_ADDRESS - ).then((data) => { - console.log("data", data); - }) + ) } > {t("common.text.authorize")} @@ -241,6 +347,7 @@ export default function Robots() { type: getRobotTypeString(robot.Type), address: robot.Address, connectedAt: FormatDatetime(robot.ConnectedAt), + firmwareVersion: robot.FirmwareVersion, actions: robot.Actions, }); }); @@ -293,12 +400,8 @@ export default function Robots() { robotsContext.setRobots((arr) => { const newArr = [...arr]; - console.log("arr", arr); - const index = arr.findIndex((x) => x.Id === body.RobotId); - console.log("index", index); - if (index !== -1) { newArr[index].Status = body.Status; } @@ -335,6 +438,19 @@ export default function Robots() { return newArr; }); + + // remove from unauthorized robots + robotsContext.setUnauthorizedRobots((arr) => { + const newArr = [...arr]; + + const index = arr.findIndex((x) => x.Id === body.Id); + + if (index !== -1) { + newArr.splice(index, 1); + } + + return newArr; + }); break; case ReceivedSSECommands.RemoveUnauthorizedRobot: robotsContext.setUnauthorizedRobots((arr) => { @@ -349,6 +465,32 @@ export default function Robots() { return newArr; }); break; + case ReceivedSSECommands.RemoveRobot: + robotsContext.setRobots((arr) => { + const newArr = [...arr]; + + const index = arr.findIndex((x) => x.Id === body); + + if (index !== -1) { + newArr.splice(index, 1); + } + + return newArr; + }); + break; + case ReceivedSSECommands.RobotUpdated: + robotsContext.setRobots((arr) => { + const newArr = [...arr]; + + const index = arr.findIndex((x) => x.Id === body.RobotId); + + if (index !== -1) { + newArr[index].Name = body.Name; + } + + return newArr; + }); + break; default: break; } @@ -365,6 +507,8 @@ export default function Robots() { return ( <> + {notificationContextHolder} + {t("robotics.robots.header")} ({robotsContext.robots.length}) diff --git a/src/utils.js b/src/utils.js index 6a05b24..3ba6c79 100644 --- a/src/utils.js +++ b/src/utils.js @@ -102,6 +102,8 @@ export const Constants = { MAX_LOG_MANAGER_DISPLAY_NAME_LENGTH: 16, MIN_LOG_MANAGER_ADDRESS_LENGTH: 3, MAX_LOG_MANAGER_ADDRESS_LENGTH: 100, + MIN_ROBOTICS_ROBOT_NAME_LENGTH: 2, + MAX_ROBOTICS_ROBOT_NAME_LENGTH: 30, }, MAX_AVATAR_SIZE: 5 * 1024 * 1024, ACCEPTED_AVATAR_FILE_TYPES: [ @@ -1390,7 +1392,7 @@ export function myFetch( window.location.href = "/"; } - return; + throw response.status; } // check if response is json