From 42f67e5390d66d42f30966e9573e6729193cae34 Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:27:52 +0700 Subject: [PATCH] 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. --- .../app/controllers/healcheck_controller.ts | 7 +- BACKEND/app/services/line_connection.ts | 21 ++--- BACKEND/app/ultils/helper.ts | 2 +- .../src/components/Modal/ModalTerminal.tsx | 79 ++++++++++++++----- FRONTEND/src/components/TerminalXTerm.tsx | 20 +++++ 5 files changed, 98 insertions(+), 31 deletions(-) diff --git a/BACKEND/app/controllers/healcheck_controller.ts b/BACKEND/app/controllers/healcheck_controller.ts index 4a95506..14a4ac6 100644 --- a/BACKEND/app/controllers/healcheck_controller.ts +++ b/BACKEND/app/controllers/healcheck_controller.ts @@ -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', + }, ], } } diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index 1f1fe62..35e3ea3 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -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) => ` - + ${r.level} - ${r.rule} + ${r.rule} ${r.message} - - ${escapeHtml(r.log)} + +
+ ${escapeHtml(r.log.trim())}
` @@ -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 `

Cisco Device Log Result

Line: ${this.config.lineNumber} - Station: ${this.config.stationName}

-

Summary: ${result.summary}

+

Summary: ${result.summary}. ${value.summary}


${table}
-
-

AI Detected:

- ${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) } } diff --git a/BACKEND/app/ultils/helper.ts b/BACKEND/app/ultils/helper.ts index 598a8da..5473326 100644 --- a/BACKEND/app/ultils/helper.ts +++ b/BACKEND/app/ultils/helper.ts @@ -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( diff --git a/FRONTEND/src/components/Modal/ModalTerminal.tsx b/FRONTEND/src/components/Modal/ModalTerminal.tsx index 3739784..6353068 100644 --- a/FRONTEND/src/components/Modal/ModalTerminal.tsx +++ b/FRONTEND/src/components/Modal/ModalTerminal.tsx @@ -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(""); const [valueIssue, setValueIssue] = useState(""); const [dataTextfsm, setDataTextfsm] = useState([]); + const [isClearKeepScrollBack, setIsClearKeepScrollBack] = + useState(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} /> - + + + + + + + + + + + + = ({ @@ -52,6 +53,7 @@ const TerminalCLI: React.FC = ({ focusTerminal, isSelected, loadingClearTerminal, + isClearKeepScrollBack, }) => { const xtermRef = useRef(null); const terminal = useRef(null); @@ -75,6 +77,7 @@ const TerminalCLI: React.FC = ({ }, rows: 24, cols: 80, + scrollback: 3000, }); const fitAddon = new FitAddon(); fitRef.current = fitAddon; @@ -227,6 +230,23 @@ const TerminalCLI: React.FC = ({ } }, [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 ( <>