Enhance user tracking and UI adjustments across app

Backend now logs user information during command/script execution and adds retry logic for command sending. Frontend adjusts button/menu sizes, input widths, and scenario button font sizes for better UI consistency. ModalLog now highlights user log entries, and minor style tweaks are applied throughout.
This commit is contained in:
nguyentrungthat 2025-11-17 13:36:21 +07:00
parent abfae279da
commit c2d68c685c
8 changed files with 82 additions and 51 deletions

View File

@ -25,7 +25,7 @@ interface LineConfig {
openCLI: boolean
userEmailOpenCLI: string
userOpenCLI: string
inventory?: string
inventory: string
latestScenario?: {
name: string
time: number
@ -54,6 +54,7 @@ export default class LineConnection {
private outputInventory: string
private outputScenario: string
private bufferCommand: string
private retryConnect: number
constructor(config: LineConfig, socketIO: any) {
this.config = config
@ -66,6 +67,7 @@ export default class LineConnection {
this.outputInventory = ''
this.outputScenario = ''
this.bufferCommand = ''
this.retryConnect = 0
}
connect(timeoutMs = 5000) {
@ -187,27 +189,39 @@ export default class LineConnection {
})
}
writeCommand(cmd: string | Buffer<ArrayBuffer>, isWrite = false) {
async writeCommand(cmd: string | Buffer<ArrayBuffer>, userName = '') {
if (this.client.destroyed) {
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
if (this.retryConnect <= 3) {
await sleep(2000)
console.log('Retry connect times', this.retryConnect)
this.retryConnect += 1
await this.connect()
await this.writeCommand(cmd)
}
return
}
this.client.write(`${cmd}`)
if (isWrite) {
const command = cmd.toString()
for (const char of command) {
if (char === '\x7F') this.bufferCommand = this.bufferCommand.slice(0, -1)
else if (char === '\r' && cleanData(this.bufferCommand).length > 0) {
this.config.commands = [
cleanData(this.bufferCommand.replace('\r', '')),
...this.config.commands.filter(
(el) => el !== cleanData(this.bufferCommand.replace('\r', ''))
),
].slice(0, 10)
this.bufferCommand = ''
} else this.bufferCommand += char
}
if (userName) {
// appendLog(
// `\n---${userName}---\n`,
// this.config.stationId,
// this.config.lineNumber,
// this.config.port
// )
// for (const char of command) {
// if (char === '\x7F') this.bufferCommand = this.bufferCommand.slice(0, -1)
// else if (char === '\r' && cleanData(this.bufferCommand).length > 0) {
// this.config.commands = [
// cleanData(this.bufferCommand.replace('\r', '')),
// ...this.config.commands.filter(
// (el) => el !== cleanData(this.bufferCommand.replace('\r', ''))
// ),
// ].slice(0, 10)
// this.bufferCommand = ''
// } else this.bufferCommand += char
// }
}
}
@ -225,7 +239,7 @@ export default class LineConnection {
}
}
async runScript(script: Scenario) {
async runScript(script: Scenario, userName: string) {
if (!this.client || this.client.destroyed) {
console.log('Not connected')
this.isRunningScript = false
@ -239,9 +253,9 @@ export default class LineConnection {
this.isRunningScript = true
const now = Date.now()
this.outputScenario += `\n\n---start-scenarios---${now}---\n---scenario---${script?.title}---${now}---\n`
this.outputScenario += `\n\n---start-scenarios---${now}---${userName}---\n---scenario---${script?.title}---${now}---\n`
appendLog(
`\n\n---start-scenarios---${now}---\n---scenario---${script?.title}---${now}---\n`,
`\n\n---start-scenarios---${now}---${userName}---\n---scenario---${script?.title}---${now}---\n`,
this.config.stationId,
this.config.lineNumber,
this.config.port
@ -264,9 +278,9 @@ export default class LineConnection {
lineId: this.config.id,
data: 'Timeout run scenario',
})
this.outputScenario += `\n---end-scenarios---${now}---\n`
this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n`
appendLog(
`\n---end-scenarios---${now}---\n`,
`\n---end-scenarios---${now}---${userName}---\n`,
this.config.stationId,
this.config.lineNumber,
this.config.port
@ -285,9 +299,9 @@ export default class LineConnection {
} else clearTimeout(timeoutTimer)
this.isRunningScript = false
this.outputBuffer = ''
this.outputScenario += `\n---end-scenarios---${now}---\n`
this.outputScenario += `\n---end-scenarios---${now}---${userName}---\n`
appendLog(
`\n---end-scenarios---${now}---\n`,
`\n---end-scenarios---${now}---${userName}---\n`,
this.config.stationId,
this.config.lineNumber,
this.config.port
@ -379,6 +393,12 @@ export default class LineConnection {
userEmailOpenCLI: user.userEmail,
userOpenCLI: user.userName,
})
appendLog(
`\n-------${user.userName}-------\n`,
this.config.stationId,
this.config.lineNumber,
this.config.port
)
}
userCloseCLI() {

View File

@ -147,7 +147,7 @@ export class WebSocketIo {
stationId,
lineIds,
async (line) =>
command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, true),
command === 'spam_break' ? line.breakSpam() : line.writeCommand(command, userName),
{ command, timeout: 120000 }
)
})
@ -159,7 +159,7 @@ export class WebSocketIo {
io,
data.stationId,
[lineId],
async (line) => line.runScript(scenario),
async (line) => line.runScript(scenario, userName),
{
scenario,
timeout: scenario?.timeout ? Number(scenario.timeout) + 120000 : 300000,
@ -472,7 +472,7 @@ export class WebSocketIo {
lines: Line[],
station: Station,
output = '',
commands: string[] = []
inventory: string = ''
) {
try {
for (const line of lines) {
@ -491,7 +491,8 @@ export class WebSocketIo {
userEmailOpenCLI: '',
userOpenCLI: '',
data: [],
commands: commands,
commands: [],
inventory: inventory,
},
socket
)
@ -531,7 +532,7 @@ export class WebSocketIo {
}
/**
* Hàm xử chung cho mọi action (writeCommand, runScript, v.v.)
* Hàm xử chung cho mọi action (write command, runScript, v.v.)
*/
async handleLineOperation(
io: CustomServer,
@ -562,7 +563,7 @@ export class WebSocketIo {
[linesData],
stationData,
line?.config?.output || '',
line?.config?.commands || []
line?.config?.inventory || ''
)
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)

View File

@ -2,9 +2,9 @@
/* background-color: red; */
}
body {
/* body {
font-family: "Mulish", sans-serif;
}
} */
.list {
position: relative;

View File

@ -172,7 +172,7 @@ const BottomToolBar = ({
<Box>
<Input
style={{
width: "600px",
width: "30vw",
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
}}
placeholder={"Send command to port(s)"}

View File

@ -80,7 +80,7 @@ export const ButtonDPELP = ({
},
{
expect: "",
send: "show env",
send: "show env all",
delay: "3000",
repeat: "1",
note: "",
@ -133,18 +133,20 @@ export const ButtonScenario = ({
onClick,
selectedLines,
scenario,
fontSize = "12px",
}: {
socket: Socket | null;
isDisable: boolean;
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
selectedLines: TLine[];
scenario: IScenario;
fontSize?: string;
}) => {
return (
<Button
disabled={isDisable}
miw={"100px"}
style={{ minHeight: "28px", height: "auto" }}
style={{ minHeight: "28px", height: "auto", fontSize: fontSize }}
mr={"5px"}
variant="outline"
color="#00a164"

View File

@ -274,7 +274,7 @@ const CardLine = ({
</Flex>
</Flex>
</Menu.Target>
<Menu.Dropdown style={{ width: "90px", backgroundColor: "#2d2d2d" }}>
<Menu.Dropdown style={{ width: "80px", backgroundColor: "#2d2d2d" }}>
<Flex
justify={"space-between"}
direction={"column"}
@ -363,6 +363,7 @@ const CardLine = ({
}, 5000);
}}
scenario={el}
fontSize="9px"
/>
))}
</Flex>
@ -386,7 +387,7 @@ const CardLine = ({
BAUD <IconCaretRight size={"14px"} />
</Button>
</Menu.Target>
<Menu.Dropdown style={{ width: "100px" }}>
<Menu.Dropdown style={{ width: "90px" }}>
<Flex
justify={"space-between"}
direction={"column"}

View File

@ -130,9 +130,9 @@
}
.buttonMenuTool {
font-size: 11px !important;
width: 80px !important;
min-width: 80px !important;
font-size: 9px !important;
width: 70px !important;
min-width: 70px !important;
height: 30px !important;
padding-right: 4px !important;
padding-left: 4px !important;

View File

@ -23,18 +23,25 @@ const ModalLog = ({
const highlightSystemLog = (logText: string): string => {
const colorStart = "#7fffd4";
const colorEnd = "#ffa589";
return logText
.replace(/---scenario---.*\n?/g, "") // Remove lines with ---scenario---
.replace(/\n?---send-command---.*\n?/g, "") // Remove lines with ---send-command---
.replace(
/^(---start-testing---|---end-testing---|---start-scenarios---|---end-scenarios---)(\d+)(---.*)?$/gm,
(_, prefix, timestamp, suffix = "") => {
const date = convertTimestampToDate(timestamp);
return `<span style="background-color: ${
prefix.includes("start") ? colorStart : colorEnd
}" title="${date}">${prefix}${timestamp}${suffix}</span>`;
}
);
const colorUser = "#f1ef5e";
return (
logText
.replace(/---scenario---.*\n?/g, "") // Remove lines with ---scenario---
.replace(/\n?---send-command---.*\n?/g, "") // Remove lines with ---send-command---
.replace(
/^(---start-testing---|---end-testing---|---start-scenarios---|---end-scenarios---)(\d+)(---.*)?$/gm,
(_, prefix, timestamp, suffix = "") => {
const date = convertTimestampToDate(timestamp);
return `<span style="background-color: ${
prefix.includes("start") ? colorStart : colorEnd
}" title="${date}">${prefix}${timestamp}${suffix}</span>`;
}
)
// Highlight full ---User---
.replace(/^-------([^-\n]+)-------$/gm, (match) => {
return `<span style="background-color: ${colorUser}" title="User">${match}</span>`;
})
);
};
return (