Enhance terminal clear options and update note handling

Added a 'Clear (scrollback)' option to the terminal modal, allowing users to clear only the scrollback buffer without emitting a clear event. Updated backend and helper logic to use 'testNotes' instead of 'notes' for serial number updates, and improved note formatting with timezone support. Also improved email content formatting and error reporting.
This commit is contained in:
nguyentrungthat 2025-12-24 14:27:52 +07:00
parent fd5d1628a5
commit 42f67e5390
5 changed files with 98 additions and 31 deletions

View File

@ -54,7 +54,7 @@ export default class HealCheckController {
serialNumberA: dataSN?.serialNumberA,
productModelId: dataSN?.productModelId,
orgId: dataSN?.orgId,
notes: dataSN?.notes,
testNotes: dataSN?.testNotes,
},
},
{
@ -88,6 +88,11 @@ export default class HealCheckController {
status: false,
message: error?.message || 'Checking api wiki fail',
},
{
name: 'update-note-sn',
status: false,
message: error?.message || 'Checking api update note SN fail',
},
],
}
}

View File

@ -24,6 +24,7 @@ import redis from '@adonisjs/redis/services/main'
import Line from '#models/line'
import { ErrorRow, TestResult } from '../ultils/types.js'
import moment from 'moment'
import momentTZ from 'moment-timezone'
type Inventory = {
pid: string
@ -872,13 +873,14 @@ export default class LineConnection {
.map(
(r) => `
<tr>
<td style="padding:6px;">
<td style="padding:6px; text-align:center;">
<span style="color:${r.level === 'FAIL' ? 'red' : 'orange'};">${r.level}</span>
</td>
<td style="padding:6px;">${r.rule}</td>
<td style="padding:6px; text-align:center;">${r.rule}</td>
<td style="padding:6px; text-align:center;">${r.message}</td>
<td style="padding:6px;font-family:monospace;">
${escapeHtml(r.log)}
<td style="padding:6px; font-family:monospace;">
<div style="white-space: break-spaces;"><span style="color: black;">
${escapeHtml(r.log.trim())}</span></div>
</td>
</tr>
`
@ -911,18 +913,15 @@ export default class LineConnection {
buildEmailContent(result: TestResult, value: any): string {
const rows = mapErrorsToRows(result.errors)
const table = this.renderErrorTable(rows)
const tableAI = this.renderAIDetectTable(value)
// const tableAI = this.renderAIDetectTable(value)
return `
<h3>Cisco Device Log Result</h3>
<p>Line: <b>${this.config.lineNumber}</b> - Station: <b>${this.config.stationName}</b></p>
<p><b>Summary:</b> ${result.summary}</p>
<p><b>Summary:</b> ${result.summary}. ${value.summary}</p>
<hr />
${table}
<br />
<hr />
<p>AI Detected:</p>
${tableAI}
`
}
@ -932,7 +931,9 @@ export default class LineConnection {
: data.license
? [data.license]
: []
const note = `\n\n-------ATC-${moment(new Date()).format('YYYY/MM/DD')}-------\nLicense: ${licenses.join(', ')}\nIssues:\n${data.issues?.length ? `- ` + data.issues.join(`\n- `) : ''}`
const timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm')
const note = `-------[ATC]-[${dataFormat}]-------\nLicense: ${licenses.join(', ')}\nSummary: ${data?.summary || ''}\nIssues:\n${data.issues?.length ? `- ` + data.issues.join(`\n- `) : ''}\n\n`
await updateNoteToERP(sn, note)
}
}

View File

@ -650,7 +650,7 @@ export async function updateNoteToERP(sn: string, note: string) {
serialNumberA: dataSN?.serialNumberA,
productModelId: dataSN?.productModelId,
orgId: dataSN?.orgId,
notes: (dataSN?.notes || '') + note,
testNotes: note + (dataSN?.testNotes || ''),
}
// console.log(payload)
await axios.post(

View File

@ -27,6 +27,7 @@ import TerminalCLI from "../TerminalXTerm";
import type { Socket } from "socket.io-client";
import { useEffect, useMemo, useState } from "react";
import {
IconCaretRight,
IconCircleCheckFilled,
IconCircleDot,
IconInfoCircle,
@ -82,6 +83,8 @@ const ModalTerminal = ({
const [valueBaud, setValueBaud] = useState<string>("");
const [valueIssue, setValueIssue] = useState<string>("");
const [dataTextfsm, setDataTextfsm] = useState<TextFSM[]>([]);
const [isClearKeepScrollBack, setIsClearKeepScrollBack] =
useState<boolean>(false);
useEffect(() => {
if (opened && line?.tickets && line?.tickets?.length > 0) {
@ -905,27 +908,65 @@ const ModalTerminal = ({
}
line_status={line?.status || ""}
loadingClearTerminal={line?.loadingClearTerminal}
isClearKeepScrollBack={isClearKeepScrollBack}
/>
<Flex justify={"space-around"} mt={"md"} pt={"md"} pb={"md"}>
<Button
fw={400}
disabled={isDisable}
variant="filled"
color="red"
size="xs"
onClick={() => {
socket?.emit("clear_terminal", {
lineId: line?.id,
stationId: stationItem?.id,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 1000);
}}
>
Clear Terminal
</Button>
<Menu trigger="hover" withArrow shadow="md" position="right">
<Menu.Target>
<Button
fw={400}
disabled={isDisable}
variant="filled"
color="red"
size="xs"
// style={{ height: "30px", width: "100px" }}
onClick={() => {}}
>
Clear Terminal <IconCaretRight size={"14px"} />
</Button>
</Menu.Target>
<Menu.Dropdown>
<Flex direction={"column"} gap={"8px"}>
<Button
fw={400}
disabled={isDisable}
variant="outline"
color="yellow"
size="xs"
onClick={() => {
setIsDisable(true);
setIsClearKeepScrollBack(true);
setTimeout(() => {
setIsDisable(false);
setIsClearKeepScrollBack(false);
}, 1000);
}}
>
Clear (scrollback)
</Button>
<Button
fw={400}
disabled={isDisable}
variant="outline"
color="red"
size="xs"
onClick={() => {
socket?.emit("clear_terminal", {
lineId: line?.id,
stationId: stationItem?.id,
});
setIsDisable(true);
setTimeout(() => {
setIsDisable(false);
}, 1000);
}}
>
Clear all
</Button>
</Flex>
</Menu.Dropdown>
</Menu>
<ButtonDPELP
socket={socket}
selectedLines={line ? [line] : []}

View File

@ -32,6 +32,7 @@ interface TerminalCLIProps {
focusTerminal?: boolean;
isSelected?: boolean;
loadingClearTerminal?: boolean;
isClearKeepScrollBack?: boolean;
}
const TerminalCLI: React.FC<TerminalCLIProps> = ({
@ -52,6 +53,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
focusTerminal,
isSelected,
loadingClearTerminal,
isClearKeepScrollBack,
}) => {
const xtermRef = useRef<HTMLDivElement>(null);
const terminal = useRef<Terminal>(null);
@ -75,6 +77,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
},
rows: 24,
cols: 80,
scrollback: 3000,
});
const fitAddon = new FitAddon();
fitRef.current = fitAddon;
@ -227,6 +230,23 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
}
}, [loadingClearTerminal]);
useEffect(() => {
if (isClearKeepScrollBack && terminal.current) {
// terminal.current?.clear();
terminal.current?.write(Array(70).fill("\r\n").join(""));
socket?.emit("write_command_line_from_web", {
lineIds: [line_id],
stationId: station_id,
command: "\r\n",
});
// setTimeout(() => {
// terminal.current?.scrollToLine(-100);
// terminal.current?.clear();
// }, 100);
if (!miniSize && !isDisabled) terminal.current?.focus();
}
}, [isClearKeepScrollBack]);
return (
<>
<div