Enhance ticket handling and UI interactions
Backend: Added lineId to ticket creation, improved ticket update logic, and switched ticket update route to POST. Added 'update_ticket' event to socket.io provider. Frontend: Integrated 'motion' for animated bottom toolbar, added expand/collapse functionality, improved ticket creation and update flows, and ensured terminal focus on CLI open. Adjusted delays in ButtonDPELP, improved ticket info copy, and enhanced input handling in ModalTerminal. Updated dependencies to include 'motion'.
This commit is contained in:
parent
d908cf204c
commit
c36b9f69df
|
|
@ -85,6 +85,7 @@ export default class TicketsController {
|
|||
model: payload.model.trim(),
|
||||
sn: payload.sn.trim(),
|
||||
stationId: payload.station_id,
|
||||
lineId: payload.line_id,
|
||||
status: 'open',
|
||||
history: JSON.stringify(history),
|
||||
},
|
||||
|
|
@ -149,11 +150,14 @@ export default class TicketsController {
|
|||
const listHistory = ticket.history ? JSON.parse(ticket.history) : []
|
||||
listHistory.unshift(history)
|
||||
payload.history = JSON.stringify(listHistory)
|
||||
delete payload.userName
|
||||
delete payload.userId
|
||||
ticket.merge(payload)
|
||||
await ticket.save()
|
||||
|
||||
return response.ok({ status: true, message: 'Ticket updated successfully', data: ticket })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return response.internalServerError({
|
||||
status: false,
|
||||
message: 'Failed to update ticket',
|
||||
|
|
|
|||
|
|
@ -448,6 +448,10 @@ export class WebSocketIo {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('update_ticket', async (data) => {
|
||||
io.emit('update_ticket', data)
|
||||
})
|
||||
})
|
||||
|
||||
socketServer.listen(SOCKET_IO_PORT, () => {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ router
|
|||
router.post('/all', '#controllers/tickets_controller.getAll')
|
||||
router.post('create', '#controllers/tickets_controller.create')
|
||||
|
||||
router.put('update/:id', '#controllers/tickets_controller.update')
|
||||
router.post('update/:id', '#controllers/tickets_controller.update')
|
||||
router.delete('delete/:id', '#controllers/tickets_controller.delete')
|
||||
})
|
||||
.prefix('api/ticket')
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
"@xterm/addon-fit": "^0.10.0",
|
||||
"axios": "^1.12.2",
|
||||
"moment": "^2.30.1",
|
||||
"motion": "^12.23.24",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.4",
|
||||
|
|
@ -2868,6 +2869,33 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.24",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
|
|
@ -3339,6 +3367,47 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/motion": {
|
||||
"version": "12.23.24",
|
||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz",
|
||||
"integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.23.24",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.23",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"@xterm/addon-fit": "^0.10.0",
|
||||
"axios": "^1.12.2",
|
||||
"moment": "^2.30.1",
|
||||
"motion": "^12.23.24",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.4",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ function App() {
|
|||
const [usersConnecting, setUsersConnecting] = useState<TUser[]>([]);
|
||||
const [testLogContent, setTestLogContent] = useState("");
|
||||
const [isLogModalOpen, setIsLogModalOpen] = useState(false);
|
||||
const [expandedBottomBar, setExpandedBottomBar] = useState(true);
|
||||
|
||||
const connectApcSwitch = (station: TStation) => {
|
||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||
|
|
@ -281,6 +282,18 @@ function App() {
|
|||
}, 100);
|
||||
});
|
||||
|
||||
socket?.on("update_ticket", (data) => {
|
||||
setTimeout(() => {
|
||||
updateValueLineStation(
|
||||
data.lineId,
|
||||
{
|
||||
tickets: data.data,
|
||||
},
|
||||
data?.stationId
|
||||
);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// ✅ cleanup on unmount or when socket changes
|
||||
return () => {
|
||||
socket.off("init");
|
||||
|
|
@ -293,6 +306,7 @@ function App() {
|
|||
socket.off("user_close_cli");
|
||||
socket.off("response_content_log");
|
||||
socket.off("data_textfsm");
|
||||
socket.off("update_ticket");
|
||||
};
|
||||
}, [socket, stations, selectedLine]);
|
||||
|
||||
|
|
@ -396,7 +410,7 @@ function App() {
|
|||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
<ScrollArea h={"73vh"}>
|
||||
<ScrollArea h={expandedBottomBar ? "73vh" : "85vh"}>
|
||||
{station.lines.length > 8 ? (
|
||||
<Grid
|
||||
style={{
|
||||
|
|
@ -492,6 +506,7 @@ function App() {
|
|||
setIsLogModalOpen={setIsLogModalOpen}
|
||||
setTestLogContent={setTestLogContent}
|
||||
scenarios={scenarios}
|
||||
setExpanded={setExpandedBottomBar}
|
||||
/>
|
||||
</Flex>
|
||||
</Tabs.Panel>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
CloseButton,
|
||||
|
|
@ -18,6 +19,8 @@ import { ButtonDPELP, ButtonScenario, 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";
|
||||
|
||||
interface TabsProps {
|
||||
selectedLines: TLine[];
|
||||
|
|
@ -31,6 +34,7 @@ interface TabsProps {
|
|||
setIsLogModalOpen: (value: React.SetStateAction<boolean>) => void;
|
||||
setTestLogContent: (value: React.SetStateAction<string>) => void;
|
||||
scenarios: IScenario[];
|
||||
setExpanded: (value: React.SetStateAction<boolean>) => void;
|
||||
}
|
||||
|
||||
const BottomToolBar = ({
|
||||
|
|
@ -45,6 +49,7 @@ const BottomToolBar = ({
|
|||
setIsLogModalOpen,
|
||||
setTestLogContent,
|
||||
scenarios,
|
||||
setExpanded,
|
||||
}: TabsProps) => {
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") &&
|
||||
|
|
@ -54,8 +59,46 @@ const BottomToolBar = ({
|
|||
}, []);
|
||||
const [valueInput, setValueInput] = useState<string>("");
|
||||
const [activeTabBottom, setActiveBottom] = useState<string>("command");
|
||||
const [isExpand, setIsExpand] = useState<boolean>(true);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{
|
||||
height: isExpand ? "15vh" : 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={{
|
||||
position: "absolute",
|
||||
top: isExpand ? -4 : -24,
|
||||
left: "50%",
|
||||
translate: "-19px 0",
|
||||
backgroundColor: "#e3e0e0",
|
||||
width: "55px",
|
||||
}}
|
||||
variant="light"
|
||||
onClick={() => {
|
||||
setIsExpand((prev) => !prev);
|
||||
setExpanded((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
{isExpand ? (
|
||||
<IconCaretDown color="green" />
|
||||
) : (
|
||||
<IconCaretUp color="green" />
|
||||
)}
|
||||
</ActionIcon>
|
||||
<Grid>
|
||||
<Grid.Col span={1}></Grid.Col>
|
||||
<Grid.Col span={10}>
|
||||
|
|
@ -72,7 +115,8 @@ const BottomToolBar = ({
|
|||
<Tabs.List>
|
||||
<Tabs.Tab
|
||||
style={{
|
||||
backgroundColor: activeTabBottom === "command" ? "#c8d9fd" : "",
|
||||
backgroundColor:
|
||||
activeTabBottom === "command" ? "#c8d9fd" : "",
|
||||
fontSize: "13px",
|
||||
paddingTop: "8px",
|
||||
paddingBottom: "8px",
|
||||
|
|
@ -94,7 +138,8 @@ const BottomToolBar = ({
|
|||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
style={{
|
||||
backgroundColor: activeTabBottom === "switch" ? "#c8d9fd" : "",
|
||||
backgroundColor:
|
||||
activeTabBottom === "switch" ? "#c8d9fd" : "",
|
||||
fontSize: "13px",
|
||||
paddingTop: "8px",
|
||||
paddingBottom: "8px",
|
||||
|
|
@ -127,7 +172,9 @@ const BottomToolBar = ({
|
|||
aria-label="Clear input"
|
||||
onClick={() => {
|
||||
setSelectedLines(
|
||||
selectedLines.filter((line) => line.id !== el.id)
|
||||
selectedLines.filter(
|
||||
(line) => line.id !== el.id
|
||||
)
|
||||
);
|
||||
socket?.emit("close_cli", {
|
||||
lineId: el?.id,
|
||||
|
|
@ -337,6 +384,8 @@ const BottomToolBar = ({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={1}></Grid.Col>
|
||||
</Grid>
|
||||
</Box>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -68,42 +68,42 @@ export const ButtonDPELP = ({
|
|||
{
|
||||
expect: "",
|
||||
send: "show diag",
|
||||
delay: "2000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
{
|
||||
expect: "",
|
||||
send: "show post",
|
||||
delay: "3000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
{
|
||||
expect: "",
|
||||
send: "show env all",
|
||||
delay: "3000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
{
|
||||
expect: "",
|
||||
send: "show license",
|
||||
delay: "3000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
{
|
||||
expect: "",
|
||||
send: "show log",
|
||||
delay: "3000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
{
|
||||
expect: "",
|
||||
send: "show platform",
|
||||
delay: "3000",
|
||||
delay: "1500",
|
||||
repeat: "1",
|
||||
note: "",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const CardLine = ({
|
|||
}, []);
|
||||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||
const [valueBaud, setValueBaud] = useState<string>("");
|
||||
const [focusTerminal, setFocusTerminal] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
@ -109,6 +110,7 @@ const CardLine = ({
|
|||
}
|
||||
} else {
|
||||
setSelectedLines((pre) => [...pre, line]);
|
||||
setFocusTerminal(true);
|
||||
socket?.emit("open_cli", {
|
||||
lineId: line.id,
|
||||
stationId: line.stationId || line.station_id,
|
||||
|
|
@ -177,6 +179,10 @@ const CardLine = ({
|
|||
navigator.clipboard.writeText(
|
||||
`PID: ${line?.inventory?.pid || ""} | SN: ${
|
||||
line?.inventory?.sn || ""
|
||||
} | Ticket: ${
|
||||
line?.tickets && line?.tickets?.length > 0
|
||||
? line?.tickets[0].description
|
||||
: ""
|
||||
}`
|
||||
);
|
||||
}}
|
||||
|
|
@ -541,6 +547,7 @@ const CardLine = ({
|
|||
handleClick();
|
||||
}}
|
||||
onBlur={() => {
|
||||
setFocusTerminal(false);
|
||||
if (
|
||||
!selectedLines.find((value) => value.id === line?.id) &&
|
||||
line?.userOpenCLI === user?.userName
|
||||
|
|
@ -550,6 +557,7 @@ const CardLine = ({
|
|||
stationId: line.stationId || line.station_id,
|
||||
});
|
||||
}}
|
||||
focusTerminal={focusTerminal}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ const ModalTerminal = ({
|
|||
? JSON.parse(localStorage.getItem("user") || "")
|
||||
: null;
|
||||
}, []);
|
||||
const [inputTicket, setInputTicket] = useState<string>("");
|
||||
const [isDisable, setIsDisable] = useState<boolean>(false);
|
||||
const [isDisableTicket, setIsDisableTicket] = useState<boolean>(false);
|
||||
const [latestTicket, setLatestTicket] = useState<TDataTicket>({
|
||||
|
|
@ -116,7 +115,7 @@ const ModalTerminal = ({
|
|||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", fontWeight: "bold" }} fw={600}>
|
||||
@{item?.userName}:
|
||||
{item?.status === "closed" ? "*" : ""}@{item?.userName}:
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
|
|
@ -168,14 +167,18 @@ const ModalTerminal = ({
|
|||
description: dataTicket.description.trim(),
|
||||
model: dataTicket.model.trim(),
|
||||
sn: dataTicket.sn.trim(),
|
||||
station_id: Number(dataTicket.station_id),
|
||||
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) {
|
||||
// setDataTicket(res.data)
|
||||
// setLatestTicket(res.data);
|
||||
// setDataTicket({ ...res.data, description: "" });
|
||||
|
||||
// notifications.show({
|
||||
// title: 'Success',
|
||||
|
|
@ -183,10 +186,11 @@ const ModalTerminal = ({
|
|||
// color: 'green',
|
||||
// })
|
||||
|
||||
// socket?.emit(
|
||||
// "create_ticket",
|
||||
// payload,
|
||||
// )
|
||||
socket?.emit("update_ticket", {
|
||||
lineId: Number(line?.id),
|
||||
data: [res.data.data, ...(line?.tickets || [])],
|
||||
stationId: Number(stationItem?.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -218,38 +222,44 @@ const ModalTerminal = ({
|
|||
: "",
|
||||
model: dataTicket.model.trim(),
|
||||
sn: dataTicket.sn.trim(),
|
||||
station_id: Number(dataTicket.station_id),
|
||||
station_id: Number(stationItem?.id),
|
||||
line_id: Number(line?.id),
|
||||
status: status,
|
||||
userName: user?.userName,
|
||||
userId: user?.id,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.put(
|
||||
`${apiUrl + "api/ticket/create" + "/" + dataTicket.id}`,
|
||||
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",
|
||||
});
|
||||
// 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(
|
||||
// SOCKET_EVENTS.RELOAD_TICKET.RELOAD_TICKET_FROM_WEB,
|
||||
// payload,
|
||||
// )
|
||||
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) {
|
||||
|
|
@ -308,7 +318,7 @@ const ModalTerminal = ({
|
|||
>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<Flex justify={"space-between"} direction={"column"} h={"100%"}>
|
||||
<Flex justify={"space-between"} direction={"column"} h={"95%"}>
|
||||
<Box>
|
||||
<Flex gap={"sm"} justify={"center"} align={"center"}>
|
||||
<Text size="xl">
|
||||
|
|
@ -324,7 +334,7 @@ const ModalTerminal = ({
|
|||
)}
|
||||
</Flex>
|
||||
<Flex mt="4px">
|
||||
<Text size="md" w={"50px"}>
|
||||
<Text size="md" mr="6px">
|
||||
BAUD:
|
||||
</Text>
|
||||
<Text size="md">
|
||||
|
|
@ -332,7 +342,7 @@ const ModalTerminal = ({
|
|||
</Text>
|
||||
</Flex>
|
||||
<Flex mt="4px">
|
||||
<Text size="md" w={"50px"}>
|
||||
<Text size="md" mr="6px">
|
||||
PID:
|
||||
</Text>
|
||||
<Text size="md">{line?.inventory?.pid || ""}</Text>
|
||||
|
|
@ -345,13 +355,13 @@ const ModalTerminal = ({
|
|||
)}
|
||||
</Flex>
|
||||
<Flex mt="4px">
|
||||
<Text size="md" w={"50px"}>
|
||||
<Text size="md" mr="6px">
|
||||
SN:
|
||||
</Text>
|
||||
<Text size="md">{line?.inventory?.sn || ""}</Text>
|
||||
</Flex>
|
||||
<Flex mt="4px">
|
||||
<Text size="md" mr={"sm"} fw={"bold"} w={"50px"}>
|
||||
<Text size="md" mr={"6px"} fw={"bold"}>
|
||||
IOS:
|
||||
</Text>
|
||||
<Text size="md">{""}</Text>
|
||||
|
|
@ -543,7 +553,6 @@ const ModalTerminal = ({
|
|||
variant="filled"
|
||||
color="orange"
|
||||
size="xs"
|
||||
radius="md"
|
||||
onClick={() => {
|
||||
socket?.emit("write_command_line_from_web", {
|
||||
lineIds: [line?.id],
|
||||
|
|
@ -648,7 +657,11 @@ const ModalTerminal = ({
|
|||
</Flex>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<ScrollArea h={600} style={{ border: "1px solid #ccc" }} p={"4px"}>
|
||||
<ScrollArea
|
||||
h={"65vh"}
|
||||
style={{ border: "1px solid #ccc" }}
|
||||
p={"4px"}
|
||||
>
|
||||
{renderHistory(latestTicket)}
|
||||
</ScrollArea>
|
||||
<Box mt={"8px"}>
|
||||
|
|
@ -658,14 +671,16 @@ const ModalTerminal = ({
|
|||
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
|
||||
}}
|
||||
placeholder={"Input description ticket"}
|
||||
value={inputTicket}
|
||||
value={dataTicket.description || ""}
|
||||
onChange={(event) => {
|
||||
const newValue = event.currentTarget.value;
|
||||
setInputTicket(newValue);
|
||||
setDataTicket((pre) => ({
|
||||
...pre,
|
||||
description: event.target.value,
|
||||
}));
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
setInputTicket("");
|
||||
if (event.key === "Enter" && dataTicket.description) {
|
||||
setDataTicket((pre) => ({ ...pre, description: "" }));
|
||||
if (dataTicket?.status === "closed") {
|
||||
handleCreate();
|
||||
} else handleUpdate("open");
|
||||
|
|
@ -679,9 +694,11 @@ const ModalTerminal = ({
|
|||
rightSection={
|
||||
<CloseButton
|
||||
aria-label="Clear input"
|
||||
onClick={() => setInputTicket("")}
|
||||
onClick={() =>
|
||||
setDataTicket((pre) => ({ ...pre, description: "" }))
|
||||
}
|
||||
style={{
|
||||
display: inputTicket ? undefined : "none",
|
||||
display: dataTicket?.description ? undefined : "none",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
|
@ -691,12 +708,13 @@ const ModalTerminal = ({
|
|||
<Flex justify={"end"} mt={"4px"}>
|
||||
{dataTicket?.status === "closed" ? (
|
||||
<Button
|
||||
disabled={isDisableTicket}
|
||||
disabled={isDisableTicket || !dataTicket.description}
|
||||
mr={"8px"}
|
||||
fw={400}
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setDataTicket((pre) => ({ ...pre, description: "" }));
|
||||
handleCreate();
|
||||
setIsDisableTicket(true);
|
||||
setTimeout(() => {
|
||||
|
|
@ -708,7 +726,7 @@ const ModalTerminal = ({
|
|||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={isDisableTicket}
|
||||
disabled={isDisableTicket || !dataTicket.description}
|
||||
mr={"8px"}
|
||||
fw={400}
|
||||
variant="outline"
|
||||
|
|
@ -725,13 +743,18 @@ const ModalTerminal = ({
|
|||
</Button>
|
||||
)}
|
||||
<Button
|
||||
disabled={isDisableTicket || dataTicket?.status === "closed"}
|
||||
disabled={
|
||||
isDisableTicket ||
|
||||
dataTicket?.status === "closed" ||
|
||||
!dataTicket.description
|
||||
}
|
||||
mr={"8px"}
|
||||
fw={400}
|
||||
variant="outline"
|
||||
color="orange"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setDataTicket((pre) => ({ ...pre, description: "" }));
|
||||
handleUpdate("issue");
|
||||
setIsDisableTicket(true);
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ interface TerminalCLIProps {
|
|||
fontSize?: number;
|
||||
miniSize?: boolean;
|
||||
loadingContent?: boolean;
|
||||
focusTerminal?: boolean;
|
||||
}
|
||||
|
||||
const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||
|
|
@ -46,6 +47,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
|||
loadingContent = false,
|
||||
onFocus,
|
||||
onBlur,
|
||||
focusTerminal,
|
||||
}) => {
|
||||
const xtermRef = useRef<HTMLDivElement>(null);
|
||||
const terminal = useRef<Terminal>(null);
|
||||
|
|
@ -177,6 +179,12 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (focusTerminal && terminal.current) {
|
||||
terminal.current?.focus();
|
||||
}
|
||||
}, [focusTerminal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
@ -185,7 +193,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
|||
height: "100%",
|
||||
backgroundColor: "black",
|
||||
paddingBottom: customStyle.paddingBottom ?? "10px",
|
||||
maxHeight: customStyle.maxHeight ?? "73vh",
|
||||
maxHeight: customStyle.maxHeight ?? "70vh",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
|
@ -206,8 +214,8 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
|||
paddingLeft: customStyle.paddingLeft ?? "10px",
|
||||
paddingBottom: customStyle.paddingBottom ?? "10px",
|
||||
fontSize: customStyle.fontSize ?? "9px",
|
||||
maxHeight: customStyle.maxHeight ?? "73vh",
|
||||
height: customStyle.height ?? "73vh",
|
||||
maxHeight: customStyle.maxHeight ?? "70vh",
|
||||
height: customStyle.height ?? "70vh",
|
||||
padding: customStyle.padding ?? "4px",
|
||||
}}
|
||||
onDoubleClick={(event) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue