Update short log, print log
This commit is contained in:
parent
d4ea801bef
commit
e9c99814b2
|
|
@ -2183,7 +2183,7 @@ Ports Missing/Down: ${missing.length}\n\n`
|
||||||
const regex = new RegExp(`\\b${sn.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\b`, 'g')
|
const regex = new RegExp(`\\b${sn.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\b`, 'g')
|
||||||
result = result.replace(
|
result = result.replace(
|
||||||
regex,
|
regex,
|
||||||
`<span id="${escapeHtml(sn)}" style="background-color:#fbbf24;color:#78350f;font-weight:600;padding:2px 6px;border-radius:3px;cursor:pointer;" title="Click Hardware Inventory link to scroll">${escapeHtml(sn)}</span>`
|
`<span id="${sn}" style="background-color:#fbbf24;color:#78350f;font-weight:600;padding:2px 6px;border-radius:3px;cursor:pointer;" title="Click Hardware Inventory link to scroll">${escapeHtml(sn)}</span>`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@ const ModalLineHistory = ({
|
||||||
setOpenLog(false);
|
setOpenLog(false);
|
||||||
}}
|
}}
|
||||||
testLogContent={selectedHistory?.output || ""}
|
testLogContent={selectedHistory?.output || ""}
|
||||||
|
isShowShortLog={true}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,29 @@
|
||||||
import { Modal, Text } from "@mantine/core";
|
import { Button, Flex, Modal, Text } from "@mantine/core";
|
||||||
import classes from "../Component.module.css";
|
import classes from "../Component.module.css";
|
||||||
import { convertTimestampToDate } from "../../untils/helper";
|
import {
|
||||||
|
convertTimestampToDate,
|
||||||
|
createShortLog,
|
||||||
|
printLogWeb,
|
||||||
|
} from "../../untils/helper";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
const ModalLog = ({
|
const ModalLog = ({
|
||||||
opened,
|
opened,
|
||||||
onClose,
|
onClose,
|
||||||
testLogContent,
|
testLogContent,
|
||||||
|
isShowShortLog = false,
|
||||||
}: {
|
}: {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
testLogContent: string;
|
testLogContent: string;
|
||||||
|
isShowShortLog?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const [valueLog, setValueLog] = useState(testLogContent);
|
||||||
|
const [isShort, setIsShort] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValueLog(testLogContent);
|
||||||
|
}, [testLogContent]);
|
||||||
const addTooltipsToHighlights = () => {
|
const addTooltipsToHighlights = () => {
|
||||||
const highlights = document.querySelectorAll(".highlight");
|
const highlights = document.querySelectorAll(".highlight");
|
||||||
highlights.forEach((highlight) => {
|
highlights.forEach((highlight) => {
|
||||||
|
|
@ -60,7 +73,7 @@ const ModalLog = ({
|
||||||
size="90%"
|
size="90%"
|
||||||
styles={{
|
styles={{
|
||||||
content: {
|
content: {
|
||||||
height: "85vh",
|
height: isShowShortLog ? "90vh" : "85vh",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
},
|
},
|
||||||
|
|
@ -72,13 +85,50 @@ const ModalLog = ({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: highlightSystemLog(testLogContent),
|
__html: highlightSystemLog(valueLog),
|
||||||
}}
|
}}
|
||||||
className={`${classes.viewLog} ${classes.logLight}`}
|
className={`${classes.viewLog} ${classes.logLight}`}
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
if (el) addTooltipsToHighlights();
|
if (el) addTooltipsToHighlights();
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
{isShowShortLog ? (
|
||||||
|
<Flex justify="flex-end" mt="md" gap={"md"}>
|
||||||
|
{!isShort ? (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setValueLog(createShortLog(testLogContent));
|
||||||
|
setIsShort(true);
|
||||||
|
}}
|
||||||
|
color="green"
|
||||||
|
>
|
||||||
|
Short
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setValueLog(testLogContent);
|
||||||
|
setIsShort(false);
|
||||||
|
}}
|
||||||
|
color="green"
|
||||||
|
>
|
||||||
|
Original
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => printLogWeb(valueLog)}
|
||||||
|
color="blue"
|
||||||
|
>
|
||||||
|
Print
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -543,6 +543,7 @@ const ModalTerminal = ({
|
||||||
<Modal
|
<Modal
|
||||||
zIndex={95}
|
zIndex={95}
|
||||||
opened={opened}
|
opened={opened}
|
||||||
|
closeOnEscape={false}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
if (openDrawerScenario) {
|
if (openDrawerScenario) {
|
||||||
setOpenDrawerScenario(false);
|
setOpenDrawerScenario(false);
|
||||||
|
|
@ -642,7 +643,7 @@ const ModalTerminal = ({
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Stepper
|
<Stepper
|
||||||
w={600}
|
w={"35vw"}
|
||||||
size="xs"
|
size="xs"
|
||||||
color={isDisable ? "gray" : "cyan"}
|
color={isDisable ? "gray" : "cyan"}
|
||||||
active={activeStep}
|
active={activeStep}
|
||||||
|
|
@ -743,7 +744,20 @@ const ModalTerminal = ({
|
||||||
description={"Complete all to send report"}
|
description={"Complete all to send report"}
|
||||||
></Stepper.Step>
|
></Stepper.Step>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
<Flex></Flex>
|
<Flex justify={"flex-end"} w={"15vw"}>
|
||||||
|
<Button
|
||||||
|
fw={400}
|
||||||
|
disabled={isDisable}
|
||||||
|
variant="outline"
|
||||||
|
color="cyan"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => {
|
||||||
|
setOpenLineHistory(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View History
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -1570,18 +1584,6 @@ const ModalTerminal = ({
|
||||||
>
|
>
|
||||||
Send Break
|
Send Break
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
fw={400}
|
|
||||||
disabled={isDisable}
|
|
||||||
variant="outline"
|
|
||||||
color="cyan"
|
|
||||||
size="xs"
|
|
||||||
onClick={() => {
|
|
||||||
setOpenLineHistory(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
View History
|
|
||||||
</Button>
|
|
||||||
<Menu trigger="hover" withArrow shadow="md" position="top">
|
<Menu trigger="hover" withArrow shadow="md" position="top">
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -113,11 +113,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
// Handle Ctrl+V (Paste)
|
// Handle Ctrl+V (Paste)
|
||||||
if (e.ctrlKey && e.key.toLowerCase() === "v") return false;
|
if (e.ctrlKey && e.key.toLowerCase() === "v") return false;
|
||||||
// Handle Esc
|
// Handle Esc
|
||||||
if (e.key === "Escape") return false;
|
// if (e.key === "Escape") return false;
|
||||||
// Handle Enter
|
// Handle Enter
|
||||||
// if (e.key === "ArrowUp") handleArrowUp();
|
// if (e.key === "ArrowUp") handleArrowUp();
|
||||||
return true; // allow all other keys through
|
return true; // allow all other keys through
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleContextMenu = async (e: MouseEvent) => {
|
const handleContextMenu = async (e: MouseEvent) => {
|
||||||
|
|
@ -147,11 +147,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
"[CLEAR_TERMINAL_SCROLL_BACK]",
|
"[CLEAR_TERMINAL_SCROLL_BACK]",
|
||||||
Array(miniSize ? 20 : 70)
|
Array(miniSize ? 20 : 70)
|
||||||
.fill("\r\n")
|
.fill("\r\n")
|
||||||
.join("")
|
.join(""),
|
||||||
)
|
)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"[CONNECT_TO_SERVER_TFTP_FAIL]",
|
"[CONNECT_TO_SERVER_TFTP_FAIL]",
|
||||||
"\x1b[41;37m CONNECT TO SERVER TFTP FAIL \x1b[0m\n"
|
"\x1b[41;37m CONNECT TO SERVER TFTP FAIL \x1b[0m\n",
|
||||||
);
|
);
|
||||||
terminal.current?.write(valueContent);
|
terminal.current?.write(valueContent);
|
||||||
terminal.current?.scrollToBottom();
|
terminal.current?.scrollToBottom();
|
||||||
|
|
@ -182,11 +182,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
"[CLEAR_TERMINAL_SCROLL_BACK]",
|
"[CLEAR_TERMINAL_SCROLL_BACK]",
|
||||||
Array(miniSize ? 20 : 70)
|
Array(miniSize ? 20 : 70)
|
||||||
.fill("\r\n")
|
.fill("\r\n")
|
||||||
.join("")
|
.join(""),
|
||||||
)
|
)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"[CONNECT_TO_SERVER_TFTP_FAIL]",
|
"[CONNECT_TO_SERVER_TFTP_FAIL]",
|
||||||
"\x1b[41;37m CONNECT TO SERVER TFTP FAIL \x1b[0m\n"
|
"\x1b[41;37m CONNECT TO SERVER TFTP FAIL \x1b[0m\n",
|
||||||
);
|
);
|
||||||
terminal.current?.write(valueContent);
|
terminal.current?.write(valueContent);
|
||||||
setIsInit(true);
|
setIsInit(true);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export const convertTimestampToDate = (timestamp: number) => {
|
||||||
*/
|
*/
|
||||||
export const useDebounce = <T extends (...args: any[]) => void>(
|
export const useDebounce = <T extends (...args: any[]) => void>(
|
||||||
callback: T,
|
callback: T,
|
||||||
delay: number
|
delay: number,
|
||||||
) => {
|
) => {
|
||||||
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ export const useDebounce = <T extends (...args: any[]) => void>(
|
||||||
callback(...args);
|
callback(...args);
|
||||||
}, delay);
|
}, delay);
|
||||||
},
|
},
|
||||||
[callback, delay]
|
[callback, delay],
|
||||||
);
|
);
|
||||||
|
|
||||||
return debouncedFn;
|
return debouncedFn;
|
||||||
|
|
@ -199,7 +199,7 @@ export const bodyDPELP = [
|
||||||
|
|
||||||
export function convertFromKilobytesString(
|
export function convertFromKilobytesString(
|
||||||
input: string,
|
input: string,
|
||||||
decimals = 0
|
decimals = 0,
|
||||||
): string {
|
): string {
|
||||||
if (!input) return "0 KB";
|
if (!input) return "0 KB";
|
||||||
|
|
||||||
|
|
@ -242,3 +242,92 @@ export function convertFromKilobytesString(
|
||||||
|
|
||||||
return `${displayValue.toFixed(decimals)} ${units[unitIndex]}`;
|
return `${displayValue.toFixed(decimals)} ${units[unitIndex]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hàm tách và rút gọn log Cisco (show inv, show version, show license)
|
||||||
|
* @param {string} rawLog - Chuỗi log thô ban đầu (chứa toàn bộ kết quả terminal)
|
||||||
|
* @returns {string} Chuỗi log đã được rút gọn theo định dạng yêu cầu
|
||||||
|
*/
|
||||||
|
export function createShortLog(rawLog: string): string {
|
||||||
|
const shortLog = [];
|
||||||
|
|
||||||
|
// 1. Tách show inventory
|
||||||
|
const invRegex =
|
||||||
|
/(NAME:\s*"[^"]*",\s*DESCR:\s*"[^"]*"\r?\nPID:\s*[^,]+,\s*VID:\s*[^,]+,\s*SN:\s*\S+)/i;
|
||||||
|
const invMatch = rawLog.match(invRegex);
|
||||||
|
if (invMatch) {
|
||||||
|
shortLog.push(invMatch[1].trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Tách show version (Đã fix vụ bỏ phần thừa ở giữa)
|
||||||
|
const verRegex =
|
||||||
|
/(System image file is[^\r\n]+)[\s\S]*?(cisco\s+[a-zA-Z0-9\-]+\s+\([^)]+\)\s+processor[\s\S]*?Configuration register is 0x[0-9a-fA-F]+)/i;
|
||||||
|
const verMatch = rawLog.match(verRegex);
|
||||||
|
if (verMatch) {
|
||||||
|
shortLog.push(`${verMatch[1].trim()}\n${verMatch[2].trim()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Tách show license
|
||||||
|
const licRegex =
|
||||||
|
/(Index\s+1\s+Feature:[\s\S]*?)(?=\r?\n[a-zA-Z0-9\-\_]+[#>]|$)/i;
|
||||||
|
const licMatch = rawLog.match(licRegex);
|
||||||
|
if (licMatch) {
|
||||||
|
shortLog.push("\n" + licMatch[1].trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return shortLog.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hàm in log ra máy in vật lý trong môi trường Web (React/Vue/Vanilla)
|
||||||
|
* @param shortLog - Chuỗi log đã được rút gọn
|
||||||
|
*/
|
||||||
|
export function printLogWeb(shortLog: string): void {
|
||||||
|
if (!shortLog.trim()) {
|
||||||
|
console.warn("Không có dữ liệu để in.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mở một cửa sổ ẩn để in
|
||||||
|
const printWindow = window.open("", "_blank", "width=800,height=600");
|
||||||
|
if (!printWindow) {
|
||||||
|
console.error("Trình duyệt đã chặn popup. Vui lòng cho phép popup để in.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ghi nội dung HTML với font monospace (giống terminal)
|
||||||
|
printWindow.document.write(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Cisco Short Log</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap; /* Tự động xuống dòng nếu quá dài */
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
@page { margin: 1cm; }
|
||||||
|
body { padding: 0; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>${shortLog}</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
|
||||||
|
printWindow.document.close();
|
||||||
|
printWindow.focus();
|
||||||
|
|
||||||
|
// Gọi lệnh in và đóng cửa sổ sau khi in xong (hoặc hủy)
|
||||||
|
printWindow.print();
|
||||||
|
printWindow.onafterprint = () => printWindow.close();
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue