Improve CLI line selection and user access control
Enhanced frontend and backend logic to better manage CLI line selection, ensuring only one user can access a line at a time. Added clipboard copy functionality for PID/SN, improved UI feedback for line usage, and updated event handling for opening and closing CLI sessions. Also improved status messaging for disconnected devices and refactored related components for clarity and maintainability.
This commit is contained in:
parent
8a06650eab
commit
654fbe0468
|
|
@ -112,8 +112,8 @@ export class WebSocketIo {
|
|||
socket.on('disconnect', () => {
|
||||
console.log(`FE disconnected: ${socket.id}`)
|
||||
this.userConnecting.delete(userId)
|
||||
const listLineS = Array.from(this.lineMap.values()).map((el) => el?.config || {})
|
||||
listLineS.forEach((el) => {
|
||||
const listLine = Array.from(this.lineMap.values()).map((el) => el?.config || {})
|
||||
listLine.forEach((el) => {
|
||||
if (el?.userOpenCLI === userName) {
|
||||
const line = this.lineMap.get(el.id)
|
||||
if (line) {
|
||||
|
|
@ -546,6 +546,7 @@ export class WebSocketIo {
|
|||
if (line && line.config.status === 'connected') {
|
||||
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)
|
||||
this.setTimeoutConnect(lineId, line, options.timeout)
|
||||
await sleep(500)
|
||||
await action(line, options)
|
||||
} else {
|
||||
if (this.lineConnecting.includes(lineId)) continue
|
||||
|
|
@ -567,6 +568,7 @@ export class WebSocketIo {
|
|||
const lineReconnect = this.lineMap.get(lineId)
|
||||
if (lineReconnect) {
|
||||
this.setTimeoutConnect(lineId, lineReconnect, options.timeout)
|
||||
await sleep(500)
|
||||
await action(lineReconnect, options)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ function App() {
|
|||
|
||||
socket?.on("init", (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
// console.log(data);
|
||||
data.forEach((value) => {
|
||||
updateValueLineStation(
|
||||
value?.id,
|
||||
|
|
@ -570,6 +571,7 @@ function App() {
|
|||
/>
|
||||
|
||||
<ModalTerminal
|
||||
selectedLines={selectedLines}
|
||||
opened={openModalTerminal}
|
||||
onClose={() => {
|
||||
setOpenModalTerminal(false);
|
||||
|
|
|
|||
|
|
@ -111,11 +111,15 @@ const BottomToolBar = ({
|
|||
<CloseButton
|
||||
style={{ minWidth: "24px" }}
|
||||
aria-label="Clear input"
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
setSelectedLines(
|
||||
selectedLines.filter((line) => line.id !== el.id)
|
||||
)
|
||||
}
|
||||
);
|
||||
socket?.emit("close_cli", {
|
||||
lineId: el?.id,
|
||||
stationId: el.stationId || el.station_id,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
|
@ -174,13 +178,13 @@ const BottomToolBar = ({
|
|||
stationId: station.id,
|
||||
command: valueInput + "\n",
|
||||
});
|
||||
setTimeout(() => {
|
||||
socket?.emit("write_command_line_from_web", {
|
||||
lineIds: listLine.map((line) => line.id),
|
||||
stationId: station.id,
|
||||
command: " \n",
|
||||
});
|
||||
}, 1000);
|
||||
// setTimeout(() => {
|
||||
// socket?.emit("write_command_line_from_web", {
|
||||
// lineIds: listLine.map((line) => line.id),
|
||||
// stationId: station.id,
|
||||
// command: " \n",
|
||||
// });
|
||||
// }, 1000);
|
||||
}
|
||||
setValueInput("");
|
||||
}
|
||||
|
|
@ -204,13 +208,39 @@ const BottomToolBar = ({
|
|||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
station={station}
|
||||
userName={user?.userName}
|
||||
onClick={() => {
|
||||
const lines = station.lines.filter(
|
||||
(line) =>
|
||||
!line?.userOpenCLI || line?.userOpenCLI === user?.userName
|
||||
);
|
||||
if (selectedLines.length !== lines.length) {
|
||||
setSelectedLines(lines);
|
||||
lines.forEach((line) => {
|
||||
socket?.emit("open_cli", {
|
||||
lineId: line.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
userEmail: user?.email,
|
||||
userName: user?.userName,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
lines.forEach((line) => {
|
||||
socket?.emit("close_cli", {
|
||||
lineId: line?.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
});
|
||||
});
|
||||
setSelectedLines([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ButtonDPELP
|
||||
socket={socket}
|
||||
selectedLines={selectedLines}
|
||||
isDisable={isDisable || selectedLines.length === 0}
|
||||
onClick={() => {
|
||||
setSelectedLines([]);
|
||||
// setSelectedLines([]);
|
||||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
setIsDisable(false);
|
||||
|
|
@ -257,7 +287,7 @@ const BottomToolBar = ({
|
|||
).length === 0
|
||||
}
|
||||
onClick={() => {
|
||||
setSelectedLines([]);
|
||||
// setSelectedLines([]);
|
||||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
setIsDisable(false);
|
||||
|
|
|
|||
|
|
@ -223,24 +223,28 @@ export const ButtonCopy = ({
|
|||
|
||||
export const ButtonSelect = ({
|
||||
selectedLines,
|
||||
setSelectedLines,
|
||||
station,
|
||||
onClick,
|
||||
userName,
|
||||
}: {
|
||||
setSelectedLines: (value: React.SetStateAction<TLine[]>) => void;
|
||||
selectedLines: TLine[];
|
||||
station: TStation;
|
||||
onClick: () => void;
|
||||
userName: string;
|
||||
}) => {
|
||||
return (
|
||||
<Button
|
||||
variant="filled"
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => {
|
||||
if (selectedLines.length !== station.lines.length)
|
||||
setSelectedLines(station.lines);
|
||||
else setSelectedLines([]);
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
{selectedLines.length !== station.lines.length
|
||||
{selectedLines.length !==
|
||||
station.lines.filter(
|
||||
(line) => !line?.userOpenCLI || line?.userOpenCLI === userName
|
||||
).length
|
||||
? "Select All"
|
||||
: "Deselect"}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { IScenario, TLine, TStation } from "../untils/types";
|
|||
import classes from "./Component.module.css";
|
||||
import TerminalCLI from "./TerminalXTerm";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import { memo, useMemo, useState } from "react";
|
||||
import { memo, useEffect, useMemo, useState } from "react";
|
||||
import { convertTimestampToDate } from "../untils/helper";
|
||||
import { ButtonDPELP, ButtonScenario } from "./ButtonAction";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
|
|
@ -38,6 +38,17 @@ const CardLine = ({
|
|||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||
const [valueBaud, setValueBaud] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
typeof line?.userOpenCLI !== "undefined" &&
|
||||
typeof line?.userOpenCLI === "string" &&
|
||||
line?.userOpenCLI.length > 0 &&
|
||||
line?.userOpenCLI !== user?.userName
|
||||
)
|
||||
setIsDisabled(true);
|
||||
else setIsDisabled(false);
|
||||
}, [line?.userOpenCLI]);
|
||||
|
||||
const controlApc = (action: string) => {
|
||||
if (!line.outlet) {
|
||||
notifications.show({
|
||||
|
|
@ -78,7 +89,35 @@ const CardLine = ({
|
|||
setIsDisabled(false);
|
||||
}, 5000);
|
||||
};
|
||||
console.log("RERENDER", line.lineNumber);
|
||||
|
||||
const handleClick = (isSelect = false) => {
|
||||
if (
|
||||
typeof line?.userOpenCLI !== "undefined" &&
|
||||
typeof line?.userOpenCLI === "string" &&
|
||||
line?.userOpenCLI.length > 0 &&
|
||||
line?.userOpenCLI !== user?.userName
|
||||
)
|
||||
return;
|
||||
|
||||
if (selectedLines.find((val) => val.id === line.id)) {
|
||||
if (isSelect) {
|
||||
setSelectedLines(selectedLines.filter((val) => val.id !== line.id));
|
||||
socket?.emit("close_cli", {
|
||||
lineId: line?.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setSelectedLines((pre) => [...pre, line]);
|
||||
socket?.emit("open_cli", {
|
||||
lineId: line.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
userEmail: user?.email,
|
||||
userName: user?.userName,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={line.id}
|
||||
|
|
@ -99,9 +138,7 @@ const CardLine = ({
|
|||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (selectedLines.find((val) => val.id === line.id))
|
||||
setSelectedLines(selectedLines.filter((val) => val.id !== line.id));
|
||||
else setSelectedLines((pre) => [...pre, line]);
|
||||
handleClick(true);
|
||||
}}
|
||||
onMouseLeave={() => setTimeout(() => setShowMenu(false), 150)}
|
||||
>
|
||||
|
|
@ -131,6 +168,16 @@ const CardLine = ({
|
|||
<Flex direction={"column"} justify={"center"} align={"center"}>
|
||||
<Box>
|
||||
<Text
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(
|
||||
`PID: ${line?.inventory?.pid || ""} | SN: ${
|
||||
line?.inventory?.sn || ""
|
||||
}`
|
||||
);
|
||||
}}
|
||||
className={classes.buttonCopy}
|
||||
fw={600}
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
|
|
@ -160,7 +207,19 @@ const CardLine = ({
|
|||
<Flex justify={"space-between"} w={"100%"}>
|
||||
<div className={classes.info_line}>
|
||||
PID:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
<Text
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(
|
||||
`PID: ${line?.inventory?.pid || ""} | SN: ${
|
||||
line?.inventory?.sn || ""
|
||||
}`
|
||||
);
|
||||
}}
|
||||
className={`${classes.info_line} ${classes.buttonCopy}`}
|
||||
fs={"italic"}
|
||||
>
|
||||
{line?.inventory?.pid || ""}
|
||||
</Text>
|
||||
{line?.inventory?.vid ? (
|
||||
|
|
@ -176,19 +235,38 @@ const CardLine = ({
|
|||
style={{ width: "120px" }}
|
||||
>
|
||||
SN:{" "}
|
||||
<Text className={classes.info_line} fs={"italic"}>
|
||||
<Text
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(
|
||||
`PID: ${line?.inventory?.pid || ""} | SN: ${
|
||||
line?.inventory?.sn || ""
|
||||
}`
|
||||
);
|
||||
}}
|
||||
className={`${classes.info_line} ${classes.buttonCopy}`}
|
||||
fs={"italic"}
|
||||
>
|
||||
{line?.inventory?.sn || ""}
|
||||
</Text>
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex justify={"space-between"} w={"100%"}>
|
||||
<Box></Box>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
fontSize: "11px",
|
||||
color: "red",
|
||||
}}
|
||||
>
|
||||
{line?.userOpenCLI ? line?.userOpenCLI + " is using" : ""}
|
||||
{line?.userOpenCLI
|
||||
? (line?.userOpenCLI === user?.userName
|
||||
? "You are"
|
||||
: line?.userOpenCLI + " is") + " using"
|
||||
: ""}
|
||||
</div>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
|
@ -360,7 +438,7 @@ const CardLine = ({
|
|||
mt={"8px"}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
color="orange"
|
||||
color="red"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
controlApc("off");
|
||||
|
|
@ -372,7 +450,7 @@ const CardLine = ({
|
|||
mt={"8px"}
|
||||
disabled={isDisabled}
|
||||
variant="outline"
|
||||
color="red"
|
||||
color="orange"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
controlApc("restart");
|
||||
|
|
@ -401,12 +479,13 @@ const CardLine = ({
|
|||
line_id={Number(line?.id)}
|
||||
line={line}
|
||||
station_id={Number(stationItem.id)}
|
||||
isDisabled={
|
||||
typeof line?.userOpenCLI !== "undefined" &&
|
||||
typeof line?.userOpenCLI === "string" &&
|
||||
line?.userOpenCLI.length > 0 &&
|
||||
line?.userOpenCLI !== user?.userName
|
||||
}
|
||||
// isDisabled={
|
||||
// typeof line?.userOpenCLI !== "undefined" &&
|
||||
// typeof line?.userOpenCLI === "string" &&
|
||||
// line?.userOpenCLI.length > 0 &&
|
||||
// line?.userOpenCLI !== user?.userName
|
||||
// }
|
||||
isDisabled={false}
|
||||
line_status={line?.status || ""}
|
||||
fontSize={11}
|
||||
miniSize={true}
|
||||
|
|
@ -421,14 +500,13 @@ const CardLine = ({
|
|||
openTerminal(line);
|
||||
}}
|
||||
onFocus={() => {
|
||||
socket?.emit("open_cli", {
|
||||
lineId: line.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
userEmail: user?.email,
|
||||
userName: user?.userName,
|
||||
});
|
||||
handleClick();
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (
|
||||
!selectedLines.find((value) => value.id === line?.id) &&
|
||||
line?.userOpenCLI === user?.userName
|
||||
)
|
||||
socket?.emit("close_cli", {
|
||||
lineId: line?.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
|
|
|
|||
|
|
@ -124,3 +124,7 @@
|
|||
.topBarLine:hover {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.buttonCopy:hover {
|
||||
background-color: #ccc !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,9 +211,9 @@ export const DrawerAPCControl: React.FC<DrawerProps> = ({
|
|||
default:
|
||||
return (
|
||||
<>
|
||||
{/* <Text fw={800} c="red" fz={"12px"}>
|
||||
WRONG CONFIG
|
||||
</Text> */}
|
||||
<Text fw={800} c="red" fz={"12px"}>
|
||||
DISCONNECTED
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -799,8 +799,6 @@ export const DrawerSwitchControl: React.FC<DrawerProps> = ({
|
|||
case "DISCONNECTED":
|
||||
return (
|
||||
<>
|
||||
<div></div>
|
||||
|
||||
<Text size="sm" fw={800} c="red">
|
||||
{apc.status}
|
||||
</Text>
|
||||
|
|
@ -879,7 +877,13 @@ export const DrawerSwitchControl: React.FC<DrawerProps> = ({
|
|||
}}
|
||||
>
|
||||
<Box ps={"8px"} pt={"4px"}>
|
||||
{dataStation?.switch ? RenderAPCStatus(dataStation?.switch) : ""}
|
||||
{dataStation?.switch ? (
|
||||
RenderAPCStatus(dataStation?.switch)
|
||||
) : (
|
||||
<Text size="sm" fw={800} c="red">
|
||||
DISCONNECTED
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
{dataStation?.switch?.status !== "CONNECTED" ? (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ const ModalTerminal = ({
|
|||
setDisableRequestTakeOver,
|
||||
setCountDownRequest,
|
||||
setDataRequestTakeOver,
|
||||
selectedLines,
|
||||
}: {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
|
|
@ -45,6 +46,7 @@ const ModalTerminal = ({
|
|||
dataRequestTakeOver: IDataTakeOver | undefined;
|
||||
countDownRequest: number;
|
||||
disableRequestTakeOver: boolean;
|
||||
selectedLines: TLine[];
|
||||
setDisableRequestTakeOver: (value: React.SetStateAction<boolean>) => void;
|
||||
setCountDownRequest: (value: React.SetStateAction<number>) => void;
|
||||
setDataRequestTakeOver: (
|
||||
|
|
@ -96,7 +98,10 @@ const ModalTerminal = ({
|
|||
opened={opened}
|
||||
onClose={() => {
|
||||
onClose();
|
||||
if (line?.userOpenCLI === user?.userName)
|
||||
if (
|
||||
line?.userOpenCLI === user?.userName &&
|
||||
!selectedLines.find((value) => value.id === line?.id)
|
||||
)
|
||||
socket?.emit("close_cli", {
|
||||
lineId: line?.id,
|
||||
stationId: line?.station_id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue