Update load License
This commit is contained in:
parent
a9dce43ab2
commit
38b6cb4981
|
|
@ -1,18 +1,51 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
interface FileInfo {
|
||||
name: string
|
||||
fileSize: number
|
||||
dateModify: number
|
||||
}
|
||||
|
||||
export default class IosLicenseController {
|
||||
/* ================= HELPER ================= */
|
||||
|
||||
private getBinFiles(dir: string): string[] {
|
||||
private getBinFiles(dir: string): FileInfo[] {
|
||||
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 []
|
||||
|
||||
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 ================= */
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ export default class LineConnection {
|
|||
private session: TestSession
|
||||
public physicalTest: PhysicalPortTest
|
||||
private outputPhysicalTest: string
|
||||
private outputLoadIosLicense: string | boolean
|
||||
private listDeviceIos: string[]
|
||||
|
||||
constructor(config: LineConfig, socketIO: any, handleClearLine: () => void) {
|
||||
|
|
@ -155,6 +156,7 @@ export default class LineConnection {
|
|||
this.handleClearLine = handleClearLine
|
||||
this.physicalTest = new PhysicalPortTest([])
|
||||
this.outputPhysicalTest = ''
|
||||
this.outputLoadIosLicense = ''
|
||||
this.listDeviceIos = []
|
||||
}
|
||||
/**
|
||||
|
|
@ -200,6 +202,10 @@ export default class LineConnection {
|
|||
if (!this.config.inventory)
|
||||
this.outputInventory = this.outputInventory.slice(-3000) + message
|
||||
}
|
||||
if (this.outputLoadIosLicense) {
|
||||
if (this.outputLoadIosLicense === true) this.outputLoadIosLicense = ''
|
||||
this.outputLoadIosLicense += message
|
||||
}
|
||||
if (this.config.runningPhysical) {
|
||||
this.outputPhysicalTest += message
|
||||
const ports = this.physicalTest.handleLog(message)
|
||||
|
|
@ -1130,6 +1136,7 @@ export default class LineConnection {
|
|||
async loadIosRouter(nameIos: string, userName: string) {
|
||||
const station = await Station.find(this.config.stationId)
|
||||
if (!station) return
|
||||
this.outputLoadIosLicense = true
|
||||
const network = station?.gateway || '172.25.1.1'
|
||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||
const [a, b] = network.split('.').map(Number)
|
||||
|
|
@ -1165,6 +1172,7 @@ export default class LineConnection {
|
|||
async loadIosSwitch(nameIos: string, userName: string) {
|
||||
const station = await Station.find(this.config.stationId)
|
||||
if (!station) return
|
||||
this.outputLoadIosLicense = true
|
||||
const network = station?.gateway || '172.25.1.1'
|
||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||
const [a, b] = network.split('.').map(Number)
|
||||
|
|
@ -1214,8 +1222,14 @@ export default class LineConnection {
|
|||
|
||||
await sendMessageToMail(
|
||||
`[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(
|
||||
`[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}`)
|
||||
// continue
|
||||
// }
|
||||
if (listIos?.includes(ios)) {
|
||||
if (listIos?.map((value) => value.name)?.includes(ios)) {
|
||||
console.log(`Already backed up: ${ios}`)
|
||||
if (ios !== nameIos) await this.deleteFileOnFlash(ios)
|
||||
} else {
|
||||
|
|
@ -1373,7 +1393,7 @@ export default class LineConnection {
|
|||
async loadLicenseSwitch(licenseFileName: string, userName: string, portName: string) {
|
||||
const station = await Station.find(this.config.stationId)
|
||||
if (!station) return
|
||||
|
||||
this.outputLoadIosLicense = true
|
||||
// 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 tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||
|
|
@ -1413,7 +1433,7 @@ export default class LineConnection {
|
|||
async loadLicenseRouter(licenseFileName: string, userName: string, portName: string) {
|
||||
const station = await Station.find(this.config.stationId)
|
||||
if (!station) return
|
||||
|
||||
this.outputLoadIosLicense = true
|
||||
const network = station?.gateway || '172.25.1.1'
|
||||
const tftpIp = station?.tftp_ip || '172.16.7.69'
|
||||
const [a, b] = network.split('.').map(Number)
|
||||
|
|
|
|||
|
|
@ -768,6 +768,13 @@ export function buildBody(
|
|||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
{
|
||||
expect: '#',
|
||||
send: `show inventory`,
|
||||
delay: '1',
|
||||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
{
|
||||
expect: '#',
|
||||
send: `show license`,
|
||||
|
|
@ -777,15 +784,8 @@ export function buildBody(
|
|||
},
|
||||
{
|
||||
expect: '#',
|
||||
send: ` show inventory`,
|
||||
delay: '1',
|
||||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
{
|
||||
expect: '#',
|
||||
send: `show version`,
|
||||
delay: '1',
|
||||
send: ` show version`,
|
||||
delay: '3',
|
||||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
|
|
@ -998,7 +998,7 @@ export function buildBody(
|
|||
{
|
||||
expect: '#',
|
||||
send: ` show version`,
|
||||
delay: '1',
|
||||
delay: '3',
|
||||
repeat: '1',
|
||||
note: 'Verify version info',
|
||||
},
|
||||
|
|
@ -1073,7 +1073,7 @@ export function buildBody(
|
|||
{
|
||||
expect: '',
|
||||
send: ``,
|
||||
delay: '1',
|
||||
delay: '2',
|
||||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
|
|
@ -1159,7 +1159,7 @@ export function buildBody(
|
|||
{
|
||||
expect: '#',
|
||||
send: ` show version`,
|
||||
delay: '1',
|
||||
delay: '3',
|
||||
repeat: '1',
|
||||
note: 'Verify version info',
|
||||
},
|
||||
|
|
@ -1234,7 +1234,7 @@ export function buildBody(
|
|||
{
|
||||
expect: '',
|
||||
send: ``,
|
||||
delay: '1',
|
||||
delay: '2',
|
||||
repeat: '1',
|
||||
note: '',
|
||||
},
|
||||
|
|
@ -1273,6 +1273,13 @@ export function buildBody(
|
|||
repeat: '1',
|
||||
note: 'Reload router',
|
||||
},
|
||||
{
|
||||
expect: '',
|
||||
send: `yes`,
|
||||
delay: '1',
|
||||
repeat: '1',
|
||||
note: 'Confirm reload',
|
||||
},
|
||||
{
|
||||
expect: '',
|
||||
send: ``,
|
||||
|
|
@ -1320,7 +1327,7 @@ export function buildBody(
|
|||
{
|
||||
expect: '#',
|
||||
send: ` show version`,
|
||||
delay: '1',
|
||||
delay: '3',
|
||||
repeat: '1',
|
||||
note: 'Verify version info',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
LoadingOverlay,
|
||||
} from "@mantine/core";
|
||||
import type {
|
||||
FileInfo,
|
||||
IScenario,
|
||||
ReceivedFile,
|
||||
ResponseData,
|
||||
|
|
@ -103,8 +104,8 @@ function App() {
|
|||
const flushScheduledRef = useRef(false);
|
||||
const [listBrands, setListBrands] = useState<TBrands[]>([]);
|
||||
const [listCategories, setListCategories] = useState<TCategories[]>([]);
|
||||
const [listIos, setListIos] = useState<string[]>([]);
|
||||
const [listLicense, setListLicense] = useState<string[]>([]);
|
||||
const [listIos, setListIos] = useState<FileInfo[]>([]);
|
||||
const [listLicense, setListLicense] = useState<FileInfo[]>([]);
|
||||
|
||||
const connectApcSwitch = (station: TStation) => {
|
||||
if (station?.apc_1_ip && station?.apc_1_port) {
|
||||
|
|
@ -886,6 +887,8 @@ function App() {
|
|||
scenarios={scenarios}
|
||||
listIos={listIos}
|
||||
listLicense={listLicense}
|
||||
getListIos={getListIos}
|
||||
getListLicense={getListLicense}
|
||||
/>
|
||||
|
||||
{/* <ModalConfirmRunScenario
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Loader,
|
||||
Modal,
|
||||
ScrollArea,
|
||||
Table,
|
||||
|
|
@ -10,9 +12,11 @@ import {
|
|||
TextInput,
|
||||
} from "@mantine/core";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import type { TLine, TStation } from "../../untils/types";
|
||||
import { IconPlayerPlay, IconX } from "@tabler/icons-react";
|
||||
import type { FileInfo, TLine, TStation } from "../../untils/types";
|
||||
import { IconPlayerPlay, IconRepeat, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import moment from "moment";
|
||||
import { bytesToMB } from "../../untils/helper";
|
||||
|
||||
const ModalSelectIOS = ({
|
||||
socket,
|
||||
|
|
@ -21,16 +25,19 @@ const ModalSelectIOS = ({
|
|||
opened,
|
||||
close,
|
||||
line,
|
||||
getListIos,
|
||||
}: {
|
||||
socket: Socket | null;
|
||||
station: TStation | undefined;
|
||||
listIos: string[];
|
||||
listIos: FileInfo[];
|
||||
opened: boolean;
|
||||
close: () => void;
|
||||
line: TLine | undefined;
|
||||
getListIos: () => void;
|
||||
}) => {
|
||||
const [isReboot, setIsReboot] = useState<boolean>(true);
|
||||
const [inputSearch, setInputSearch] = useState<string>("");
|
||||
const [isDisable, setIsDisable] = useState<boolean>(false);
|
||||
|
||||
const filterIos = (type: string = "") => {
|
||||
// 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;
|
||||
|
||||
return listIos
|
||||
.filter((name) => {
|
||||
.filter((ios) => {
|
||||
if (type === "switch") {
|
||||
return switchRegex.test(name);
|
||||
return switchRegex.test(ios.name);
|
||||
}
|
||||
if (type === "router") {
|
||||
return routerRegex.test(name);
|
||||
return routerRegex.test(ios.name);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.filter((ios) => ios.toLowerCase().includes(inputSearch.toLowerCase()));
|
||||
.filter((ios) =>
|
||||
ios.name.toLowerCase().includes(inputSearch.toLowerCase())
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -65,11 +74,28 @@ const ModalSelectIOS = ({
|
|||
setIsReboot(true);
|
||||
}}
|
||||
title={
|
||||
<Text fz={"lg"} fw={"bolder"}>
|
||||
Select IOS
|
||||
</Text>
|
||||
<Flex gap={"xs"}>
|
||||
<Text fz={"lg"} fw={"bolder"}>
|
||||
Select IOS
|
||||
</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.List>
|
||||
|
|
@ -106,76 +132,114 @@ const ModalSelectIOS = ({
|
|||
onChange={(event) => setIsReboot(event.currentTarget.checked)}
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
{isDisable ? (
|
||||
<Box
|
||||
style={{
|
||||
height: "70vh",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
<Loader color="blue" />
|
||||
</Box>
|
||||
) : (
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</Table.Th>
|
||||
<Table.Th
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("router")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{ios || ""}</Table.Td>
|
||||
<Table.Td
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
const payload = {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
station: station,
|
||||
outletNumber: line?.outlet || -1,
|
||||
apcName: line?.apcName || line?.apc_name,
|
||||
isReboot: isReboot,
|
||||
};
|
||||
socket?.emit("load_ios_router", payload);
|
||||
close();
|
||||
Name
|
||||
</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
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("router")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<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
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
const payload = {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
station: station,
|
||||
outletNumber: line?.outlet || -1,
|
||||
apcName: line?.apcName || line?.apc_name,
|
||||
isReboot: isReboot,
|
||||
};
|
||||
socket?.emit("load_ios_router", payload);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
)}
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="switch" w={"100%"}>
|
||||
<Flex justify={"space-between"} align={"center"} mt={"xs"}>
|
||||
|
|
@ -197,71 +261,109 @@ const ModalSelectIOS = ({
|
|||
size="xs"
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
{isDisable ? (
|
||||
<Box
|
||||
style={{
|
||||
height: "70vh",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
<Loader color="blue" />
|
||||
</Box>
|
||||
) : (
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</Table.Th>
|
||||
<Table.Th
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("switch")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{ios || ""}</Table.Td>
|
||||
<Table.Td
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
socket?.emit("load_ios_switch", {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
});
|
||||
close();
|
||||
Name
|
||||
</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
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterIos("switch")?.map((ios, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<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
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
socket?.emit("load_ios_switch", {
|
||||
stationId: Number(station?.id),
|
||||
lineId: Number(line?.id),
|
||||
iosName: ios,
|
||||
});
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
)}
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Loader,
|
||||
Modal,
|
||||
ScrollArea,
|
||||
Table,
|
||||
|
|
@ -9,9 +11,11 @@ import {
|
|||
TextInput,
|
||||
} from "@mantine/core";
|
||||
import type { Socket } from "socket.io-client";
|
||||
import type { TLine, TStation } from "../../untils/types";
|
||||
import { IconPlayerPlay, IconX } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import type { FileInfo, TLine, TStation } from "../../untils/types";
|
||||
import { IconPlayerPlay, IconRepeat, IconX } from "@tabler/icons-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import moment from "moment";
|
||||
import { bytesToKB } from "../../untils/helper";
|
||||
|
||||
const ModalSelectLicense = ({
|
||||
socket,
|
||||
|
|
@ -20,22 +24,33 @@ const ModalSelectLicense = ({
|
|||
opened,
|
||||
close,
|
||||
line,
|
||||
getListLicense,
|
||||
}: {
|
||||
socket: Socket | null;
|
||||
station: TStation | undefined;
|
||||
listLicense: string[];
|
||||
listLicense: FileInfo[];
|
||||
opened: boolean;
|
||||
close: () => void;
|
||||
line: TLine | undefined;
|
||||
getListLicense: () => void;
|
||||
}) => {
|
||||
const [inputSearch, setInputSearch] = useState<string>("");
|
||||
const [inputPort, setInputPort] = useState<string>("GigabitEthernet0/0");
|
||||
const [licenseName, setLicenseName] = useState<string>("");
|
||||
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 = () => {
|
||||
return listLicense.filter((ios) =>
|
||||
ios.toLowerCase().includes(inputSearch.toLowerCase())
|
||||
return listLicense.filter((lic) =>
|
||||
lic.name.toLowerCase().includes(inputSearch.toLowerCase())
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -48,11 +63,28 @@ const ModalSelectLicense = ({
|
|||
setInputSearch("");
|
||||
}}
|
||||
title={
|
||||
<Text fz={"lg"} fw={"bolder"}>
|
||||
Select License
|
||||
</Text>
|
||||
<Flex gap={"xs"}>
|
||||
<Text fz={"lg"} fw={"bolder"}>
|
||||
Select License
|
||||
</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"}>
|
||||
<TextInput
|
||||
|
|
@ -73,73 +105,116 @@ const ModalSelectLicense = ({
|
|||
size="xs"
|
||||
/>
|
||||
</Flex>
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
{isDisable ? (
|
||||
<Box
|
||||
style={{
|
||||
height: "70vh",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
<Loader color="blue" />
|
||||
</Box>
|
||||
) : (
|
||||
<ScrollArea h={"70vh"} style={{ marginTop: "15px" }}>
|
||||
<Table
|
||||
stickyHeader
|
||||
striped
|
||||
highlightOnHover
|
||||
withRowBorders={true}
|
||||
withTableBorder={true}
|
||||
withColumnBorders={true}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</Table.Th>
|
||||
<Table.Th
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterLicense()?.map((lic, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{lic || ""}</Table.Td>
|
||||
<Table.Td
|
||||
<Table.Thead
|
||||
style={{
|
||||
top: 1,
|
||||
}}
|
||||
>
|
||||
<Table.Tr>
|
||||
<Table.Th
|
||||
style={{
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
// socket?.emit("load_ios_switch", {
|
||||
// stationId: Number(station?.id),
|
||||
// lineId: Number(line?.id),
|
||||
// iosName: ios,
|
||||
// });
|
||||
// close();
|
||||
setLicenseName(lic);
|
||||
setModalConfirm(true);
|
||||
Name
|
||||
</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
|
||||
style={{
|
||||
width: "200px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#94c6ff",
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{filterLicense()?.map((lic, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<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",
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
{bytesToKB(lic?.fileSize) || "0"} KB
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
style={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ width: "100px" }}
|
||||
variant="light"
|
||||
color="green"
|
||||
size="sm"
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
onClick={() => {
|
||||
// socket?.emit("load_ios_switch", {
|
||||
// stationId: Number(station?.id),
|
||||
// lineId: Number(line?.id),
|
||||
// iosName: ios,
|
||||
// });
|
||||
// close();
|
||||
setLicenseName(lic?.name);
|
||||
setModalConfirm(true);
|
||||
}}
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
style={{ position: "absolute", left: 0 }}
|
||||
|
|
@ -190,6 +265,7 @@ const ModalSelectLicense = ({
|
|||
</Tabs.List>
|
||||
<Tabs.Panel value="router" w={"100%"}>
|
||||
<Flex align={"center"} justify={"center"} p={"md"}>
|
||||
<Text me={"xs"}>Port Name:</Text>{" "}
|
||||
<TextInput
|
||||
style={{ width: "350px" }}
|
||||
placeholder="Port name"
|
||||
|
|
@ -234,6 +310,7 @@ const ModalSelectLicense = ({
|
|||
</Tabs.Panel>
|
||||
<Tabs.Panel value="switch" w={"100%"}>
|
||||
<Flex align={"center"} justify={"center"} p={"md"}>
|
||||
<Text me={"xs"}>Port Name:</Text>
|
||||
<TextInput
|
||||
style={{ width: "350px" }}
|
||||
placeholder="Port name"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import type {
|
||||
FileInfo,
|
||||
IScenario,
|
||||
SwitchPortsProps,
|
||||
TDataTicket,
|
||||
|
|
@ -66,6 +67,8 @@ const ModalTerminal = ({
|
|||
selectedLines,
|
||||
listIos,
|
||||
listLicense,
|
||||
getListLicense,
|
||||
getListIos,
|
||||
}: {
|
||||
opened: boolean;
|
||||
onClose: () => void;
|
||||
|
|
@ -74,8 +77,10 @@ const ModalTerminal = ({
|
|||
stationItem: TStation | undefined;
|
||||
scenarios: IScenario[];
|
||||
selectedLines: TLine[];
|
||||
listIos: string[];
|
||||
listLicense: string[];
|
||||
listIos: FileInfo[];
|
||||
listLicense: FileInfo[];
|
||||
getListLicense: () => void;
|
||||
getListIos: () => void;
|
||||
}) => {
|
||||
const user = useMemo(() => {
|
||||
return localStorage.getItem("user") &&
|
||||
|
|
@ -1210,18 +1215,20 @@ const ModalTerminal = ({
|
|||
size="xs"
|
||||
onClick={() => {
|
||||
setOpenSelectIos(true);
|
||||
getListIos();
|
||||
}}
|
||||
>
|
||||
Select IOS
|
||||
</Button>
|
||||
<Button
|
||||
disabled={true}
|
||||
disabled={isDisable}
|
||||
fw={400}
|
||||
variant="outline"
|
||||
color="yellow"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setOpenSelectLicense(true);
|
||||
getListLicense();
|
||||
}}
|
||||
>
|
||||
Select License
|
||||
|
|
@ -1503,6 +1510,7 @@ const ModalTerminal = ({
|
|||
station={stationItem}
|
||||
listIos={listIos}
|
||||
line={line}
|
||||
getListIos={getListIos}
|
||||
/>
|
||||
|
||||
<ModalSelectLicense
|
||||
|
|
@ -1514,6 +1522,7 @@ const ModalTerminal = ({
|
|||
station={stationItem}
|
||||
listLicense={listLicense}
|
||||
line={line}
|
||||
getListLicense={getListLicense}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -79,3 +79,13 @@ export const handleGetStorageCommand = (lineId: number): string[] => {
|
|||
const history: Record<number, string[]> = storage ? JSON.parse(storage) : {};
|
||||
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;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type FileInfo = {
|
||||
name: string;
|
||||
fileSize: number;
|
||||
dateModify: string;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue