Update short log, print log

This commit is contained in:
andrew.ng 2026-05-20 10:46:34 +07:00
parent d4ea801bef
commit e9c99814b2
6 changed files with 170 additions and 28 deletions

View File

@ -2183,7 +2183,7 @@ Ports Missing/Down: ${missing.length}\n\n`
const regex = new RegExp(`\\b${sn.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\b`, 'g')
result = result.replace(
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>`
)
}
})

View File

@ -241,6 +241,7 @@ const ModalLineHistory = ({
setOpenLog(false);
}}
testLogContent={selectedHistory?.output || ""}
isShowShortLog={true}
/>
</>
);

View File

@ -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 { convertTimestampToDate } from "../../untils/helper";
import {
convertTimestampToDate,
createShortLog,
printLogWeb,
} from "../../untils/helper";
import { useEffect, useState } from "react";
const ModalLog = ({
opened,
onClose,
testLogContent,
isShowShortLog = false,
}: {
opened: boolean;
onClose: () => void;
testLogContent: string;
isShowShortLog?: boolean;
}) => {
const [valueLog, setValueLog] = useState(testLogContent);
const [isShort, setIsShort] = useState(false);
useEffect(() => {
setValueLog(testLogContent);
}, [testLogContent]);
const addTooltipsToHighlights = () => {
const highlights = document.querySelectorAll(".highlight");
highlights.forEach((highlight) => {
@ -60,7 +73,7 @@ const ModalLog = ({
size="90%"
styles={{
content: {
height: "85vh",
height: isShowShortLog ? "90vh" : "85vh",
display: "flex",
flexDirection: "column",
},
@ -72,13 +85,50 @@ const ModalLog = ({
>
<div
dangerouslySetInnerHTML={{
__html: highlightSystemLog(testLogContent),
__html: highlightSystemLog(valueLog),
}}
className={`${classes.viewLog} ${classes.logLight}`}
ref={(el) => {
if (el) addTooltipsToHighlights();
}}
></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>
);
};

View File

@ -543,6 +543,7 @@ const ModalTerminal = ({
<Modal
zIndex={95}
opened={opened}
closeOnEscape={false}
onClose={() => {
if (openDrawerScenario) {
setOpenDrawerScenario(false);
@ -642,7 +643,7 @@ const ModalTerminal = ({
</Flex>
</Flex>
<Stepper
w={600}
w={"35vw"}
size="xs"
color={isDisable ? "gray" : "cyan"}
active={activeStep}
@ -743,7 +744,20 @@ const ModalTerminal = ({
description={"Complete all to send report"}
></Stepper.Step>
</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>
}
>
@ -1570,18 +1584,6 @@ const ModalTerminal = ({
>
Send Break
</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.Target>
<Button

View File

@ -113,11 +113,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
// Handle Ctrl+V (Paste)
if (e.ctrlKey && e.key.toLowerCase() === "v") return false;
// Handle Esc
if (e.key === "Escape") return false;
// if (e.key === "Escape") return false;
// Handle Enter
// if (e.key === "ArrowUp") handleArrowUp();
return true; // allow all other keys through
}
},
);
const handleContextMenu = async (e: MouseEvent) => {
@ -147,11 +147,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
"[CLEAR_TERMINAL_SCROLL_BACK]",
Array(miniSize ? 20 : 70)
.fill("\r\n")
.join("")
.join(""),
)
.replaceAll(
"[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?.scrollToBottom();
@ -182,11 +182,11 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
"[CLEAR_TERMINAL_SCROLL_BACK]",
Array(miniSize ? 20 : 70)
.fill("\r\n")
.join("")
.join(""),
)
.replaceAll(
"[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);
setIsInit(true);

View File

@ -43,7 +43,7 @@ export const convertTimestampToDate = (timestamp: number) => {
*/
export const useDebounce = <T extends (...args: any[]) => void>(
callback: T,
delay: number
delay: number,
) => {
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
@ -57,7 +57,7 @@ export const useDebounce = <T extends (...args: any[]) => void>(
callback(...args);
}, delay);
},
[callback, delay]
[callback, delay],
);
return debouncedFn;
@ -199,7 +199,7 @@ export const bodyDPELP = [
export function convertFromKilobytesString(
input: string,
decimals = 0
decimals = 0,
): string {
if (!input) return "0 KB";
@ -242,3 +242,92 @@ export function convertFromKilobytesString(
return `${displayValue.toFixed(decimals)} ${units[unitIndex]}`;
}
/**
* Hàm tách 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 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();
}