Refactor BottomToolBar UI and remove debug log

Removed a console.log statement from line_connection.ts to clean up debug output. Refactored BottomToolBar.tsx for improved layout, spacing, and usability, including changes to selected line display, button arrangement, and input handling for better user experience.
This commit is contained in:
nguyentrungthat 2025-11-27 08:53:06 +07:00
parent 065900f48b
commit f2a78c76d2
2 changed files with 305 additions and 258 deletions

View File

@ -696,8 +696,6 @@ export default class LineConnection {
timestamp: now,
})
console.log(newItem)
// Lấy phần tử cuối
const lastItems = await redis.zrevrange(key, 0, 0)
if (lastItems.length > 0) {

View File

@ -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[];
@ -296,217 +301,143 @@ const BottomToolBar = ({
zIndex: 1,
}}
>
<Box style={{ position: "relative" }}>
<ActionIcon
style={{
position: "absolute",
top: isExpand ? -4 : -24,
left: "50%",
translate: "-19px 0",
backgroundColor: "#e3e0e0",
width: "55px",
}}
variant="light"
onClick={() => {
setExpanded((prev) => !prev);
}}
>
{isExpand ? (
<IconCaretDown color="green" />
) : (
<IconCaretUp color="green" />
)}
</ActionIcon>
<Grid>
<Grid.Col span={1}></Grid.Col>
<Grid.Col span={10}>
<Tabs
defaultValue="command"
orientation="vertical"
value={activeTabBottom}
onChange={(val) => {
setActiveTabBottom(val || "command");
}}
className={classes.containerBottom}
style={{ height: "20vh" }}
>
<Tabs.List>
<Tabs.Tab
style={{
backgroundColor:
activeTabBottom === "command" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="command"
>
Command Line
</Tabs.Tab>
<Tabs.Tab
style={{
backgroundColor: activeTabBottom === "apc" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="apc"
>
APC
</Tabs.Tab>
<Tabs.Tab
style={{
backgroundColor:
activeTabBottom === "switch" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="switch"
>
Switch
</Tabs.Tab>
</Tabs.List>
<Box style={{ position: "relative" }}>
<ActionIcon
style={{
position: "absolute",
top: isExpand ? -4 : -24,
left: "50%",
translate: "-19px 0",
backgroundColor: "#e3e0e0",
width: "55px",
}}
variant="light"
onClick={() => {
setExpanded((prev) => !prev);
}}
>
{isExpand ? (
<IconCaretDown color="green" />
) : (
<IconCaretUp color="green" />
)}
</ActionIcon>
<Grid>
<Grid.Col span={1}></Grid.Col>
<Grid.Col span={10}>
<Tabs
defaultValue="command"
orientation="vertical"
value={activeTabBottom}
onChange={(val) => {
setActiveTabBottom(val || "command");
}}
className={classes.containerBottom}
style={{ height: "20vh" }}
>
<Tabs.List>
<Tabs.Tab
style={{
backgroundColor:
activeTabBottom === "command" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="command"
>
Command Line
</Tabs.Tab>
<Tabs.Tab
style={{
backgroundColor:
activeTabBottom === "apc" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="apc"
>
APC
</Tabs.Tab>
<Tabs.Tab
style={{
backgroundColor:
activeTabBottom === "switch" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
}}
value="switch"
>
Switch
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="command" p={"xs"}>
<Flex justify={"space-between"}>
<ScrollArea h={"15vh"}>
<Flex wrap={"wrap"} gap={"xs"} w={"400px"}>
{selectedLines.map((el) => (
<Box
key={el.id}
style={{
paddingLeft: "4px",
height: "30px",
width: "80px",
backgroundColor: "#d4e3ff",
borderRadius: "8px",
}}
>
<Flex align={"center"} justify={"center"} gap={"4px"}>
<Text fz={"12px"}>Line {el.lineNumber}</Text>
<CloseButton
style={{ minWidth: "24px" }}
aria-label="Clear input"
onClick={() => {
setSelectedLines(
selectedLines.filter(
(line) => line.id !== el.id
)
);
socket?.emit("close_cli", {
lineId: el?.id,
stationId: el.stationId || el.station_id,
});
}}
/>
</Flex>
</Box>
))}
</Flex>
</ScrollArea>
<Box pl={"md"} pr={"md"}>
<Flex justify={"space-between"} mb={"xs"}>
<Flex></Flex>
<Button
fw={400}
disabled={isDisable || selectedLines.length === 0}
variant="filled"
color="orange"
size="xs"
radius="md"
onClick={() => {
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: "spam_break",
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}
}}
>
Send Break
</Button>
</Flex>
<Tabs.Panel value="command" p={"xs"}>
<Flex justify={"space-between"}>
<Box>
<Input
ref={inputRef}
style={{
width: "30vw",
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
}}
placeholder={"Send command to port(s)"}
value={valueInput}
onChange={(event) => {
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={
<CloseButton
aria-label="Clear input"
onClick={() => setValueInput("")}
style={{
display: valueInput ? undefined : "none",
}}
/>
}
/>
</Box>
</Box>
<Box style={{ width: "220px" }}>
<Flex align={"center"} wrap={"wrap"} gap={"xs"}>
<ButtonSelect
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 {
<ScrollArea h={"12vh"}>
<Flex wrap={"wrap"} gap={"8px"} w={"420px"}>
{selectedLines.map((el) => (
<Box
key={el.id}
style={{
position: "relative",
padding: "4px 6px",
height: "26px",
width: "60px",
backgroundColor: "#d4e3ff",
borderRadius: "8px",
}}
>
{/* Close button góc trên phải */}
<CloseButton
size="xs"
style={{
position: "absolute",
top: "-4px",
right: "-6px",
minWidth: "18px",
width: "18px",
height: "18px",
zIndex: 10,
}}
onClick={() => {
setSelectedLines(
selectedLines.filter(
(line) => line.id !== el.id
)
);
socket?.emit("close_cli", {
lineId: el?.id,
stationId: el.stationId || el.station_id,
});
}}
/>
<Flex
align={"center"}
justify={"center"}
h="100%"
>
<Text fz={"11px"}>Line {el.lineNumber}</Text>
</Flex>
</Box>
))}
</Flex>
</ScrollArea>
{selectedLines?.length > 0 ? (
<Button
fw={400}
className={classes.buttonControl}
variant="outline"
onClick={() => {
const lines = station.lines.filter(
(line) =>
!line?.userOpenCLI ||
line?.userOpenCLI === user?.userName
);
lines.forEach((line) => {
socket?.emit("close_cli", {
lineId: line?.id,
@ -514,53 +445,171 @@ const BottomToolBar = ({
});
});
setSelectedLines([]);
}}
>
Clear
</Button>
) : (
""
)}
</Box>
<Box pl={"md"} pr={"md"}>
<Flex justify={"space-between"} mb={"xs"}>
<Flex></Flex>
<Button
fw={400}
disabled={isDisable || selectedLines.length === 0}
variant="filled"
color="orange"
size="xs"
radius="md"
onClick={() => {
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: "spam_break",
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}
}}
>
Send Break
</Button>
</Flex>
<Box>
<Input
ref={inputRef}
style={{
width: "30vw",
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
}}
placeholder={"Send command to port(s)"}
value={valueInput}
onChange={(event) => {
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={
<CloseButton
aria-label="Clear input"
onClick={() => setValueInput("")}
style={{
display: valueInput ? undefined : "none",
}}
/>
}
}}
/>
<ButtonDPELP
socket={socket}
selectedLines={selectedLines}
isDisable={isDisable || selectedLines.length === 0}
onClick={() => {
// setSelectedLines([]);
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
/>
<Button
fw={400}
disabled={isDisable || selectedLines.length === 0}
variant="filled"
color="yellow"
style={{ height: "30px", width: "100px" }}
onClick={() => setOpenScenarioModal(true)}
>
Scenario
</Button>
<DrawerLogs
socket={socket}
isLogModalOpen={isLogModalOpen}
setIsLogModalOpen={setIsLogModalOpen}
testLogContent={testLogContent}
setTestLogContent={setTestLogContent}
/>
</Flex>
</Box>
</Flex>
</Tabs.Panel>
<Tabs.Panel value="apc" ps={"xs"}>
<DrawerAPCControl socket={socket} stationAPI={station} />
</Tabs.Panel>
<Tabs.Panel value="switch" ps={"xs"}>
<DrawerSwitchControl socket={socket} stationAPI={station} />
</Tabs.Panel>
</Tabs>
</Grid.Col>
<Grid.Col span={1}></Grid.Col>
</Grid>
</Box>
/>
</Box>
</Box>
<Box style={{ width: "220px" }}>
<Flex align={"center"} wrap={"wrap"} gap={"xs"}>
<ButtonSelect
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([]);
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
/>
<Button
fw={400}
disabled={isDisable || selectedLines.length === 0}
variant="filled"
color="yellow"
style={{ height: "30px", width: "100px" }}
onClick={() => setOpenScenarioModal(true)}
>
Scenario
</Button>
<DrawerLogs
socket={socket}
isLogModalOpen={isLogModalOpen}
setIsLogModalOpen={setIsLogModalOpen}
testLogContent={testLogContent}
setTestLogContent={setTestLogContent}
/>
</Flex>
</Box>
</Flex>
</Tabs.Panel>
<Tabs.Panel value="apc" ps={"xs"}>
<DrawerAPCControl socket={socket} stationAPI={station} />
</Tabs.Panel>
<Tabs.Panel value="switch" ps={"xs"}>
<DrawerSwitchControl socket={socket} stationAPI={station} />
</Tabs.Panel>
</Tabs>
</Grid.Col>
<Grid.Col span={1}></Grid.Col>
</Grid>
</Box>
</motion.div>
{/* Drawer Scenario để Add/Edit */}