ATC_SIMPLE/FRONTEND/src/components/Modal/ModalTerminal.tsx

1242 lines
39 KiB
TypeScript

import {
Box,
Button,
CloseButton,
Flex,
Grid,
Input,
Menu,
Modal,
ScrollArea,
Text,
Textarea,
Tooltip,
} from "@mantine/core";
import type {
IScenario,
SwitchPortsProps,
TDataTicket,
TextFSM,
TextTSMLicense,
THistoryTicket,
TLine,
TStation,
} from "../../untils/types";
import TerminalCLI from "../TerminalXTerm";
import type { Socket } from "socket.io-client";
import { useEffect, useMemo, useState } from "react";
import {
IconCircleCheckFilled,
IconCircleDot,
IconInfoCircle,
} from "@tabler/icons-react";
import { ButtonDPELP, ButtonScenario } from "../ButtonAction";
import CopyIcon from "../CopyIcon";
import moment from "moment";
import axios from "axios";
import { notifications } from "@mantine/notifications";
import classes from "../Component.module.css";
import { listBaudDefault } from "../../untils/constanst";
import { motion } from "motion/react";
const apiUrl = import.meta.env.VITE_BACKEND_URL;
const INIT_TICKET = {
description: "",
sn: "",
model: "",
station_id: 0,
history: "",
status: "open",
};
const ModalTerminal = ({
opened,
onClose,
line,
socket,
stationItem,
scenarios,
selectedLines,
}: {
opened: boolean;
onClose: () => void;
line: TLine | undefined;
socket: Socket | null;
stationItem: TStation | undefined;
scenarios: IScenario[];
selectedLines: TLine[];
}) => {
const user = useMemo(() => {
return localStorage.getItem("user") &&
typeof localStorage.getItem("user") === "string"
? JSON.parse(localStorage.getItem("user") || "")
: null;
}, []);
const [isDisable, setIsDisable] = useState<boolean>(false);
const [isDisableTicket, setIsDisableTicket] = useState<boolean>(false);
const [listPorts, setListPorts] = useState<SwitchPortsProps[]>([]);
const [latestTicket, setLatestTicket] = useState<TDataTicket>(INIT_TICKET);
const [dataTicket, setDataTicket] = useState<TDataTicket>(INIT_TICKET);
const [valueBaud, setValueBaud] = useState<string>("");
const [valueIssue, setValueIssue] = useState<string>("");
const [dataTextfsm, setDataTextfsm] = useState<TextFSM[]>([]);
useEffect(() => {
if (opened && line?.tickets && line?.tickets?.length > 0) {
const data = line?.tickets[0];
setLatestTicket(data);
setDataTicket({ ...data, description: "" });
} else {
setLatestTicket(INIT_TICKET);
setDataTicket(INIT_TICKET);
setValueBaud("");
}
}, [opened, line?.tickets]);
useEffect(() => {
if (opened && line?.latestScenario?.detectAI) {
const data =
line?.latestScenario?.detectAI?.issue &&
Array.isArray(line?.latestScenario?.detectAI?.issue)
? "- " + line?.latestScenario?.detectAI?.issue?.join("\n- ")
: "";
setValueIssue(data);
} else {
setValueIssue("");
}
}, [opened, line?.latestScenario?.detectAI]);
useEffect(() => {
if (opened && line?.data) {
const data = Array.isArray(line?.data) ? line?.data : [];
setDataTextfsm(data);
} else {
setDataTextfsm([]);
}
}, [opened, line?.data]);
useEffect(() => {
setListPorts([]);
}, [stationItem?.id]);
useEffect(() => {
socket?.on("switch_ports_status", (data) => {
if (data.stationId !== stationItem?.id) return;
if (data?.ports && data?.ports.length > 0)
setListPorts(data?.ports || []);
});
return () => {
socket?.off("switch_ports_status");
};
}, [socket, stationItem]);
const renderHistory = (data: TDataTicket) => {
const latest = JSON.parse(latestTicket?.history || "[]");
const list =
data?.history && data?.id
? JSON.parse(data?.history)
: latest?.length > 0
? [latest[0], latest[latest?.length - 1]]
: [];
return (
<div>
{list.reverse().map((item: THistoryTicket, index: number) => (
<div key={index}>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: "4px",
}}
>
<Flex
style={{
gap: "4px",
color:
index === 0
? "#30d100"
: item?.status === "closed"
? "red"
: item?.status === "issue"
? "#fab005"
: "inherit",
}}
>
<Text style={{ fontSize: "14px", fontWeight: "bold" }} fw={600}>
{item?.status === "closed" ? "*" : ""}@{item?.userName}:
</Text>
<Text
style={{
fontSize: "14px",
}}
c={
index === 0
? "#30d100"
: item?.status === "closed"
? "red"
: item?.status === "issue"
? "#fab005"
: "dimmed"
}
fw={400}
>
{item?.description}
</Text>
</Flex>
<Text
style={{
fontSize: "14px",
}}
c="dimmed"
fw={500}
>
{moment(Number(item?.time)).format("HH:m DD/M")}
</Text>
</div>
</div>
))}
</div>
);
};
const handleCreate = async () => {
if (!dataTicket?.description.trim()) {
notifications.show({
title: "Error",
message: "Description is required",
color: "red",
});
return;
}
const payload = {
id: dataTicket.id || 0,
description: dataTicket.description.trim(),
model: line?.inventory?.pid.trim(),
sn: line?.inventory?.sn.trim(),
station_id: Number(stationItem?.id),
line_id: Number(line?.id),
status: "open",
userName: user?.userName,
userId: user?.id,
};
try {
const res = await axios.post(apiUrl + "api/ticket/create", payload);
if (res.status) {
// setLatestTicket(res.data);
// setDataTicket({ ...res.data, description: "" });
// notifications.show({
// title: 'Success',
// message: res.message,
// color: 'green',
// })
socket?.emit("update_ticket", {
lineId: Number(line?.id),
data: [res.data.data, ...(line?.tickets || [])],
stationId: Number(stationItem?.id),
});
return;
}
} catch (error) {
console.log("Error create ticket", error);
notifications.show({
title: "Error",
message: "Failed to create ticket, please try again!",
color: "red",
});
}
};
const handleUpdate = async (status: string) => {
if (!dataTicket?.description.trim()) {
notifications.show({
title: "Error",
message: "Description is required",
color: "red",
});
return;
}
const payload = {
id: dataTicket.id || 0,
description: dataTicket.description.trim()
? dataTicket.description.trim()
: status === "closed"
? "Closed"
: "",
model: dataTicket.model.trim(),
sn: dataTicket.sn.trim(),
station_id: Number(stationItem?.id),
line_id: Number(line?.id),
status: status,
userName: user?.userName,
userId: user?.id,
};
try {
const res = await axios.post(
`${apiUrl + "api/ticket/update" + "/" + dataTicket.id}`,
payload
);
if (res.status) {
// if (res?.data?.status !== "closed")
// setDataTicket({ ...res.data, description: "" });
// else
// setDataTicket({
// id: 0,
// description: "",
// model: latestTicket.model.trim(),
// sn: latestTicket.sn.trim(),
// station_id: latestTicket.station_id,
// history: "",
// status: "open",
// });
// notifications.show({
// title: 'Success',
// message: res.message,
// color: 'green',
// })
socket?.emit("update_ticket", {
lineId: Number(line?.id),
data: line?.tickets?.map((el) =>
el.id === dataTicket.id ? res.data.data : el
),
stationId: Number(stationItem?.id),
});
return;
}
} catch (error) {
console.log("Error update ticket", error);
notifications.show({
title: "Error",
message: "Failed to update ticket, please try again!",
color: "red",
});
}
};
const controlApc = (action: string) => {
if (!line?.outlet) {
notifications.show({
title: "Error",
message: "Hasn't config outlet number",
color: "red",
});
return;
}
const apcName = line.apcName || line.apc_name;
if (!apcName) {
notifications.show({
title: "Error",
message: "Hasn't config apc",
color: "red",
});
return;
}
if (
(apcName === "apc_1" && !stationItem?.apc_1_ip) ||
(apcName === "apc_2" && !stationItem?.apc_2_ip)
) {
notifications.show({
title: "Error",
message: "Hasn't config apc ip",
color: "red",
});
return;
}
socket?.emit("control_apc", {
outletNumbers: [line.outlet],
station: stationItem,
action: action,
apcName: line.apcName || line.apc_name,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 10000);
};
const controlSwitch = (action: string) => {
if (!line?.interface) {
notifications.show({
title: "Error",
message: "Hasn't config interface",
color: "red",
});
return;
}
socket?.emit("control_switch", {
ports: [line?.interface],
command: action,
station: { ...stationItem, lines: [] },
ip: stationItem?.switch_control_ip,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 15000);
};
const findSwitchPort = (portName: string): SwitchPortsProps | null => {
if (listPorts?.length > 0) {
const port = listPorts.find(
(el) => normalizePortName(el.name) === normalizePortName(portName)
);
if (port) return port;
}
return null;
};
const normalizePortName = (port: string): string => {
if (!port) return "";
// Example inputs: "Fa0/1", "Gi0/0/1", "Fa0/0/2"
const match = port.match(/^([A-Za-z]+)([\d/]+)$/);
if (!match) return port;
const type = match[1]; // Fa, Gi, Te, etc.
const numbers = match[2]; // "0/1" / "0/0/1" / "0/0/2"
// Get the last part after slash
const parts = numbers.split("/");
const last = parts[parts.length - 1];
return `${type?.slice(0, 2)}${last}`;
};
const findDataShowVersion = () => {
const showVersion = dataTextfsm?.find(
(d) =>
d.command?.trim() === "show version" ||
d.command?.trim() === "sh version" ||
d.command?.trim() === "show ver" ||
d.command?.trim() === "sh ver"
);
return showVersion?.textfsm && showVersion?.textfsm?.[0]
? showVersion?.textfsm?.[0]
: null;
};
const findDataShowLicense = () => {
const showLicense = dataTextfsm?.find(
(d) =>
d.command?.trim() === "show license" ||
d.command?.trim() === "sh license" ||
d.command?.trim() === "show lic" ||
d.command?.trim() === "sh lic"
);
return showLicense?.textfsm && Array.isArray(showLicense?.textfsm)
? showLicense?.textfsm
: null;
};
return (
<Box>
<Modal
opened={opened}
onClose={() => {
onClose();
if (
line?.userOpenCLI === user?.userName &&
!selectedLines.find((value) => value.id === line?.id)
)
socket?.emit("close_cli", {
lineId: line?.id,
stationId: line?.station_id,
});
}}
size={"100%"}
style={{ position: "absolute", left: 0 }}
title={
<Flex>
<Box
style={{
display: "flex",
// justifyContent: "center",
width: "400px",
}}
>
<div
style={{
alignItems: "center",
fontSize: "13px",
color: "red",
display: "flex",
}}
>
{line?.userOpenCLI
? (line?.userOpenCLI === user?.userName
? "You are"
: line?.userOpenCLI + " is") + " using"
: "Terminal is used"}
</div>
</Box>
</Flex>
}
>
<Grid>
<Grid.Col span={3}>
<Flex style={{ height: "20px" }}>
{line?.connecting && (
<motion.div
style={{ fontSize: "12px", color: "red" }}
animate={{ opacity: [0.2, 1, 0.2] }}
transition={{
duration: 1.2,
repeat: Infinity,
ease: "easeInOut",
}}
>
connecting...
</motion.div>
)}
{line?.runningScenario && (
<motion.div
style={{ fontSize: "12px", color: "red" }}
animate={{ opacity: [0.2, 1, 0.2] }}
transition={{
duration: 1.2,
repeat: Infinity,
ease: "easeInOut",
}}
>
Running {line?.runningScenario}
</motion.div>
)}
</Flex>
<Flex justify={"space-between"} direction={"column"} h={"95%"}>
<Box>
<Flex gap={"sm"} justify={"center"} align={"center"}>
<Text size="xl">
<strong>
Line {line?.lineNumber || line?.line_number || ""}
</strong>
</Text>
<Text size="xl">
- <strong>{line?.port || ""}</strong>
</Text>
{line?.status === "connected" && (
<IconCircleCheckFilled color="green" fontSize={"18px"} />
)}
</Flex>
<Flex mt="4px">
<Text size="md" mr="6px">
BAUD:
</Text>
<Text size="md">
<strong>{line?.baud || ""}</strong>
</Text>
</Flex>
<Flex mt="4px" align="center">
<Text size="md" mr="6px">
PID:
</Text>
<Text
size="md"
style={{
opacity: line?.inventory?.pid ? 1 : 0.5,
cursor: line?.inventory?.pid ? "pointer" : "default",
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (!line?.inventory?.pid) return;
navigator.clipboard.writeText(line.inventory?.pid || "");
}}
>
{line?.inventory?.pid || ""}
</Text>
{line?.inventory?.vid ? (
<Text size="md" ml={"sm"}>
{line?.inventory?.vid}
</Text>
) : (
""
)}
<CopyIcon
value={line?.inventory?.pid}
label="Copy PID"
copiedLabel="Copied PID"
ml={8}
/>
</Flex>
<Flex mt="4px" align="center">
<Text size="md" mr="6px">
SN:
</Text>
<Text
size="md"
style={{
opacity: line?.inventory?.sn ? 1 : 0.5,
cursor: line?.inventory?.sn ? "pointer" : "default",
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (!line?.inventory?.sn) return;
navigator.clipboard.writeText(line.inventory?.sn || "");
}}
>
{line?.inventory?.sn || ""}
</Text>
<CopyIcon
value={line?.inventory?.sn}
label="Copy SN"
copiedLabel="Copied SN"
ml={8}
mr={8}
/>
<CopyIcon
value={
line?.inventory?.pid && line?.inventory?.sn
? `Line ${
line?.lineNumber || line?.line_number || ""
}: ${line.inventory?.pid}${
line.inventory?.vid ? ` ${line.inventory.vid}` : ""
} - ${line.inventory?.sn}`
: ""
}
label="Copy PID + SN"
copiedLabel="Copied PID + SN"
color="grape"
copiedColor="violet"
/>
</Flex>
<Flex mt="4px">
<Text size="md" mr={"6px"} fw={"bold"}>
IOS:
</Text>
<Text size="md">
{findDataShowVersion()
? findDataShowVersion()?.SOFTWARE_IMAGE
? findDataShowVersion()?.SOFTWARE_IMAGE +
" " +
(findDataShowVersion()?.VERSION || "")
: ""
: ""}
</Text>
</Flex>
</Box>
<Flex>
<Text size="md" mr={"sm"} fw={"bold"}>
License:
</Text>
<Text size="md">
{findDataShowLicense()
? findDataShowLicense()
?.filter(
(el: TextTSMLicense) =>
el.LICENSE_TYPE === "Permanent"
)
?.map((v: TextTSMLicense) => v.FEATURE)
?.join(", ")
: ""}
</Text>
</Flex>
<Flex>
<Text size="md" mr={"sm"} fw={"bold"}>
Sh env/module:
</Text>
<Text size="md">{""}</Text>
</Flex>
<Flex>
<Text size="md" mr={"sm"} fw={"bold"}>
Mem/Flash:
</Text>
<Text size="md">
{findDataShowVersion()
? findDataShowVersion()?.MEMORY +
(findDataShowVersion()?.USB_FLASH
? " - " + findDataShowVersion()?.USB_FLASH
: "")
: ""}
</Text>
</Flex>
<Box>
<Text size="md" mr={"sm"} fw={"bold"}>
Warning from test report: AI
</Text>
<Box>
<Textarea
rows={5}
size="sm"
placeholder="Report from AI"
value={valueIssue}
onChange={(event) =>
setValueIssue(event.currentTarget.value)
}
/>
</Box>
</Box>
<Box
style={{
display: "flex",
justifyContent: "center",
}}
>
<fieldset
style={{
width: "280px",
}}
>
<Flex justify={"center"}>
<IconCircleDot
color={
line?.interface &&
findSwitchPort(line?.interface)?.status === "ON"
? "green"
: "red"
}
/>
<Flex>
<Text size="sm" ml={"sm"}>
Internet
</Text>
{line?.interface ? (
findSwitchPort(line?.interface)?.status === "ON" ? (
<Text size="sm" ml={"4px"} c={"green"}>
Connected ({normalizePortName(line?.interface)})
</Text>
) : (
<Text size="sm" ml={"4px"} c={"red"}>
Not Connected ({normalizePortName(line?.interface)})
</Text>
)
) : (
<Text c={"red"} size="sm" ml={"4px"}>
Not config
</Text>
)}
</Flex>
</Flex>
<Flex justify={"space-around"} mt={"4px"}>
<Button
className={classes.buttonControl}
disabled={isDisable || !line?.interface}
fw={400}
variant="outline"
color="green"
size="xs"
onClick={() => {
controlSwitch("on");
}}
>
ON
</Button>
<Button
className={classes.buttonControl}
disabled={isDisable || !line?.interface}
fw={400}
variant="outline"
color="red"
size="xs"
onClick={() => {
controlSwitch("off");
}}
>
OFF
</Button>
<Button
className={classes.buttonControl}
disabled={isDisable || !line?.interface}
fw={400}
variant="outline"
color="orange"
size="xs"
onClick={() => {
controlSwitch("restart");
}}
>
Restart
</Button>
</Flex>
</fieldset>
</Box>
<Flex
justify={"center"}
style={{
borderTop: "1px solid #ccc",
borderBottom: "1px solid #ccc",
paddingTop: "12px",
paddingBottom: "12px",
}}
>
<Button
disabled={isDisable}
fw={400}
w={"120px"}
variant="outline"
color="green"
size="xs"
onClick={() => {
if (!line?.lineClear && !line?.line_clear) {
notifications.show({
title: "Error",
message: "Clear line has not been configured",
color: "red",
});
return;
}
socket?.emit("clear_line", {
lineClear: line?.lineClear || line?.line_clear,
stationId: stationItem?.id,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
>
Clear line
</Button>
</Flex>
</Flex>
</Grid.Col>
<Grid.Col
span={9}
style={{
// borderRight: "1px solid #ccc",
borderLeft: "1px solid #ccc",
}}
>
<TerminalCLI
cliOpened={opened}
socket={socket}
content={line?.output ?? ""}
initContent={line?.netOutput ?? ""}
loadingContent={line?.loadingOutput}
line_id={Number(line?.id)}
line={line}
station_id={Number(stationItem?.id)}
isDisabled={
typeof line?.userOpenCLI !== "undefined" &&
line?.userOpenCLI !== user?.userName
}
line_status={line?.status || ""}
/>
<Flex justify={"space-around"} mt={"md"} pt={"md"} pb={"md"}>
<ButtonDPELP
socket={socket}
selectedLines={line ? [line] : []}
isDisable={isDisable}
onClick={() => {
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 10000);
}}
/>
<Menu trigger="hover" withArrow shadow="md" position="top">
<Menu.Target>
<Button
fw={400}
disabled={isDisable}
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={line ? [line] : []}
isDisable={isDisable}
onClick={() => {
// setSelectedLines([]);
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
scenario={el}
/>
))}
</Box>
</Menu.Dropdown>
</Menu>
<Menu
closeOnItemClick={false}
closeOnClickOutside={false}
trigger="hover"
shadow="md"
position="top"
>
<Menu.Target>
<Button
fw={400}
disabled={isDisable}
variant="filled"
size="xs"
style={{ height: "30px", width: "100px" }}
onClick={() => {}}
>
BAUD
</Button>
</Menu.Target>
<Menu.Dropdown style={{ width: "110px" }}>
<Flex
justify={"space-between"}
direction={"column"}
style={{
gap: "8px",
}}
>
{listBaudDefault.map((el, i) => (
<Button
key={i}
disabled={isDisable}
variant="outline"
size="xs"
fw={400}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
socket?.emit("set_baud", {
lineId: line?.id,
baud: el,
stationId: Number(stationItem?.id),
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
>
{el}
</Button>
))}
<Input
placeholder="Custom"
value={valueBaud}
onChange={(e) => setValueBaud(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
socket?.emit("set_baud", {
lineId: line?.id,
baud: Number(valueBaud),
stationId: Number(stationItem?.id),
});
setValueBaud("");
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}
}}
/>
</Flex>
</Menu.Dropdown>
</Menu>
<Button
disabled={true}
fw={400}
variant="filled"
color="green"
size="xs"
onClick={() => {}}
>
Select license
</Button>
<Button
disabled={true}
fw={400}
variant="filled"
color="green"
size="xs"
onClick={() => {}}
>
Select IOS
</Button>
<Button
fw={400}
disabled={isDisable}
variant="filled"
color="orange"
size="xs"
onClick={() => {
socket?.emit("write_command_line_from_web", {
lineIds: [line?.id],
stationId: stationItem?.id,
command: "spam_break",
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 5000);
}}
>
Send Break
</Button>
<Menu trigger="hover" withArrow shadow="md" position="top">
<Menu.Target>
<Button
fw={400}
disabled={isDisable}
variant="filled"
style={{ height: "30px", width: "100px" }}
onClick={() => {}}
>
Power
</Button>
</Menu.Target>
<Menu.Dropdown>
<Flex gap={"4px"} p={"4px"}>
<Button
disabled={isDisable}
mr={"8px"}
fw={400}
variant="outline"
color="green"
size="xs"
onClick={() => {
controlApc("on");
}}
>
ON
</Button>
<Button
disabled={isDisable}
mr={"8px"}
fw={400}
variant="outline"
color="red"
size="xs"
onClick={() => {
controlApc("off");
}}
>
OFF
</Button>
<Button
disabled={isDisable}
fw={400}
variant="outline"
color="orange"
size="xs"
onClick={() => {
controlApc("restart");
}}
>
Restart
</Button>
</Flex>
</Menu.Dropdown>
</Menu>
</Flex>
</Grid.Col>
<Grid.Col span={3} display={"none"}>
<Box>
<Tooltip
label={
<div>
<Flex>
<Text
style={{
fontSize: "14px",
}}
c={"#30d100"}
fw={500}
>
Text
</Text>
: is opened
</Flex>
<Flex>
<Text
style={{
fontSize: "14px",
}}
c={"#fab005"}
fw={500}
>
Text
</Text>
: is issue
</Flex>
<Flex>
<Text
style={{
fontSize: "14px",
}}
c={"red"}
fw={500}
>
Text
</Text>
: is closed
</Flex>
</div>
}
position="right"
>
<Flex align={"center"} gap={"6px"} w={"85px"}>
<Text size="md">
<strong>Ticket:</strong>{" "}
</Text>
<IconInfoCircle
color="#3bb7e9"
width={"18px"}
height={"18px"}
/>
</Flex>
</Tooltip>
</Box>
<ScrollArea
h={"65vh"}
style={{ border: "1px solid #ccc" }}
p={"4px"}
>
{renderHistory(latestTicket)}
</ScrollArea>
<Box mt={"8px"}>
<Input
style={{
width: "100%",
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
}}
placeholder={"Input description ticket"}
value={dataTicket.description || ""}
onChange={(event) => {
setDataTicket((pre) => ({
...pre,
description: event.target.value,
}));
}}
onKeyDown={(event) => {
if (event.key === "Enter" && dataTicket.description) {
setDataTicket((pre) => ({ ...pre, description: "" }));
if (dataTicket?.status === "closed" || !dataTicket.id) {
handleCreate();
} else handleUpdate("open");
setIsDisableTicket(true);
setTimeout(() => {
setIsDisableTicket(false);
}, 2000);
}
}}
rightSectionPointerEvents="all"
rightSection={
<CloseButton
aria-label="Clear input"
onClick={() =>
setDataTicket((pre) => ({ ...pre, description: "" }))
}
style={{
display: dataTicket?.description ? undefined : "none",
}}
/>
}
/>
</Box>
<Box mt={"8px"}>
<Flex justify={"end"} mt={"4px"}>
{dataTicket?.status === "closed" || !dataTicket.id ? (
<Button
disabled={isDisableTicket || !dataTicket.description}
mr={"8px"}
fw={400}
variant="outline"
size="xs"
onClick={() => {
setDataTicket((pre) => ({ ...pre, description: "" }));
handleCreate();
setIsDisableTicket(true);
setTimeout(() => {
setIsDisableTicket(false);
}, 2000);
}}
>
Open
</Button>
) : (
<Button
disabled={isDisableTicket || !dataTicket.description}
mr={"8px"}
fw={400}
variant="outline"
size="xs"
onClick={() => {
handleUpdate("open");
setIsDisableTicket(true);
setTimeout(() => {
setIsDisableTicket(false);
}, 2000);
}}
>
Send
</Button>
)}
<Button
disabled={
isDisableTicket ||
dataTicket?.status === "closed" ||
!dataTicket.id ||
!dataTicket.description
}
mr={"8px"}
fw={400}
variant="outline"
color="orange"
size="xs"
onClick={() => {
setDataTicket((pre) => ({ ...pre, description: "" }));
handleUpdate("issue");
setIsDisableTicket(true);
setTimeout(() => {
setIsDisableTicket(false);
}, 2000);
}}
>
Issue
</Button>
<Button
disabled={
isDisableTicket ||
!dataTicket.id ||
dataTicket?.status === "closed"
}
fw={400}
variant="outline"
color="red"
size="xs"
onClick={() => {
handleUpdate("closed");
setIsDisableTicket(true);
setTimeout(() => {
setIsDisableTicket(false);
}, 2000);
}}
>
Close
</Button>
</Flex>
</Box>
</Grid.Col>
</Grid>
</Modal>
</Box>
);
};
export default ModalTerminal;