Update load License

This commit is contained in:
nguyentrungthat 2026-01-13 10:54:12 +07:00
parent a9dce43ab2
commit 38b6cb4981
9 changed files with 497 additions and 230 deletions

View File

@ -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 ================= */

View File

@ -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)

View File

@ -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',
},

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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>
);

View File

@ -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));
}

View File

@ -263,3 +263,9 @@ export type TCategories = {
id: number;
name: string;
};
export type FileInfo = {
name: string;
fileSize: number;
dateModify: string;
};