robots table

main
alex 2023-10-14 20:41:59 +02:00
parent 4acbe50f8b
commit 28c823bc41
5 changed files with 213 additions and 24 deletions

7
commit_and_push.sh Executable file
View File

@ -0,0 +1,7 @@
git add *
read -p "Commit message: " commit_message
git commit -m "$commit_message"
git push -u origin main

View File

@ -178,13 +178,31 @@
"firmwareVersion": "Firmware Version", "firmwareVersion": "Firmware Version",
"createdAt": "Erstellt am", "createdAt": "Erstellt am",
"actions": "Maßnahmen" "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": { "unauthorizedRobots": {
"header": "Nicht autorisierte Roboter", "header": "Nicht autorisierte Roboter",
"popconfirmDeny": { "popconfirmDeny": {
"title": "Sind Sie sicher, dass Sie diesen Roboter ablehnen wollen?", "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": { "popconfirmAuthorize": {
"title": "Sind Sie sicher, dass Sie diesen Roboter autorisieren wollen?", "title": "Sind Sie sicher, dass Sie diesen Roboter autorisieren wollen?",

View File

@ -178,6 +178,24 @@
"firmwareVersion": "Firmware version", "firmwareVersion": "Firmware version",
"createdAt": "Created At", "createdAt": "Created At",
"actions": "Actions" "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": { "unauthorizedRobots": {

View File

@ -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 { useTranslation } from "react-i18next";
import { useRoboticsRobotContext } from "../../../Contexts/RoboticsRobot"; import { useRoboticsRobotContext } from "../../../Contexts/RoboticsRobot";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -16,6 +24,8 @@ const ReceivedSSECommands = {
AddUnauthorizedRobot: 2, AddUnauthorizedRobot: 2,
AddRobot: 3, AddRobot: 3,
RemoveUnauthorizedRobot: 4, RemoveUnauthorizedRobot: 4,
RemoveRobot: 5,
RobotUpdated: 6,
}; };
function getRobotTypeString(type) { function getRobotTypeString(type) {
@ -32,27 +42,46 @@ function getRobotTypeString(type) {
export default function Robots() { export default function Robots() {
const robotsContext = useRoboticsRobotContext(); const robotsContext = useRoboticsRobotContext();
const { t } = useTranslation(); const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [robotsPaginationPage, setRobotsPaginationPage] = useState(1); const [robotsPaginationPage, setRobotsPaginationPage] = useState(1);
const [ const [
unauthorizedRobotsPaginationPage, unauthorizedRobotsPaginationPage,
setUnauthorizedRobotsPaginationPage, setUnauthorizedRobotsPaginationPage,
] = useState(1); ] = useState(1);
const [selectedRobotName, setSelectedRobotName] = useState("");
const sseEventSource = useRef(null); const sseEventSource = useRef(null);
const getRobotStatusBadge = (status) => { const getRobotStatusBadge = (status) => {
switch (status) { switch (status) {
case 1: case 1:
return <Badge status="success" text={"Idle"} />; return (
<Badge status="success" text={t("robotics.robots.status.idle")} />
);
case 2: case 2:
return <Badge status="processing" text={"Running"} />; return (
<Badge
status="processing"
text={t("robotics.robots.status.processing")}
/>
);
case 3: case 3:
return <Badge status="warning" text={"Connecting"} />; return (
<Badge
status="warning"
text={t("robotics.robots.status.connecting")}
/>
);
case 4: case 4:
return <Badge status="warning" text={"Error"} />; return (
<Badge status="warning" text={t("robotics.robots.status.error")} />
);
case 5: case 5:
return <Badge status="default" text={"Offline"} />; return (
<Badge status="default" text={t("robotics.robots.status.offline")} />
);
default: default:
return "Unknown"; return "Unknown";
} }
@ -116,8 +145,78 @@ export default function Robots() {
key: "actions", key: "actions",
render: (_, record) => ( render: (_, record) => (
<Space size="middle"> <Space size="middle">
<Link to="#">{t("common.text.edit")}</Link> <Popconfirm
placement="left"
title={t("robotics.robots.popconfirmEdit.title")}
description={
<Input
placeholder="Name"
value={selectedRobotName}
onChange={(e) => 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"
),
});
}
})
}
>
<Link to="#" onClick={() => setSelectedRobotName(record.name)}>
{t("common.text.edit")}
</Link>
</Popconfirm>
<Popconfirm
placement="left"
title={t("robotics.robots.popconfirmDisconnect.title")}
description={t(
"robotics.robots.popconfirmDisconnect.description"
)}
okText={t("common.button.confirm")}
cancelText={t("common.button.cancel")}
onConfirm={() =>
myFetch(
`/robot/${record.id}`,
"DELETE",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
)
}
>
<Link to="#">{t("common.text.disconnect")}</Link> <Link to="#">{t("common.text.disconnect")}</Link>
</Popconfirm>
</Space> </Space>
), ),
}, },
@ -129,21 +228,27 @@ export default function Robots() {
const getRobotsTableItems = (robots) => { const getRobotsTableItems = (robots) => {
let items = []; let items = [];
robots.forEach((robot) => { robots.sort((a, b) => a.Status - b.Status);
robots.forEach((robot) =>
items.push({ items.push({
key: robot.Id, key: robot.Id,
id: robot.Id, id: robot.Id,
type: getRobotTypeString(robot.Type), type: getRobotTypeString(robot.Type),
name: robot.Name, name: robot.Name,
status: getRobotStatusBadge(robot.Status), status: getRobotStatusBadge(robot.Status),
currentJob: robot.CurrentJob, currentJob:
robot.CurrentJobId === ""
? Constants.TEXT_EMPTY_PLACEHOLDER
: robot.CurrentJobId,
jobsWaiting: robot.JobsWaitingCount, jobsWaiting: robot.JobsWaitingCount,
address: robot.Address, address: robot.Address,
firmwareVersion: robot.FirmwareVersion, firmwareVersion: robot.FirmwareVersion,
connectedAt: FormatDatetime(robot.ConnectedAt), connectedAt: FormatDatetime(robot.ConnectedAt),
createdAt: FormatDatetime(robot.CreatedAt),
actions: robot.Actions, actions: robot.Actions,
}); })
}); );
return items; return items;
}; };
@ -170,6 +275,11 @@ export default function Robots() {
dataIndex: "connectedAt", dataIndex: "connectedAt",
key: "connectedAt", key: "connectedAt",
}, },
{
title: t("robotics.robots.column.firmwareVersion"),
dataIndex: "firmwareVersion",
key: "firmwareVersion",
},
{ {
title: t("robotics.robots.column.actions"), title: t("robotics.robots.column.actions"),
dataIndex: "actions", dataIndex: "actions",
@ -192,9 +302,7 @@ export default function Robots() {
{}, {},
myFetchContentType.JSON, myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS Constants.ROBOTICS_API_ADDRESS
).then((data) => { )
console.log("data", data);
})
} }
> >
<Link to="#">{t("common.text.deny")}</Link> <Link to="#">{t("common.text.deny")}</Link>
@ -216,9 +324,7 @@ export default function Robots() {
{}, {},
myFetchContentType.JSON, myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS Constants.ROBOTICS_API_ADDRESS
).then((data) => { )
console.log("data", data);
})
} }
> >
<Link to="#">{t("common.text.authorize")}</Link> <Link to="#">{t("common.text.authorize")}</Link>
@ -241,6 +347,7 @@ export default function Robots() {
type: getRobotTypeString(robot.Type), type: getRobotTypeString(robot.Type),
address: robot.Address, address: robot.Address,
connectedAt: FormatDatetime(robot.ConnectedAt), connectedAt: FormatDatetime(robot.ConnectedAt),
firmwareVersion: robot.FirmwareVersion,
actions: robot.Actions, actions: robot.Actions,
}); });
}); });
@ -293,12 +400,8 @@ export default function Robots() {
robotsContext.setRobots((arr) => { robotsContext.setRobots((arr) => {
const newArr = [...arr]; const newArr = [...arr];
console.log("arr", arr);
const index = arr.findIndex((x) => x.Id === body.RobotId); const index = arr.findIndex((x) => x.Id === body.RobotId);
console.log("index", index);
if (index !== -1) { if (index !== -1) {
newArr[index].Status = body.Status; newArr[index].Status = body.Status;
} }
@ -335,6 +438,19 @@ export default function Robots() {
return newArr; 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; break;
case ReceivedSSECommands.RemoveUnauthorizedRobot: case ReceivedSSECommands.RemoveUnauthorizedRobot:
robotsContext.setUnauthorizedRobots((arr) => { robotsContext.setUnauthorizedRobots((arr) => {
@ -349,6 +465,32 @@ export default function Robots() {
return newArr; return newArr;
}); });
break; 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: default:
break; break;
} }
@ -365,6 +507,8 @@ export default function Robots() {
return ( return (
<> <>
{notificationContextHolder}
<Typography.Title level={4}> <Typography.Title level={4}>
{t("robotics.robots.header")} ({robotsContext.robots.length}) {t("robotics.robots.header")} ({robotsContext.robots.length})
</Typography.Title> </Typography.Title>

View File

@ -102,6 +102,8 @@ export const Constants = {
MAX_LOG_MANAGER_DISPLAY_NAME_LENGTH: 16, MAX_LOG_MANAGER_DISPLAY_NAME_LENGTH: 16,
MIN_LOG_MANAGER_ADDRESS_LENGTH: 3, MIN_LOG_MANAGER_ADDRESS_LENGTH: 3,
MAX_LOG_MANAGER_ADDRESS_LENGTH: 100, MAX_LOG_MANAGER_ADDRESS_LENGTH: 100,
MIN_ROBOTICS_ROBOT_NAME_LENGTH: 2,
MAX_ROBOTICS_ROBOT_NAME_LENGTH: 30,
}, },
MAX_AVATAR_SIZE: 5 * 1024 * 1024, MAX_AVATAR_SIZE: 5 * 1024 * 1024,
ACCEPTED_AVATAR_FILE_TYPES: [ ACCEPTED_AVATAR_FILE_TYPES: [
@ -1390,7 +1392,7 @@ export function myFetch(
window.location.href = "/"; window.location.href = "/";
} }
return; throw response.status;
} }
// check if response is json // check if response is json