ATC_SIMPLE/FRONTEND/src/components/Drawer/DrawerLogs.tsx

315 lines
10 KiB
TypeScript

import { useDisclosure } from "@mantine/hooks";
import {
Button,
Box,
Drawer,
Grid,
Table,
Text,
ScrollArea,
Tooltip,
TextInput,
} from "@mantine/core";
import { DateInput } from "@mantine/dates";
import { useEffect, useState } from "react";
import type { ISystemLog } from "../../untils/types";
import {
IconDownload,
IconEye,
IconInfoCircle,
IconX,
} from "@tabler/icons-react";
import classes from "../Component.module.css";
import moment from "moment";
import type { Socket } from "socket.io-client";
import ModalLog from "../Modal/ModalLog";
function DrawerLogs({
socket,
isLogModalOpen,
setIsLogModalOpen,
testLogContent,
setTestLogContent,
}: {
socket: Socket | null;
isLogModalOpen: boolean;
setIsLogModalOpen: (value: React.SetStateAction<boolean>) => void;
testLogContent: string;
setTestLogContent: (value: React.SetStateAction<string>) => void;
}) {
const [opened, { open, close }] = useDisclosure(false);
const [systemLogs, setSystemLogs] = useState<ISystemLog[]>([]);
const [isDownloadLog, setIsDownloadLog] = useState(false);
// const [testLogContent, setTestLogContent] = useState("");
// const [isLogModalOpen, setIsLogModalOpen] = useState(false);
const [downloadName, setDownloadName] = useState("");
const [searchFileName, setSearchFileName] = useState("");
const [fromDate, setFromDate] = useState<Date | null>(null);
const [toDate, setToDate] = useState<Date | null>(null);
const [filteredLogs, setFilteredLogs] = useState<ISystemLog[]>([]);
useEffect(() => {
if (opened) {
socket?.emit("get_list_logs");
}
}, [opened]);
useEffect(() => {
socket?.on("list_logs", (files: string[]) => {
const list: ISystemLog[] = files.map((file) => {
const filename = file.replace(/^.*[\\/]/, "");
const createAt = filename.match(/\d{8}/);
return {
fileName:
file.split("/")[3] || file.split("/")[2] || file.split("/")[1],
createdAt: createAt ? createAt[0] : "N/A",
path: file,
};
});
setSystemLogs(
list.sort(
(a: ISystemLog, b: ISystemLog) =>
parseInt(b.createdAt) - parseInt(a.createdAt)
)
);
});
}, [socket]);
useEffect(() => {
if (isDownloadLog && testLogContent && downloadName) {
const blob = new Blob([testLogContent], { type: "text/plain" });
// Create a temporary link element
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = downloadName;
// Trigger the download by clicking the link
link.click();
// Clean up
URL.revokeObjectURL(link.href);
setIsDownloadLog(false);
setTestLogContent("");
setDownloadName("");
}
}, [testLogContent]);
useEffect(() => {
// Chuẩn bị trước các giá trị search/date để tránh tính lại trong filter cho từng phần tử
const trimmedSearch = searchFileName.trim().toLowerCase();
const hasSearch = trimmedSearch.length > 0;
const fromMoment = fromDate ? moment(fromDate).startOf("day") : null;
const toMoment = toDate ? moment(toDate).endOf("day") : null;
const delayDebounceFn = setTimeout(() => {
// Nếu không có filter nào, tránh filter tốn công, gán thẳng
if (!hasSearch && !fromMoment && !toMoment) {
setFilteredLogs(systemLogs);
return;
}
const next = systemLogs.filter((log) => {
if (hasSearch && !log.fileName.toLowerCase().includes(trimmedSearch)) {
return false;
}
const logDate = moment(log.createdAt, "YYYYMMDD");
if (fromMoment && !logDate.isSameOrAfter(fromMoment)) {
return false;
}
if (toMoment && !logDate.isSameOrBefore(toMoment)) {
return false;
}
return true;
});
setFilteredLogs(next);
}, 500);
return () => clearTimeout(delayDebounceFn);
}, [searchFileName, fromDate, toDate, systemLogs]);
return (
<>
<Drawer
size={"50%"}
position="right"
style={{ position: "absolute", left: 0 }}
offset={8}
radius="md"
opened={opened}
onClose={close}
title={
<div>
<Tooltip
label={
<div>
Format:
<i style={{ marginLeft: "4px" }}>
YYYYMMDD-AUTO-Session.{`{Station name}`}-{`{Station ID}`}-
{`{Station IP}`}-{`{Line number}`}
.log
</i>
</div>
}
position="right"
>
<Text
fw={700}
style={{ display: "flex", alignItems: "center", gap: "6px" }}
>
List Logs <IconInfoCircle color="#3bb7e9" fontSize={"12px"} />
</Text>
</Tooltip>
</div>
}
>
<Grid>
<Grid.Col span={12}>
<Box mb="xs">
<Grid gutter="xs">
<Grid.Col span={6}>
<TextInput
placeholder="Search file name"
value={searchFileName}
onChange={(event) =>
setSearchFileName(event.currentTarget.value)
}
rightSection={
searchFileName ? (
<IconX
size={14}
style={{ cursor: "pointer" }}
onClick={() => setSearchFileName("")}
/>
) : null
}
rightSectionPointerEvents="auto"
size="xs"
/>
</Grid.Col>
<Grid.Col span={3}>
<DateInput
value={fromDate}
onChange={(value) => setFromDate(value as Date | null)}
placeholder="From date"
valueFormat="DD/MM/YYYY"
size="xs"
clearable
/>
</Grid.Col>
<Grid.Col span={3}>
<DateInput
value={toDate}
onChange={(value) => setToDate(value as Date | null)}
placeholder="To date"
valueFormat="DD/MM/YYYY"
size="xs"
clearable
/>
</Grid.Col>
</Grid>
</Box>
<ScrollArea h={"85vh"} style={{ marginTop: "15px" }}>
<Table
stickyHeader
striped
highlightOnHover
withRowBorders={true}
withTableBorder={true}
withColumnBorders={true}
>
<Table.Thead
style={{
top: 1,
}}
>
<Table.Tr>
<Table.Th style={{ width: "50%" }}>File name</Table.Th>
<Table.Th style={{ width: "30%" }}>Created at</Table.Th>
<Table.Th style={{ width: "10%" }}></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{filteredLogs.map((element) => (
<Table.Tr key={element.path}>
<Table.Td>{element.fileName}</Table.Td>
<Table.Td>
<Text>
{moment(element.createdAt).format("DD/MM/YYYY")}
</Text>
</Table.Td>
<Table.Td>
<Box
key={"action-" + element.fileName}
className={classes.optionIcon}
>
<IconEye
className={classes.viewIcon}
onClick={() => {
setTestLogContent("");
socket?.emit("get_content_log", {
line: { systemLogUrl: element.path },
});
setIsLogModalOpen(true);
}}
width={20}
/>
<IconDownload
className={[
classes.downloadIcon,
isDownloadLog ? classes.isDisabled : "",
].join(" ")}
onClick={() => {
socket?.emit("get_content_log", {
line: { systemLogUrl: element.path },
});
setIsDownloadLog(true);
setTestLogContent("");
setDownloadName(
element.path.split("/")[3] ||
element.path.split("/")[2] ||
element.path.split("/")[1]
);
}}
width={20}
/>
</Box>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ScrollArea>
</Grid.Col>
</Grid>
{isLogModalOpen && (
<ModalLog
opened={isLogModalOpen}
onClose={() => {
setIsLogModalOpen(false);
}}
testLogContent={testLogContent}
/>
)}
</Drawer>
<Button
fw={400}
style={{ height: "30px", width: "100px" }}
title="Add Scenario"
variant="outline"
// color="green"
onClick={() => {
open();
}}
>
List logs{" "}
</Button>
</>
);
}
export default DrawerLogs;