Update load License
This commit is contained in:
parent
a9dce43ab2
commit
38b6cb4981
|
|
@ -1,18 +1,51 @@
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
interface FileInfo {
|
||||||
|
name: string
|
||||||
|
fileSize: number
|
||||||
|
dateModify: number
|
||||||
|
}
|
||||||
|
|
||||||
export default class IosLicenseController {
|
export default class IosLicenseController {
|
||||||
/* ================= HELPER ================= */
|
/* ================= HELPER ================= */
|
||||||
|
|
||||||
private getBinFiles(dir: string): string[] {
|
private getBinFiles(dir: string): FileInfo[] {
|
||||||
if (!fs.existsSync(dir)) return []
|
if (!fs.existsSync(dir)) return []
|
||||||
|
|
||||||
return fs.readdirSync(dir).filter((file) => file.toLowerCase().endsWith('.bin'))
|
return fs
|
||||||
|
.readdirSync(dir)
|
||||||
|
.filter((file) => file.toLowerCase().endsWith('.bin'))
|
||||||
|
.map((file) => {
|
||||||
|
const fullPath = path.join(dir, file)
|
||||||
|
const stat = fs.statSync(fullPath)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: file,
|
||||||
|
fileSize: stat.size,
|
||||||
|
dateModify: stat.mtime.getTime(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) => b.dateModify - a.dateModify)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLicFiles(dir: string): string[] {
|
private getLicFiles(dir: string): FileInfo[] {
|
||||||
if (!fs.existsSync(dir)) return []
|
if (!fs.existsSync(dir)) return []
|
||||||
|
|
||||||
return fs.readdirSync(dir).filter((file) => file.toLowerCase().endsWith('.lic'))
|
return fs
|
||||||
|
.readdirSync(dir)
|
||||||
|
.filter((file) => file.toLowerCase().endsWith('.lic'))
|
||||||
|
.map((file) => {
|
||||||
|
const fullPath = path.join(dir, file)
|
||||||
|
const stat = fs.statSync(fullPath)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: file,
|
||||||
|
fileSize: stat.size,
|
||||||
|
dateModify: stat.mtime.getTime(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) => b.dateModify - a.dateModify)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= IOS ================= */
|
/* ================= IOS ================= */
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ export default class LineConnection {
|
||||||
private session: TestSession
|
private session: TestSession
|
||||||
public physicalTest: PhysicalPortTest
|
public physicalTest: PhysicalPortTest
|
||||||
private outputPhysicalTest: string
|
private outputPhysicalTest: string
|
||||||
|
private outputLoadIosLicense: string | boolean
|
||||||
private listDeviceIos: string[]
|
private listDeviceIos: string[]
|
||||||
|
|
||||||
constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) {
|
constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) {
|
||||||
|
|
@ -155,6 +156,7 @@ export default class LineConnection {
|
||||||
this.handleClearLine = handleClearLine
|
this.handleClearLine = handleClearLine
|
||||||
this.physicalTest = new PhysicalPortTest([])
|
this.physicalTest = new PhysicalPortTest([])
|
||||||
this.outputPhysicalTest = ''
|
this.outputPhysicalTest = ''
|
||||||
|
this.outputLoadIosLicense = ''
|
||||||
this.listDeviceIos = []
|
this.listDeviceIos = []
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,6 +202,10 @@ export default class LineConnection {
|
||||||
if (!this.config.inventory)
|
if (!this.config.inventory)
|
||||||
this.outputInventory = this.outputInventory.slice(-3000) + message
|
this.outputInventory = this.outputInventory.slice(-3000) + message
|
||||||
}
|
}
|
||||||
|
if (this.outputLoadIosLicense) {
|
||||||
|
if (this.outputLoadIosLicense === true) this.outputLoadIosLicense = ''
|
||||||
|
this.outputLoadIosLicense += message
|
||||||
|
}
|
||||||
if (this.config.runningPhysical) {
|
if (this.config.runningPhysical) {
|
||||||
this.outputPhysicalTest += message
|
this.outputPhysicalTest += message
|
||||||
const ports = this.physicalTest.handleLog(message)
|
const ports = this.physicalTest.handleLog(message)
|
||||||
|
|
@ -1130,6 +1136,7 @@ export default class LineConnection {
|
||||||
async loadIosRouter(nameIos: string, userName: string) {
|
async loadIosRouter(nameIos: string, userName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
this.outputLoadIosLicense = true
|
||||||
const network = station?.gateway || '172.25.1.1'
|
const network = station?.gateway || '172.25.1.1'
|
||||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||||
const [a, b] = network.split('.').map(Number)
|
const [a, b] = network.split('.').map(Number)
|
||||||
|
|
@ -1165,6 +1172,7 @@ export default class LineConnection {
|
||||||
async loadIosSwitch(nameIos: string, userName: string) {
|
async loadIosSwitch(nameIos: string, userName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
this.outputLoadIosLicense = true
|
||||||
const network = station?.gateway || '172.25.1.1'
|
const network = station?.gateway || '172.25.1.1'
|
||||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||||
const [a, b] = network.split('.').map(Number)
|
const [a, b] = network.split('.').map(Number)
|
||||||
|
|
@ -1214,8 +1222,14 @@ export default class LineConnection {
|
||||||
|
|
||||||
await sendMessageToMail(
|
await sendMessageToMail(
|
||||||
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load IOS Report`,
|
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load IOS Report`,
|
||||||
body
|
body +
|
||||||
|
`${`
|
||||||
|
<hr />
|
||||||
|
<p>Logs:</p>
|
||||||
|
<div style="white-space: break-spaces; background-color: #f5f5f5; color: black; padding: 8px; max-height: 500px; overflow-y: scroll; border: 1px #ccc solid;"><span style="color: black;">
|
||||||
|
${this.outputLoadIosLicense}</span></div>`}`
|
||||||
)
|
)
|
||||||
|
this.outputLoadIosLicense = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1237,8 +1251,14 @@ export default class LineConnection {
|
||||||
|
|
||||||
await sendMessageToMail(
|
await sendMessageToMail(
|
||||||
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load License Report`,
|
`[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load License Report`,
|
||||||
body
|
body +
|
||||||
|
`${`
|
||||||
|
<hr />
|
||||||
|
<p>Logs:</p>
|
||||||
|
<div style="white-space: break-spaces; background-color: #f5f5f5; color: black; padding: 8px; max-height: 500px; overflow-y: scroll; border: 1px #ccc solid;"><span style="color: black;">
|
||||||
|
${this.outputLoadIosLicense}</span></div>`}`
|
||||||
)
|
)
|
||||||
|
this.outputLoadIosLicense = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1352,7 +1372,7 @@ export default class LineConnection {
|
||||||
// console.log(`SKIP active IOS: ${ios}`)
|
// console.log(`SKIP active IOS: ${ios}`)
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
if (listIos?.includes(ios)) {
|
if (listIos?.map((value) => value.name)?.includes(ios)) {
|
||||||
console.log(`Already backed up: ${ios}`)
|
console.log(`Already backed up: ${ios}`)
|
||||||
if (ios !== nameIos) await this.deleteFileOnFlash(ios)
|
if (ios !== nameIos) await this.deleteFileOnFlash(ios)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1373,7 +1393,7 @@ export default class LineConnection {
|
||||||
async loadLicenseSwitch(licenseFileName: string, userName: string, portName: string) {
|
async loadLicenseSwitch(licenseFileName: string, userName: string, portName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
this.outputLoadIosLicense = true
|
||||||
// Setup network variables (giống hệt logic load IOS để đảm bảo thông mạng)
|
// Setup network variables (giống hệt logic load IOS để đảm bảo thông mạng)
|
||||||
const network = station?.gateway || '172.25.1.1'
|
const network = station?.gateway || '172.25.1.1'
|
||||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||||
|
|
@ -1413,7 +1433,7 @@ export default class LineConnection {
|
||||||
async loadLicenseRouter(licenseFileName: string, userName: string, portName: string) {
|
async loadLicenseRouter(licenseFileName: string, userName: string, portName: string) {
|
||||||
const station = await Station.find(this.config.stationId)
|
const station = await Station.find(this.config.stationId)
|
||||||
if (!station) return
|
if (!station) return
|
||||||
|
this.outputLoadIosLicense = true
|
||||||
const network = station?.gateway || '172.25.1.1'
|
const network = station?.gateway || '172.25.1.1'
|
||||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||||
const [a, b] = network.split('.').map(Number)
|
const [a, b] = network.split('.').map(Number)
|
||||||
|
|
|
||||||
|
|
@ -768,6 +768,13 @@ export function buildBody(
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: '',
|
note: '',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
expect: '#',
|
||||||
|
send: `show inventory`,
|
||||||
|
delay: '1',
|
||||||
|
repeat: '1',
|
||||||
|
note: '',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
expect: '#',
|
expect: '#',
|
||||||
send: `show license`,
|
send: `show license`,
|
||||||
|
|
@ -777,15 +784,8 @@ export function buildBody(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expect: '#',
|
expect: '#',
|
||||||
send: ` show inventory`,
|
send: ` show version`,
|
||||||
delay: '1',
|
delay: '3',
|
||||||
repeat: '1',
|
|
||||||
note: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expect: '#',
|
|
||||||
send: `show version`,
|
|
||||||
delay: '1',
|
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: '',
|
note: '',
|
||||||
},
|
},
|
||||||
|
|
@ -998,7 +998,7 @@ export function buildBody(
|
||||||
{
|
{
|
||||||
expect: '#',
|
expect: '#',
|
||||||
send: ` show version`,
|
send: ` show version`,
|
||||||
delay: '1',
|
delay: '3',
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: 'Verify version info',
|
note: 'Verify version info',
|
||||||
},
|
},
|
||||||
|
|
@ -1073,7 +1073,7 @@ export function buildBody(
|
||||||
{
|
{
|
||||||
expect: '',
|
expect: '',
|
||||||
send: ``,
|
send: ``,
|
||||||
delay: '1',
|
delay: '2',
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: '',
|
note: '',
|
||||||
},
|
},
|
||||||
|
|
@ -1159,7 +1159,7 @@ export function buildBody(
|
||||||
{
|
{
|
||||||
expect: '#',
|
expect: '#',
|
||||||
send: ` show version`,
|
send: ` show version`,
|
||||||
delay: '1',
|
delay: '3',
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: 'Verify version info',
|
note: 'Verify version info',
|
||||||
},
|
},
|
||||||
|
|
@ -1234,7 +1234,7 @@ export function buildBody(
|
||||||
{
|
{
|
||||||
expect: '',
|
expect: '',
|
||||||
send: ``,
|
send: ``,
|
||||||
delay: '1',
|
delay: '2',
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: '',
|
note: '',
|
||||||
},
|
},
|
||||||
|
|
@ -1273,6 +1273,13 @@ export function buildBody(
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: 'Reload router',
|
note: 'Reload router',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
expect: '',
|
||||||
|
send: `yes`,
|
||||||
|
delay: '1',
|
||||||
|
repeat: '1',
|
||||||
|
note: 'Confirm reload',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
expect: '',
|
expect: '',
|
||||||
send: ``,
|
send: ``,
|
||||||
|
|
@ -1320,7 +1327,7 @@ export function buildBody(
|
||||||
{
|
{
|
||||||
expect: '#',
|
expect: '#',
|
||||||
send: ` show version`,
|
send: ` show version`,
|
||||||
delay: '1',
|
delay: '3',
|
||||||
repeat: '1',
|
repeat: '1',
|
||||||
note: 'Verify version info',
|
note: 'Verify version info',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import {
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import type {
|
import type {
|
||||||
|
FileInfo,
|
||||||
IScenario,
|
IScenario,
|
||||||
ReceivedFile,
|
ReceivedFile,
|
||||||
ResponseData,
|
ResponseData,
|
||||||
|
|
@ -103,8 +104,8 @@ function App() {
|
||||||
const flushScheduledRef = useRef(false);
|
const flushScheduledRef = useRef(false);
|
||||||
const [listBrands, setListBrands] = useState<TBrands[]>([]);
|
const [listBrands, setListBrands] = useState<TBrands[]>([]);
|
||||||
const [listCategories, setListCategories] = useState<TCategories[]>([]);
|
const [listCategories, setListCategories] = useState<TCategories[]>([]);
|
||||||
const [listIos, setListIos] = useState<string[]>([]);
|
const [listIos, setListIos] = useState<FileInfo[]>([]);
|
||||||
const [listLicense, setListLicense] = useState<string[]>([]);
|
const [listLicense, setListLicense] = useState<FileInfo[]>([]);
|
||||||
|
|
||||||
const connectApcSwitch = (station: TStation) => {
|
const connectApcSwitch = (station: TStation) => {
|
||||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||||
|
|
@ -886,6 +887,8 @@ function App() {
|
||||||
scenarios={scenarios}
|
scenarios={scenarios}
|
||||||
listIos={listIos}
|
listIos={listIos}
|
||||||
listLicense={listLicense}
|
listLicense={listLicense}
|
||||||
|
getListIos={getListIos}
|
||||||
|
getListLicense={getListLicense}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <ModalConfirmRunScenario
|
{/* <ModalConfirmRunScenario
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Flex,
|
Flex,
|
||||||
|
Loader,
|
||||||
Modal,
|
Modal,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -10,9 +12,11 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
import type { TLine, TStation } from "../../untils/types";
|
import type { FileInfo, TLine, TStation } from "../../untils/types";
|
||||||
import { IconPlayerPlay, IconX } from "@tabler/icons-react";
|
import { IconPlayerPlay, IconRepeat, IconX } from "@tabler/icons-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import moment from "moment";
|
||||||
|
import { bytesToMB } from "../../untils/helper";
|
||||||
|
|
||||||
const ModalSelectIOS = ({
|
const ModalSelectIOS = ({
|
||||||
socket,
|
socket,
|
||||||
|
|
@ -21,16 +25,19 @@ const ModalSelectIOS = ({
|
||||||
opened,
|
opened,
|
||||||
close,
|
close,
|
||||||
line,
|
line,
|
||||||
|
getListIos,
|
||||||
}: {
|
}: {
|
||||||
socket: Socket | null;
|
socket: Socket | null;
|
||||||
station: TStation | undefined;
|
station: TStation | undefined;
|
||||||
listIos: string[];
|
listIos: FileInfo[];
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
line: TLine | undefined;
|
line: TLine | undefined;
|
||||||
|
getListIos: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [isReboot, setIsReboot] = useState<boolean>(true);
|
const [isReboot, setIsReboot] = useState<boolean>(true);
|
||||||
const [inputSearch, setInputSearch] = useState<string>("");
|
const [inputSearch, setInputSearch] = useState<string>("");
|
||||||
|
const [isDisable, setIsDisable] = useState<boolean>(false);
|
||||||
|
|
||||||
const filterIos = (type: string = "") => {
|
const filterIos = (type: string = "") => {
|
||||||
// Switch: Ưu tiên các dòng 4 chữ số cụ thể
|
// Switch: Ưu tiên các dòng 4 chữ số cụ thể
|
||||||
|
|
@ -43,16 +50,18 @@ const ModalSelectIOS = ({
|
||||||
/^(c8\d{2}|c18|c19|c28|c29(?!60)|c38(?!50)|c39|isr|asr)/i;
|
/^(c8\d{2}|c18|c19|c28|c29(?!60)|c38(?!50)|c39|isr|asr)/i;
|
||||||
|
|
||||||
return listIos
|
return listIos
|
||||||
.filter((name) => {
|
.filter((ios) => {
|
||||||
if (type === "switch") {
|
if (type === "switch") {
|
||||||
return switchRegex.test(name);
|
return switchRegex.test(ios.name);
|
||||||
}
|
}
|
||||||
if (type === "router") {
|
if (type === "router") {
|
||||||
return routerRegex.test(name);
|
return routerRegex.test(ios.name);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.filter((ios) => ios.toLowerCase().includes(inputSearch.toLowerCase()));
|
.filter((ios) =>
|
||||||
|
ios.name.toLowerCase().includes(inputSearch.toLowerCase())
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -65,11 +74,28 @@ const ModalSelectIOS = ({
|
||||||
setIsReboot(true);
|
setIsReboot(true);
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
|
<Flex gap={"xs"}>
|
||||||
<Text fz={"lg"} fw={"bolder"}>
|
<Text fz={"lg"} fw={"bolder"}>
|
||||||
Select IOS
|
Select IOS
|
||||||
</Text>
|
</Text>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
disabled={isDisable}
|
||||||
|
variant="filled"
|
||||||
|
color="yellow"
|
||||||
|
onClick={() => {
|
||||||
|
getListIos();
|
||||||
|
setIsDisable(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDisable(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconRepeat size={14} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
}
|
}
|
||||||
size="xl"
|
size="55%"
|
||||||
>
|
>
|
||||||
<Tabs defaultValue="router" h={"100%"}>
|
<Tabs defaultValue="router" h={"100%"}>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
|
|
@ -106,6 +132,19 @@ const ModalSelectIOS = ({
|
||||||
onChange={(event) => setIsReboot(event.currentTarget.checked)}
|
onChange={(event) => setIsReboot(event.currentTarget.checked)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{isDisable ? (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
height: "70vh",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Loader color="blue" />
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||||
<Table
|
<Table
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
|
@ -128,6 +167,24 @@ const ModalSelectIOS = ({
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
</Table.Th>
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "170px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Date
|
||||||
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "100px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Size
|
||||||
|
</Table.Th>
|
||||||
<Table.Th
|
<Table.Th
|
||||||
style={{
|
style={{
|
||||||
width: "200px",
|
width: "200px",
|
||||||
|
|
@ -142,7 +199,13 @@ const ModalSelectIOS = ({
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{filterIos("router")?.map((ios, i) => (
|
{filterIos("router")?.map((ios, i) => (
|
||||||
<Table.Tr key={i}>
|
<Table.Tr key={i}>
|
||||||
<Table.Td>{ios || ""}</Table.Td>
|
<Table.Td>{ios?.name || ""}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
{moment(ios?.dateModify).format(
|
||||||
|
"YYYY/MM/DD HH:mm:ss"
|
||||||
|
) || ""}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>{bytesToMB(ios?.fileSize) || "0"} MB</Table.Td>
|
||||||
<Table.Td
|
<Table.Td
|
||||||
style={{
|
style={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
|
|
@ -176,6 +239,7 @@ const ModalSelectIOS = ({
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
)}
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value="switch" w={"100%"}>
|
<Tabs.Panel value="switch" w={"100%"}>
|
||||||
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
||||||
|
|
@ -197,6 +261,19 @@ const ModalSelectIOS = ({
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{isDisable ? (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
height: "70vh",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Loader color="blue" />
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||||
<Table
|
<Table
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
|
@ -219,6 +296,24 @@ const ModalSelectIOS = ({
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
</Table.Th>
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "170px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Date
|
||||||
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "100px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Size
|
||||||
|
</Table.Th>
|
||||||
<Table.Th
|
<Table.Th
|
||||||
style={{
|
style={{
|
||||||
width: "200px",
|
width: "200px",
|
||||||
|
|
@ -233,7 +328,13 @@ const ModalSelectIOS = ({
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{filterIos("switch")?.map((ios, i) => (
|
{filterIos("switch")?.map((ios, i) => (
|
||||||
<Table.Tr key={i}>
|
<Table.Tr key={i}>
|
||||||
<Table.Td>{ios || ""}</Table.Td>
|
<Table.Td>{ios?.name || ""}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
{moment(ios?.dateModify).format(
|
||||||
|
"YYYY/MM/DD HH:mm:ss"
|
||||||
|
) || ""}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>{bytesToMB(ios?.fileSize) || "0"} MB</Table.Td>
|
||||||
<Table.Td
|
<Table.Td
|
||||||
style={{
|
style={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
|
|
@ -262,6 +363,7 @@ const ModalSelectIOS = ({
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
)}
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Flex,
|
Flex,
|
||||||
|
Loader,
|
||||||
Modal,
|
Modal,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -9,9 +11,11 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
import type { TLine, TStation } from "../../untils/types";
|
import type { FileInfo, TLine, TStation } from "../../untils/types";
|
||||||
import { IconPlayerPlay, IconX } from "@tabler/icons-react";
|
import { IconPlayerPlay, IconRepeat, IconX } from "@tabler/icons-react";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import moment from "moment";
|
||||||
|
import { bytesToKB } from "../../untils/helper";
|
||||||
|
|
||||||
const ModalSelectLicense = ({
|
const ModalSelectLicense = ({
|
||||||
socket,
|
socket,
|
||||||
|
|
@ -20,22 +24,33 @@ const ModalSelectLicense = ({
|
||||||
opened,
|
opened,
|
||||||
close,
|
close,
|
||||||
line,
|
line,
|
||||||
|
getListLicense,
|
||||||
}: {
|
}: {
|
||||||
socket: Socket | null;
|
socket: Socket | null;
|
||||||
station: TStation | undefined;
|
station: TStation | undefined;
|
||||||
listLicense: string[];
|
listLicense: FileInfo[];
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
line: TLine | undefined;
|
line: TLine | undefined;
|
||||||
|
getListLicense: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [inputSearch, setInputSearch] = useState<string>("");
|
const [inputSearch, setInputSearch] = useState<string>("");
|
||||||
const [inputPort, setInputPort] = useState<string>("GigabitEthernet0/0");
|
const [inputPort, setInputPort] = useState<string>("GigabitEthernet0/0");
|
||||||
const [licenseName, setLicenseName] = useState<string>("");
|
const [licenseName, setLicenseName] = useState<string>("");
|
||||||
const [modalConfirm, setModalConfirm] = useState<boolean>(false);
|
const [modalConfirm, setModalConfirm] = useState<boolean>(false);
|
||||||
|
const [isDisable, setIsDisable] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (opened) {
|
||||||
|
if (line?.inventory?.sn) setInputSearch(line?.inventory?.sn);
|
||||||
|
} else {
|
||||||
|
setInputSearch("");
|
||||||
|
}
|
||||||
|
}, [opened]);
|
||||||
|
|
||||||
const filterLicense = () => {
|
const filterLicense = () => {
|
||||||
return listLicense.filter((ios) =>
|
return listLicense.filter((lic) =>
|
||||||
ios.toLowerCase().includes(inputSearch.toLowerCase())
|
lic.name.toLowerCase().includes(inputSearch.toLowerCase())
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -48,11 +63,28 @@ const ModalSelectLicense = ({
|
||||||
setInputSearch("");
|
setInputSearch("");
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
|
<Flex gap={"xs"}>
|
||||||
<Text fz={"lg"} fw={"bolder"}>
|
<Text fz={"lg"} fw={"bolder"}>
|
||||||
Select License
|
Select License
|
||||||
</Text>
|
</Text>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
disabled={isDisable}
|
||||||
|
variant="filled"
|
||||||
|
color="yellow"
|
||||||
|
onClick={() => {
|
||||||
|
getListLicense();
|
||||||
|
setIsDisable(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDisable(false);
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconRepeat size={14} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
}
|
}
|
||||||
size="xl"
|
size="55%"
|
||||||
>
|
>
|
||||||
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
@ -73,6 +105,19 @@ const ModalSelectLicense = ({
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{isDisable ? (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
height: "70vh",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Loader color="blue" />
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||||
<Table
|
<Table
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
|
@ -95,6 +140,24 @@ const ModalSelectLicense = ({
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
</Table.Th>
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "170px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Date
|
||||||
|
</Table.Th>
|
||||||
|
<Table.Th
|
||||||
|
style={{
|
||||||
|
width: "100px",
|
||||||
|
textAlign: "center",
|
||||||
|
backgroundColor: "#94c6ff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Size
|
||||||
|
</Table.Th>
|
||||||
<Table.Th
|
<Table.Th
|
||||||
style={{
|
style={{
|
||||||
width: "200px",
|
width: "200px",
|
||||||
|
|
@ -109,7 +172,18 @@ const ModalSelectLicense = ({
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{filterLicense()?.map((lic, i) => (
|
{filterLicense()?.map((lic, i) => (
|
||||||
<Table.Tr key={i}>
|
<Table.Tr key={i}>
|
||||||
<Table.Td>{lic || ""}</Table.Td>
|
<Table.Td>{lic?.name || ""}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
{moment(lic?.dateModify).format("YYYY/MM/DD HH:mm:ss") ||
|
||||||
|
""}
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td
|
||||||
|
style={{
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{bytesToKB(lic?.fileSize) || "0"} KB
|
||||||
|
</Table.Td>
|
||||||
<Table.Td
|
<Table.Td
|
||||||
style={{
|
style={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
|
|
@ -128,7 +202,7 @@ const ModalSelectLicense = ({
|
||||||
// iosName: ios,
|
// iosName: ios,
|
||||||
// });
|
// });
|
||||||
// close();
|
// close();
|
||||||
setLicenseName(lic);
|
setLicenseName(lic?.name);
|
||||||
setModalConfirm(true);
|
setModalConfirm(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -140,6 +214,7 @@ const ModalSelectLicense = ({
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
)}
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
style={{ position: "absolute", left: 0 }}
|
style={{ position: "absolute", left: 0 }}
|
||||||
|
|
@ -190,6 +265,7 @@ const ModalSelectLicense = ({
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Panel value="router" w={"100%"}>
|
<Tabs.Panel value="router" w={"100%"}>
|
||||||
<Flex align={"center"} justify={"center"} p={"md"}>
|
<Flex align={"center"} justify={"center"} p={"md"}>
|
||||||
|
<Text me={"xs"}>Port Name:</Text>{" "}
|
||||||
<TextInput
|
<TextInput
|
||||||
style={{ width: "350px" }}
|
style={{ width: "350px" }}
|
||||||
placeholder="Port name"
|
placeholder="Port name"
|
||||||
|
|
@ -234,6 +310,7 @@ const ModalSelectLicense = ({
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
<Tabs.Panel value="switch" w={"100%"}>
|
<Tabs.Panel value="switch" w={"100%"}>
|
||||||
<Flex align={"center"} justify={"center"} p={"md"}>
|
<Flex align={"center"} justify={"center"} p={"md"}>
|
||||||
|
<Text me={"xs"}>Port Name:</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={{ width: "350px" }}
|
style={{ width: "350px" }}
|
||||||
placeholder="Port name"
|
placeholder="Port name"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import type {
|
import type {
|
||||||
|
FileInfo,
|
||||||
IScenario,
|
IScenario,
|
||||||
SwitchPortsProps,
|
SwitchPortsProps,
|
||||||
TDataTicket,
|
TDataTicket,
|
||||||
|
|
@ -66,6 +67,8 @@ const ModalTerminal = ({
|
||||||
selectedLines,
|
selectedLines,
|
||||||
listIos,
|
listIos,
|
||||||
listLicense,
|
listLicense,
|
||||||
|
getListLicense,
|
||||||
|
getListIos,
|
||||||
}: {
|
}: {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|
@ -74,8 +77,10 @@ const ModalTerminal = ({
|
||||||
stationItem: TStation | undefined;
|
stationItem: TStation | undefined;
|
||||||
scenarios: IScenario[];
|
scenarios: IScenario[];
|
||||||
selectedLines: TLine[];
|
selectedLines: TLine[];
|
||||||
listIos: string[];
|
listIos: FileInfo[];
|
||||||
listLicense: string[];
|
listLicense: FileInfo[];
|
||||||
|
getListLicense: () => void;
|
||||||
|
getListIos: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const user = useMemo(() => {
|
const user = useMemo(() => {
|
||||||
return localStorage.getItem("user") &&
|
return localStorage.getItem("user") &&
|
||||||
|
|
@ -1210,18 +1215,20 @@ const ModalTerminal = ({
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenSelectIos(true);
|
setOpenSelectIos(true);
|
||||||
|
getListIos();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select IOS
|
Select IOS
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={true}
|
disabled={isDisable}
|
||||||
fw={400}
|
fw={400}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="yellow"
|
color="yellow"
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenSelectLicense(true);
|
setOpenSelectLicense(true);
|
||||||
|
getListLicense();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select License
|
Select License
|
||||||
|
|
@ -1503,6 +1510,7 @@ const ModalTerminal = ({
|
||||||
station={stationItem}
|
station={stationItem}
|
||||||
listIos={listIos}
|
listIos={listIos}
|
||||||
line={line}
|
line={line}
|
||||||
|
getListIos={getListIos}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ModalSelectLicense
|
<ModalSelectLicense
|
||||||
|
|
@ -1514,6 +1522,7 @@ const ModalTerminal = ({
|
||||||
station={stationItem}
|
station={stationItem}
|
||||||
listLicense={listLicense}
|
listLicense={listLicense}
|
||||||
line={line}
|
line={line}
|
||||||
|
getListLicense={getListLicense}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -79,3 +79,13 @@ export const handleGetStorageCommand = (lineId: number): string[] => {
|
||||||
const history: Record<number, string[]> = storage ? JSON.parse(storage) : {};
|
const history: Record<number, string[]> = storage ? JSON.parse(storage) : {};
|
||||||
return history[lineId] || [];
|
return history[lineId] || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Function convert bytes to MB
|
||||||
|
export function bytesToMB(bytes: number, decimal = 2): number {
|
||||||
|
return Number((bytes / (1024 * 1024)).toFixed(decimal));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Function convert bytes to KB
|
||||||
|
export function bytesToKB(bytes: number, decimal = 2): number {
|
||||||
|
return Number((bytes / 1024).toFixed(decimal));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,3 +263,9 @@ export type TCategories = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FileInfo = {
|
||||||
|
name: string;
|
||||||
|
fileSize: number;
|
||||||
|
dateModify: string;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue