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:
parent
fd5d1628a5
commit
42f67e5390
|
|
@ -54,7 +54,7 @@ export default class HealCheckController {
|
||||||
serialNumberA: dataSN?.serialNumberA,
|
serialNumberA: dataSN?.serialNumberA,
|
||||||
productModelId: dataSN?.productModelId,
|
productModelId: dataSN?.productModelId,
|
||||||
orgId: dataSN?.orgId,
|
orgId: dataSN?.orgId,
|
||||||
notes: dataSN?.notes,
|
testNotes: dataSN?.testNotes,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -88,6 +88,11 @@ export default class HealCheckController {
|
||||||
status: false,
|
status: false,
|
||||||
message: error?.message || 'Checking api wiki fail',
|
message: error?.message || 'Checking api wiki fail',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'update-note-sn',
|
||||||
|
status: false,
|
||||||
|
message: error?.message || 'Checking api update note SN fail',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import redis from '@adonisjs/redis/services/main'
|
||||||
import Line from '#models/line'
|
import Line from '#models/line'
|
||||||
import { ErrorRow, TestResult } from '../ultils/types.js'
|
import { ErrorRow, TestResult } from '../ultils/types.js'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import momentTZ from 'moment-timezone'
|
||||||
|
|
||||||
type Inventory = {
|
type Inventory = {
|
||||||
pid: string
|
pid: string
|
||||||
|
|
@ -872,13 +873,14 @@ export default class LineConnection {
|
||||||
.map(
|
.map(
|
||||||
(r) => `
|
(r) => `
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:6px;">
|
<td style="padding:6px; text-align:center;">
|
||||||
<span style="color:${r.level === 'FAIL' ? 'red' : 'orange'};">${r.level}</span>
|
<span style="color:${r.level === 'FAIL' ? 'red' : 'orange'};">${r.level}</span>
|
||||||
</td>
|
</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; text-align:center;">${r.message}</td>
|
||||||
<td style="padding:6px;font-family:monospace;">
|
<td style="padding:6px; font-family:monospace;">
|
||||||
${escapeHtml(r.log)}
|
<div style="white-space: break-spaces;"><span style="color: black;">
|
||||||
|
${escapeHtml(r.log.trim())}</span></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`
|
`
|
||||||
|
|
@ -911,18 +913,15 @@ export default class LineConnection {
|
||||||
buildEmailContent(result: TestResult, value: any): string {
|
buildEmailContent(result: TestResult, value: any): string {
|
||||||
const rows = mapErrorsToRows(result.errors)
|
const rows = mapErrorsToRows(result.errors)
|
||||||
const table = this.renderErrorTable(rows)
|
const table = this.renderErrorTable(rows)
|
||||||
const tableAI = this.renderAIDetectTable(value)
|
// const tableAI = this.renderAIDetectTable(value)
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<h3>Cisco Device Log Result</h3>
|
<h3>Cisco Device Log Result</h3>
|
||||||
<p>Line: <b>${this.config.lineNumber}</b> - Station: <b>${this.config.stationName}</b></p>
|
<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 />
|
<hr />
|
||||||
${table}
|
${table}
|
||||||
<br />
|
<br />
|
||||||
<hr />
|
|
||||||
<p>AI Detected:</p>
|
|
||||||
${tableAI}
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -932,7 +931,9 @@ export default class LineConnection {
|
||||||
: data.license
|
: data.license
|
||||||
? [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)
|
await updateNoteToERP(sn, note)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -650,7 +650,7 @@ export async function updateNoteToERP(sn: string, note: string) {
|
||||||
serialNumberA: dataSN?.serialNumberA,
|
serialNumberA: dataSN?.serialNumberA,
|
||||||
productModelId: dataSN?.productModelId,
|
productModelId: dataSN?.productModelId,
|
||||||
orgId: dataSN?.orgId,
|
orgId: dataSN?.orgId,
|
||||||
notes: (dataSN?.notes || '') + note,
|
testNotes: note + (dataSN?.testNotes || ''),
|
||||||
}
|
}
|
||||||
// console.log(payload)
|
// console.log(payload)
|
||||||
await axios.post(
|
await axios.post(
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import TerminalCLI from "../TerminalXTerm";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
IconCaretRight,
|
||||||
IconCircleCheckFilled,
|
IconCircleCheckFilled,
|
||||||
IconCircleDot,
|
IconCircleDot,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
|
|
@ -82,6 +83,8 @@ const ModalTerminal = ({
|
||||||
const [valueBaud, setValueBaud] = useState<string>("");
|
const [valueBaud, setValueBaud] = useState<string>("");
|
||||||
const [valueIssue, setValueIssue] = useState<string>("");
|
const [valueIssue, setValueIssue] = useState<string>("");
|
||||||
const [dataTextfsm, setDataTextfsm] = useState<TextFSM[]>([]);
|
const [dataTextfsm, setDataTextfsm] = useState<TextFSM[]>([]);
|
||||||
|
const [isClearKeepScrollBack, setIsClearKeepScrollBack] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (opened && line?.tickets && line?.tickets?.length > 0) {
|
if (opened && line?.tickets && line?.tickets?.length > 0) {
|
||||||
|
|
@ -905,27 +908,65 @@ const ModalTerminal = ({
|
||||||
}
|
}
|
||||||
line_status={line?.status || ""}
|
line_status={line?.status || ""}
|
||||||
loadingClearTerminal={line?.loadingClearTerminal}
|
loadingClearTerminal={line?.loadingClearTerminal}
|
||||||
|
isClearKeepScrollBack={isClearKeepScrollBack}
|
||||||
/>
|
/>
|
||||||
<Flex justify={"space-around"} mt={"md"} pt={"md"} pb={"md"}>
|
<Flex justify={"space-around"} mt={"md"} pt={"md"} pb={"md"}>
|
||||||
<Button
|
<Menu trigger="hover" withArrow shadow="md" position="right">
|
||||||
fw={400}
|
<Menu.Target>
|
||||||
disabled={isDisable}
|
<Button
|
||||||
variant="filled"
|
fw={400}
|
||||||
color="red"
|
disabled={isDisable}
|
||||||
size="xs"
|
variant="filled"
|
||||||
onClick={() => {
|
color="red"
|
||||||
socket?.emit("clear_terminal", {
|
size="xs"
|
||||||
lineId: line?.id,
|
// style={{ height: "30px", width: "100px" }}
|
||||||
stationId: stationItem?.id,
|
onClick={() => {}}
|
||||||
});
|
>
|
||||||
setIsDisable(true);
|
Clear Terminal <IconCaretRight size={"14px"} />
|
||||||
setTimeout(() => {
|
</Button>
|
||||||
setIsDisable(false);
|
</Menu.Target>
|
||||||
}, 1000);
|
<Menu.Dropdown>
|
||||||
}}
|
<Flex direction={"column"} gap={"8px"}>
|
||||||
>
|
<Button
|
||||||
Clear Terminal
|
fw={400}
|
||||||
</Button>
|
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
|
<ButtonDPELP
|
||||||
socket={socket}
|
socket={socket}
|
||||||
selectedLines={line ? [line] : []}
|
selectedLines={line ? [line] : []}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ interface TerminalCLIProps {
|
||||||
focusTerminal?: boolean;
|
focusTerminal?: boolean;
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
loadingClearTerminal?: boolean;
|
loadingClearTerminal?: boolean;
|
||||||
|
isClearKeepScrollBack?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
|
|
@ -52,6 +53,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
focusTerminal,
|
focusTerminal,
|
||||||
isSelected,
|
isSelected,
|
||||||
loadingClearTerminal,
|
loadingClearTerminal,
|
||||||
|
isClearKeepScrollBack,
|
||||||
}) => {
|
}) => {
|
||||||
const xtermRef = useRef<HTMLDivElement>(null);
|
const xtermRef = useRef<HTMLDivElement>(null);
|
||||||
const terminal = useRef<Terminal>(null);
|
const terminal = useRef<Terminal>(null);
|
||||||
|
|
@ -75,6 +77,7 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
},
|
},
|
||||||
rows: 24,
|
rows: 24,
|
||||||
cols: 80,
|
cols: 80,
|
||||||
|
scrollback: 3000,
|
||||||
});
|
});
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
fitRef.current = fitAddon;
|
fitRef.current = fitAddon;
|
||||||
|
|
@ -227,6 +230,23 @@ const TerminalCLI: React.FC<TerminalCLIProps> = ({
|
||||||
}
|
}
|
||||||
}, [loadingClearTerminal]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue