Compare commits

2 Commits

Author SHA1 Message Date
2a9449ba58 feat: add txt export 2026-02-22 19:37:02 +03:00
1c097a4ca2 v1.0.2 2026-02-22 19:36:53 +03:00
2 changed files with 56 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "white-nights",
"private": true,
"version": "1.0.1",
"version": "1.0.2",
"type": "module",
"license": "UNLICENSED",
"scripts": {

View File

@@ -1,5 +1,5 @@
import { API_URL, authInstance, Modal } from "@shared";
import { CircularProgress, TextField } from "@mui/material";
import { Button, CircularProgress, TextField } from "@mui/material";
import { useEffect, useState, useMemo } from "react";
import { toast } from "react-toastify";
@@ -82,6 +82,7 @@ const parseJsonLogLine = (line: string) => {
return { ts, level, msg, extraStr };
}
} catch {
return null;
}
return null;
};
@@ -180,6 +181,49 @@ export const DeviceLogsModal = ({
return parsed;
}, [chunks]);
const logsText = useMemo(
() =>
logs
.map((log) => {
const level = log.level === "unknown" ? "LOG" : log.level.toUpperCase();
const time = log.time ? `[${log.time}] ` : "";
return `${time}${level}: ${log.text}`;
})
.join("\n"),
[logs]
);
const handleDownloadLogs = () => {
if (!logsText) {
toast.info("Нет логов для сохранения");
return;
}
try {
const safeDeviceUuid = (deviceUuid ?? "device").replace(
/[^a-zA-Z0-9_-]/g,
"_"
);
const fileName = `logs_${safeDeviceUuid}_${dateFrom}_${dateTo}.txt`;
const blob = new Blob([`\uFEFF${logsText}`], {
type: "text/plain;charset=utf-8",
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
toast.success("Логи сохранены в .txt");
} catch {
toast.error("Не удалось сохранить логи");
}
};
return (
<Modal open={open} onClose={onClose} sx={{ width: "80vw", p: 3 }}>
<div className="flex flex-col gap-6 h-[85vh]">
@@ -188,7 +232,7 @@ export const DeviceLogsModal = ({
<div className="flex gap-4 items-center">
<TextField
type="date"
label="С"
label="От"
size="small"
value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)}
@@ -196,12 +240,20 @@ export const DeviceLogsModal = ({
/>
<TextField
type="date"
label="По"
label="До"
size="small"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
slotProps={{ inputLabel: { shrink: true } }}
/>
<Button
variant="outlined"
size="small"
onClick={handleDownloadLogs}
disabled={isLoading || Boolean(error) || logs.length === 0}
>
Скачать .txt
</Button>
</div>
</div>