From 1b9b18ce3bb336435e780fbb2d98c5c73c8db950 Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:35:37 +0700 Subject: [PATCH] Refactor BottomToolBar and update user types Improved BottomToolBar command line UI with better line selection and clearing logic. Updated TUser type to include 'id' and 'email' fields, and fixed DragTabs to support both 'userId' and 'id' for user key mapping. --- FRONTEND/src/components/BottomToolBar.tsx | 620 ++++++++++++---------- FRONTEND/src/components/DragTabs.tsx | 3 +- FRONTEND/src/untils/types.ts | 2 + 3 files changed, 337 insertions(+), 288 deletions(-) diff --git a/FRONTEND/src/components/BottomToolBar.tsx b/FRONTEND/src/components/BottomToolBar.tsx index 52ff00e..0c65db3 100644 --- a/FRONTEND/src/components/BottomToolBar.tsx +++ b/FRONTEND/src/components/BottomToolBar.tsx @@ -14,7 +14,7 @@ import { } from "@mantine/core"; import { useEffect, useMemo, useRef, useState } from "react"; import classes from "./Component.module.css"; -import type { IScenario, TLine, TStation } from "../untils/types"; +import type { IScenario, TLine, TStation, TUser } from "../untils/types"; import type { Socket } from "socket.io-client"; import { ButtonDPELP, ButtonSelect } from "./ButtonAction"; import DrawerLogs from "./DrawerLogs"; @@ -22,7 +22,12 @@ import { DrawerAPCControl, DrawerSwitchControl } from "./DrawerControl"; import DrawerScenario from "./DrawerScenario"; import { isJsonString } from "../untils/helper"; import { motion } from "motion/react"; -import { IconCaretDown, IconCaretUp, IconPlayerPlay, IconPlus } from "@tabler/icons-react"; +import { + IconCaretDown, + IconCaretUp, + IconPlayerPlay, + IconPlus, +} from "@tabler/icons-react"; interface TabsProps { selectedLines: TLine[]; @@ -58,7 +63,7 @@ const ScenarioCard = ({ index: number; isDisable: boolean; selectedLines: TLine[]; - user: any; + user: TUser; socket: Socket | null; setOpenScenarioModal: (value: boolean) => void; setIsDisable: (value: boolean) => void; @@ -73,7 +78,7 @@ const ScenarioCard = ({ const rect = cardRef.current.getBoundingClientRect(); const overlayWidth = 400; const viewportWidth = window.innerWidth; - + // Tính toán vị trí để overlay không tràn ra ngoài màn hình let left = rect.left; if (left + overlayWidth > viewportWidth) { @@ -82,7 +87,7 @@ const ScenarioCard = ({ if (left < 10) { left = 10; // 10px margin từ bên trái } - + setOverlayPosition({ top: rect.top, left: left, @@ -98,7 +103,7 @@ const ScenarioCard = ({ const rect = cardRef.current.getBoundingClientRect(); const overlayWidth = 400; const viewportWidth = window.innerWidth; - + let left = rect.left; if (left + overlayWidth > viewportWidth) { left = viewportWidth - overlayWidth - 10; @@ -106,7 +111,7 @@ const ScenarioCard = ({ if (left < 10) { left = 10; } - + setOverlayPosition({ top: rect.top, left: left, @@ -122,7 +127,7 @@ const ScenarioCard = ({ scrollContainers.forEach((container) => { container.addEventListener("scroll", updatePosition, true); }); - + window.addEventListener("scroll", updatePosition, true); window.addEventListener("resize", updatePosition); @@ -138,10 +143,7 @@ const ScenarioCard = ({ return ( -
+
- !el?.userEmailOpenCLI || el?.userEmailOpenCLI === user?.email + !el?.userEmailOpenCLI || + el?.userEmailOpenCLI === user?.email ).length === 0 } onClick={() => { @@ -270,23 +273,21 @@ const ScenarioCard = ({ overflow: "auto", }} > - {steps - .slice(0, 5) - .map((step: { send: string }, i: number) => ( - - {i + 1}. {step.send || "(empty)"} - - ))} + {steps.slice(0, 5).map((step: { send: string }, i: number) => ( + + {i + 1}. {step.send || "(empty)"} + + ))} {steps.length > 5 && ( ... and {steps.length - 5} more @@ -420,7 +421,11 @@ const BottomToolBar = ({ {scenarios.length > 0 ? ( {scenarios.map((scenario, index) => ( - - { - setExpanded((prev) => !prev); - }} - > - {isExpand ? ( - - ) : ( - - )} - - - - - { - setActiveTabBottom(val || "command"); - }} - className={classes.containerBottom} - style={{ height: "20vh" }} - > - - - Command Line - - - APC - - - Switch - - + + { + setExpanded((prev) => !prev); + }} + > + {isExpand ? ( + + ) : ( + + )} + + + + + { + setActiveTabBottom(val || "command"); + }} + className={classes.containerBottom} + style={{ height: "20vh" }} + > + + + Command Line + + + APC + + + Switch + + - - - - - {selectedLines.map((el) => ( - - - Line {el.lineNumber} - { - setSelectedLines( - selectedLines.filter( - (line) => line.id !== el.id - ) - ); - socket?.emit("close_cli", { - lineId: el?.id, - stationId: el.stationId || el.station_id, - }); - }} - /> - - - ))} - - - - - - - + + - { - const newValue = event.currentTarget.value; - setValueInput(newValue); - }} - onKeyDown={(event) => { - if (event.key === "Enter") { - const listLine = selectedLines.length - ? selectedLines - : station?.lines; - if (listLine?.length) { - socket?.emit("write_command_line_from_web", { - lineIds: listLine.map((line) => line.id), - stationId: station.id, - command: valueInput + "\r\n", - }); - // setTimeout(() => { - // socket?.emit("write_command_line_from_web", { - // lineIds: listLine.map((line) => line.id), - // stationId: station.id, - // command: " \n", - // }); - // }, 1000); - } - setValueInput(""); - } - }} - rightSectionPointerEvents="all" - rightSection={ - setValueInput("")} - style={{ - display: valueInput ? undefined : "none", - }} - /> - } - /> - - - - - { - 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 { + + + {selectedLines.map((el) => ( + + {/* Close button góc trên phải */} + { + setSelectedLines( + selectedLines.filter( + (line) => line.id !== el.id + ) + ); + socket?.emit("close_cli", { + lineId: el?.id, + stationId: el.stationId || el.station_id, + }); + }} + /> + + + Line {el.lineNumber} + + + ))} + + + {selectedLines?.length > 0 ? ( + + ) : ( + "" + )} + + + + + + + + { + const newValue = event.currentTarget.value; + setValueInput(newValue); + }} + onKeyDown={(event) => { + if (event.key === "Enter") { + const listLine = selectedLines.length + ? selectedLines + : station?.lines; + if (listLine?.length) { + socket?.emit("write_command_line_from_web", { + lineIds: listLine.map((line) => line.id), + stationId: station.id, + command: valueInput + "\r\n", + }); + // setTimeout(() => { + // socket?.emit("write_command_line_from_web", { + // lineIds: listLine.map((line) => line.id), + // stationId: station.id, + // command: " \n", + // }); + // }, 1000); + } + setValueInput(""); + } + }} + rightSectionPointerEvents="all" + rightSection={ + setValueInput("")} + style={{ + display: valueInput ? undefined : "none", + }} + /> } - }} - /> - { - // setSelectedLines([]); - setIsDisable(true); - setTimeout(() => { - setIsDisable(false); - }, 5000); - }} - /> - - - - - - - - - - - - - - - - - + /> + + + + + { + 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([]); + } + }} + /> + { + // setSelectedLines([]); + setIsDisable(true); + setTimeout(() => { + setIsDisable(false); + }, 5000); + }} + /> + + + + + + + + + + + + + + + + + {/* Drawer Scenario để Add/Edit */} diff --git a/FRONTEND/src/components/DragTabs.tsx b/FRONTEND/src/components/DragTabs.tsx index 7ec1470..b0a3611 100644 --- a/FRONTEND/src/components/DragTabs.tsx +++ b/FRONTEND/src/components/DragTabs.tsx @@ -2,7 +2,6 @@ import { ActionIcon, Avatar, Box, - Button, Flex, Group, Menu, @@ -296,7 +295,7 @@ export default function DraggableTabs({ ( - {el.userName} + {el.userName} ))} > diff --git a/FRONTEND/src/untils/types.ts b/FRONTEND/src/untils/types.ts index da8329f..03ee99c 100644 --- a/FRONTEND/src/untils/types.ts +++ b/FRONTEND/src/untils/types.ts @@ -105,6 +105,8 @@ export type TLine = { export type TUser = { userId: number; userName: string; + id: number; + email: string; }; export type APCProps = {