import { ActionIcon, Box, Button, CloseButton, Flex, Grid, ScrollArea, Tabs, Text, Card, Badge, } from "@mantine/core"; import { useEffect, useMemo, useRef, useState } from "react"; import classes from "./Component.module.css"; import type { IScenario, TLine, TStation, TUser } from "../untils/types"; import type { Socket } from "socket.io-client"; import { ButtonDPELP, ButtonSelect } from "./ButtonAction"; import DrawerLogs from "./Drawer/DrawerLogs"; import { DrawerAPCControl, DrawerSwitchControl } from "./Drawer/DrawerControl"; import DrawerScenario from "./Modal/ModalScenario"; import { isJsonString } from "../untils/helper"; import { motion } from "motion/react"; import { IconCaretDown, IconCaretUp, IconPlayerPlay, IconPlus, } from "@tabler/icons-react"; import InputHistory from "./InputHistory"; interface TabsProps { selectedLines: TLine[]; socket: Socket | null; setSelectedLines: (value: React.SetStateAction) => void; isDisable: boolean; station: TStation; stationId: number; setIsDisable: (value: React.SetStateAction) => void; testLogContent: string; isLogModalOpen: boolean; setIsLogModalOpen: (value: React.SetStateAction) => void; setTestLogContent: (value: React.SetStateAction) => void; scenarios: IScenario[]; setScenarios: (value: React.SetStateAction) => void; setExpanded: (value: React.SetStateAction) => void; activeTabBottom: string; setActiveTabBottom: (value: React.SetStateAction) => void; isExpand: boolean; } // Component cho từng Scenario Card const ScenarioCard = ({ scenario, index, isDisable, selectedLines, user, socket, setOpenScenarioModal, setIsDisable, }: { scenario: IScenario; index: number; isDisable: boolean; selectedLines: TLine[]; user: TUser; socket: Socket | null; setOpenScenarioModal: (value: boolean) => void; setIsDisable: (value: boolean) => void; }) => { const [isHovered, setIsHovered] = useState(false); const [overlayPosition, setOverlayPosition] = useState({ top: 0, left: 0 }); const cardRef = useRef(null); const steps = JSON.parse(scenario.body || "[]"); const handleMouseEnter = () => { if (cardRef.current) { const rect = cardRef.current.getBoundingClientRect(); const overlayWidth = 400; const viewportWidth = window.innerWidth; const gap = 10; // Khoảng cách giữa card và overlay // Đặt overlay bên phải card let left = rect.right + gap; // Nếu overlay tràn ra ngoài màn hình bên phải → đặt bên trái card if (left + overlayWidth > viewportWidth) { left = rect.left - overlayWidth - gap; // Nếu vẫn tràn ra ngoài bên trái → đặt ở giữa màn hình if (left < 10) { left = (viewportWidth - overlayWidth) / 2; } } setOverlayPosition({ top: rect.top, left: left, }); } setIsHovered(true); }; useEffect(() => { if (isHovered && cardRef.current) { const updatePosition = () => { if (cardRef.current) { const rect = cardRef.current.getBoundingClientRect(); const overlayWidth = 400; const viewportWidth = window.innerWidth; const gap = 10; // Khoảng cách giữa card và overlay // Đặt overlay bên phải card let left = rect.right + gap; // Nếu overlay tràn ra ngoài màn hình bên phải → đặt bên trái card if (left + overlayWidth > viewportWidth) { left = rect.left - overlayWidth - gap; // Nếu vẫn tràn ra ngoài bên trái → đặt ở giữa màn hình if (left < 10) { left = (viewportWidth - overlayWidth) / 2; } } setOverlayPosition({ top: rect.top, left: left, }); } }; // Update position immediately updatePosition(); // Listen to scroll events (including inside modal) const scrollContainers = document.querySelectorAll('[style*="overflow"]'); scrollContainers.forEach((container) => { container.addEventListener("scroll", updatePosition, true); }); window.addEventListener("scroll", updatePosition, true); window.addEventListener("resize", updatePosition); return () => { scrollContainers.forEach((container) => { container.removeEventListener("scroll", updatePosition, true); }); window.removeEventListener("scroll", updatePosition, true); window.removeEventListener("resize", updatePosition); }; } }, [isHovered]); return (
setIsHovered(false)} > {scenario.title} {/* Hover overlay - Fixed position để không bị cắt bởi modal */} {isHovered && (
e.stopPropagation()} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > {scenario.title} #{index + 1} Timeout: {scenario.timeout}ms {scenario.isReboot && ( Reboot )} {steps.length} steps Commands Preview: {steps.slice(0, 5).map((step: { send: string }, i: number) => ( {i + 1}. {step.send || "(empty)"} ))} {steps.length > 5 && ( ... and {steps.length - 5} more )}
)}
); }; const BottomToolBar = ({ selectedLines, socket, setSelectedLines, isDisable, station, setIsDisable, testLogContent, isLogModalOpen, setIsLogModalOpen, setTestLogContent, scenarios, setScenarios, setExpanded, setActiveTabBottom, activeTabBottom, isExpand, stationId, }: TabsProps) => { const user = useMemo(() => { return localStorage.getItem("user") && isJsonString(localStorage.getItem("user")) ? JSON.parse(localStorage.getItem("user") || "") : null; }, []); const [openScenarioModal, setOpenScenarioModal] = useState(false); const [openDrawerScenario, setOpenDrawerScenario] = useState(false); // const [activeTabBottom, setActiveTabBottom] = useState("command"); // const [isExpand, setIsExpand] = useState(true); return ( <> {/* Modal chọn Scenario - Custom Simple Modal */} { setExpanded((prev) => !prev); }} > {isExpand ? ( ) : ( )} {isExpand ? ( { setActiveTabBottom(val || "command"); }} className={classes.containerBottom} style={{ height: "100%" }} > Command Line APC Switch {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 && ( { selectedLines.forEach((line) => { socket?.emit("close_cli", { lineId: line?.id, stationId: line.stationId || line.station_id, }); }); setSelectedLines([]); }} > Clear )} Selected: {selectedLines.length} /{" "} {station.lines.length} { 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.forEach((line) => { socket?.emit("close_cli", { lineId: line?.id, stationId: line.stationId || line.station_id, }); }); setSelectedLines([]); } }} /> { if ( selectedLines.length > 0 // && // selectedLines.length === station?.lines?.length ) { socket?.emit("run_all_dpelp", { lineIds: selectedLines.map((line) => line.id), stationName: station.name, stationId: station.id, }); } setIsDisable(true); setTimeout(() => { setIsDisable(false); }, 5000); }} /> ) : ( {selectedLines.map((el) => ( { 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 && ( { selectedLines.forEach((line) => { socket?.emit("close_cli", { lineId: line?.id, stationId: line.stationId || line.station_id, }); }); setSelectedLines([]); }} > Clear )} Selected: {selectedLines.length} /{" "} {station.lines.length} { 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.forEach((line) => { socket?.emit("close_cli", { lineId: line?.id, stationId: line.stationId || line.station_id, }); }); setSelectedLines([]); } }} /> )} {/* Drawer Scenario để Add/Edit */} setOpenDrawerScenario(false)} /> ); }; export default BottomToolBar;