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.
This commit is contained in:
nguyentrungthat 2025-11-27 10:35:37 +07:00
parent b5bb90ca4e
commit 1b9b18ce3b
3 changed files with 337 additions and 288 deletions

View File

@ -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;
@ -138,10 +143,7 @@ const ScenarioCard = ({
return (
<Grid.Col key={scenario.id} span={3}>
<div
ref={cardRef}
style={{ position: "relative" }}
>
<div ref={cardRef} style={{ position: "relative" }}>
<Card
shadow="sm"
padding="md"
@ -178,7 +180,8 @@ const ScenarioCard = ({
isDisable ||
selectedLines.filter(
(el) =>
!el?.userEmailOpenCLI || el?.userEmailOpenCLI === user?.email
!el?.userEmailOpenCLI ||
el?.userEmailOpenCLI === user?.email
).length === 0
}
onClick={() => {
@ -270,9 +273,7 @@ const ScenarioCard = ({
overflow: "auto",
}}
>
{steps
.slice(0, 5)
.map((step: { send: string }, i: number) => (
{steps.slice(0, 5).map((step: { send: string }, i: number) => (
<Text
key={i}
size="xs"
@ -420,7 +421,11 @@ const BottomToolBar = ({
{scenarios.length > 0 ? (
<Grid
gutter="md"
style={{ margin: 0, overflow: "visible", position: "relative" }}
style={{
margin: 0,
overflow: "visible",
position: "relative",
}}
>
{scenarios.map((scenario, index) => (
<ScenarioCard
@ -524,7 +529,8 @@ const BottomToolBar = ({
</Tabs.Tab>
<Tabs.Tab
style={{
backgroundColor: activeTabBottom === "apc" ? "#c8d9fd" : "",
backgroundColor:
activeTabBottom === "apc" ? "#c8d9fd" : "",
fontSize: "13px",
paddingTop: "8px",
paddingBottom: "8px",
@ -549,24 +555,33 @@ const BottomToolBar = ({
<Tabs.Panel value="command" p={"xs"}>
<Flex justify={"space-between"}>
<ScrollArea h={"15vh"}>
<Flex wrap={"wrap"} gap={"xs"} w={"400px"}>
<Box>
<ScrollArea h={"12vh"}>
<Flex wrap={"wrap"} gap={"8px"} w={"420px"}>
{selectedLines.map((el) => (
<Box
key={el.id}
style={{
paddingLeft: "4px",
height: "30px",
width: "80px",
position: "relative",
padding: "4px 6px",
height: "26px",
width: "60px",
backgroundColor: "#d4e3ff",
borderRadius: "8px",
}}
>
<Flex align={"center"} justify={"center"} gap={"4px"}>
<Text fz={"12px"}>Line {el.lineNumber}</Text>
{/* Close button góc trên phải */}
<CloseButton
style={{ minWidth: "24px" }}
aria-label="Clear input"
size="xs"
style={{
position: "absolute",
top: "-4px",
right: "-6px",
minWidth: "18px",
width: "18px",
height: "18px",
zIndex: 10,
}}
onClick={() => {
setSelectedLines(
selectedLines.filter(
@ -579,11 +594,44 @@ const BottomToolBar = ({
});
}}
/>
<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,
stationId: line.stationId || line.station_id,
});
});
setSelectedLines([]);
}}
>
Clear
</Button>
) : (
""
)}
</Box>
<Box pl={"md"} pr={"md"}>
<Flex justify={"space-between"} mb={"xs"}>
<Flex></Flex>

View File

@ -2,7 +2,6 @@ import {
ActionIcon,
Avatar,
Box,
Button,
Flex,
Group,
Menu,
@ -296,7 +295,7 @@ export default function DraggableTabs({
<Tooltip
withArrow
label={usersConnecting.map((el) => (
<Text key={el.userId}>{el.userName}</Text>
<Text key={el.userId || el.id}>{el.userName}</Text>
))}
>
<Avatar radius="xl" me={"sm"}>

View File

@ -105,6 +105,8 @@ export type TLine = {
export type TUser = {
userId: number;
userName: string;
id: number;
email: string;
};
export type APCProps = {