Update modal select scenario
This commit is contained in:
parent
55bcc055ea
commit
6f785644be
|
|
@ -6,21 +6,22 @@ import {
|
|||
Flex,
|
||||
Grid,
|
||||
Input,
|
||||
Menu,
|
||||
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 } from "../untils/types";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import { ButtonDPELP, ButtonScenario, ButtonSelect } from "./ButtonAction";
|
||||
import { ButtonDPELP, ButtonSelect } from "./ButtonAction";
|
||||
import DrawerLogs from "./DrawerLogs";
|
||||
import { DrawerAPCControl, DrawerSwitchControl } from "./DrawerControl";
|
||||
import { isJsonString } from "../untils/helper";
|
||||
import { motion } from "motion/react";
|
||||
import { IconCaretDown, IconCaretUp } from "@tabler/icons-react";
|
||||
import { IconCaretDown, IconCaretUp, IconPlayerPlay } from "@tabler/icons-react";
|
||||
|
||||
interface TabsProps {
|
||||
selectedLines: TLine[];
|
||||
|
|
@ -65,6 +66,7 @@ const BottomToolBar = ({
|
|||
}, []);
|
||||
const [valueInput, setValueInput] = useState<string>("");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [openScenarioModal, setOpenScenarioModal] = useState<boolean>(false);
|
||||
// const [activeTabBottom, setActiveTabBottom] = useState<string>("command");
|
||||
// const [isExpand, setIsExpand] = useState<boolean>(true);
|
||||
|
||||
|
|
@ -75,21 +77,205 @@ const BottomToolBar = ({
|
|||
}, [selectedLines?.length]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{
|
||||
height: isExpand ? "20vh" : 0,
|
||||
y: 0, // đẩy xuống khi thu nhỏ
|
||||
}}
|
||||
transition={{ type: "spring", stiffness: 180, damping: 20 }}
|
||||
style={{
|
||||
width: "100%",
|
||||
position: "fixed",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{/* Modal chọn Scenario - Custom Simple Modal */}
|
||||
{openScenarioModal && (
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: "rgba(0,0,0,0.6)",
|
||||
zIndex: 99998,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backdropFilter: "blur(3px)",
|
||||
}}
|
||||
onClick={() => setOpenScenarioModal(false)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
borderRadius: "12px",
|
||||
maxWidth: "1000px",
|
||||
width: "auto",
|
||||
maxHeight: "85vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
boxShadow: "0 20px 60px rgba(0,0,0,0.3)",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Header */}
|
||||
<Flex
|
||||
justify="space-between"
|
||||
align="center"
|
||||
p="lg"
|
||||
style={{
|
||||
borderBottom: "1px solid #e9ecef",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Text fw={700} size="xl">
|
||||
🎯 Select Scenario to Run
|
||||
</Text>
|
||||
<CloseButton
|
||||
size="lg"
|
||||
onClick={() => setOpenScenarioModal(false)}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{/* Content */}
|
||||
<div
|
||||
style={{
|
||||
padding: "20px",
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden",
|
||||
flex: 1,
|
||||
}}
|
||||
className={classes.hideScrollBar}
|
||||
>
|
||||
{scenarios.length > 0 ? (
|
||||
<Grid gutter="md" style={{ margin: 0 }}>
|
||||
{scenarios.map((scenario, index) => (
|
||||
<Grid.Col key={scenario.id} span={6}>
|
||||
<Card
|
||||
shadow="sm"
|
||||
padding="lg"
|
||||
radius="md"
|
||||
withBorder
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s ease",
|
||||
height: "100%",
|
||||
}}
|
||||
className={classes.scenarioCard}
|
||||
>
|
||||
<Flex direction="column" gap="xs" h="100%">
|
||||
<Flex justify="space-between" align="center">
|
||||
<Text fw={600} size="md" lineClamp={1}>
|
||||
{scenario.title}
|
||||
</Text>
|
||||
<Badge color="blue" variant="light">
|
||||
#{index + 1}
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="xs" wrap="wrap">
|
||||
<Badge size="sm" color="gray" variant="dot">
|
||||
Timeout: {scenario.timeout}ms
|
||||
</Badge>
|
||||
{scenario.isReboot && (
|
||||
<Badge size="sm" color="orange" variant="dot">
|
||||
Reboot
|
||||
</Badge>
|
||||
)}
|
||||
<Badge size="sm" color="green" variant="dot">
|
||||
{JSON.parse(scenario.body || "[]").length} steps
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
||||
<Text
|
||||
size="xs"
|
||||
c="dimmed"
|
||||
lineClamp={2}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{JSON.parse(scenario.body || "[]")
|
||||
.slice(0, 2)
|
||||
.map((step: { send: string }) => step.send)
|
||||
.join(" → ")}
|
||||
{JSON.parse(scenario.body || "[]").length > 2 &&
|
||||
" ..."}
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
variant="light"
|
||||
color="green"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
disabled={
|
||||
isDisable ||
|
||||
selectedLines.filter(
|
||||
(el) =>
|
||||
!el?.userEmailOpenCLI ||
|
||||
el?.userEmailOpenCLI === user?.email
|
||||
).length === 0
|
||||
}
|
||||
onClick={() => {
|
||||
setOpenScenarioModal(false);
|
||||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
setIsDisable(false);
|
||||
}, 5000);
|
||||
|
||||
// Run scenario cho các line được chọn
|
||||
selectedLines
|
||||
.filter(
|
||||
(el) =>
|
||||
!el?.userEmailOpenCLI ||
|
||||
el?.userEmailOpenCLI === user?.email
|
||||
)
|
||||
.forEach((el) => {
|
||||
socket?.emit(
|
||||
"run_scenario",
|
||||
Object.assign(el, {
|
||||
scenario: scenario,
|
||||
})
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Run Scenario
|
||||
</Button>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
) : (
|
||||
<Flex
|
||||
direction="column"
|
||||
align="center"
|
||||
justify="center"
|
||||
style={{ minHeight: "300px" }}
|
||||
gap="md"
|
||||
>
|
||||
<Text size="xl" c="dimmed">
|
||||
📋
|
||||
</Text>
|
||||
<Text ta="center" c="dimmed" size="lg">
|
||||
No scenarios available
|
||||
</Text>
|
||||
<Text ta="center" c="dimmed" size="sm">
|
||||
Please create a new scenario to get started
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{
|
||||
height: isExpand ? "20vh" : 0,
|
||||
y: 0, // đẩy xuống khi thu nhỏ
|
||||
}}
|
||||
transition={{ type: "spring", stiffness: 180, damping: 20 }}
|
||||
style={{
|
||||
width: "100%",
|
||||
position: "fixed",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<Box style={{ position: "relative" }}>
|
||||
<ActionIcon
|
||||
style={{
|
||||
|
|
@ -323,64 +509,16 @@ const BottomToolBar = ({
|
|||
}, 5000);
|
||||
}}
|
||||
/>
|
||||
<Menu
|
||||
trigger="hover"
|
||||
withArrow
|
||||
shadow="md"
|
||||
position="top"
|
||||
<Button
|
||||
fw={400}
|
||||
disabled={isDisable || selectedLines.length === 0}
|
||||
variant="filled"
|
||||
color="yellow"
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => setOpenScenarioModal(true)}
|
||||
>
|
||||
<Menu.Target>
|
||||
<Button
|
||||
fw={400}
|
||||
disabled={isDisable || selectedLines.length === 0}
|
||||
variant="filled"
|
||||
color="yellow"
|
||||
style={{ height: "30px", width: "100px" }}
|
||||
onClick={() => {}}
|
||||
>
|
||||
Scenario
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Box
|
||||
px="xs"
|
||||
py="sm"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
gap: "12px",
|
||||
}}
|
||||
>
|
||||
{scenarios.map((el, i) => (
|
||||
<ButtonScenario
|
||||
key={i}
|
||||
socket={socket}
|
||||
selectedLines={selectedLines.filter(
|
||||
(el) =>
|
||||
!el?.userEmailOpenCLI ||
|
||||
el?.userEmailOpenCLI === user?.email
|
||||
)}
|
||||
isDisable={
|
||||
isDisable ||
|
||||
selectedLines.filter(
|
||||
(el) =>
|
||||
!el?.userEmailOpenCLI ||
|
||||
el?.userEmailOpenCLI === user?.email
|
||||
).length === 0
|
||||
}
|
||||
onClick={() => {
|
||||
// setSelectedLines([]);
|
||||
setIsDisable(true);
|
||||
setTimeout(() => {
|
||||
setIsDisable(false);
|
||||
}, 5000);
|
||||
}}
|
||||
scenario={el}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
Scenario
|
||||
</Button>
|
||||
<DrawerLogs
|
||||
socket={socket}
|
||||
isLogModalOpen={isLogModalOpen}
|
||||
|
|
@ -403,7 +541,8 @@ const BottomToolBar = ({
|
|||
<Grid.Col span={1}></Grid.Col>
|
||||
</Grid>
|
||||
</Box>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,13 @@
|
|||
border-top: solid rgba(201, 201, 201, 0.377) 1px; */
|
||||
}
|
||||
|
||||
.hideScrollBar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.hideScrollBar::-webkit-scrollbar {
|
||||
display: none;
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
.containerBottom {
|
||||
|
|
@ -148,3 +153,15 @@
|
|||
padding-right: 4px !important;
|
||||
padding-left: 4px !important;
|
||||
}
|
||||
|
||||
.scenarioCard {
|
||||
border: 2px solid transparent;
|
||||
background: linear-gradient(white, white) padding-box,
|
||||
linear-gradient(145deg, #e0e0e0, #f5f5f5) border-box;
|
||||
}
|
||||
|
||||
.scenarioCard:hover {
|
||||
border-color: #4dabf7;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue