muryshev's picture
update
d0ee71f
import { ChangeEventHandler, FC, useCallback, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "react-router-dom";
import Select from "react-select";
import {
useActivateDataset,
useCreateDatasetsDraft,
useDeleteDataset,
useDeleteFileFromDataset,
useGetDatasets,
} from "@/api/documents/hooks";
import Button from "@/components/generics/button/Button";
import Modal from "@/components/generics/modal/Modal";
import { DocsList } from "@/components/views/documents/docsList/DocsList";
import { CreateDatasetForm } from "@/components/views/documents/createDatasetForm/CreateDatasetForm";
import { AddDocumentForm } from "@/components/views/documents/addDocuimentForm/AddDocumentForm";
import "./Logs.scss";
import { downloadDocument } from "@/api/documents/documentsApi";
import Input from "@/components/generics/input/Input";
import Spinner from "@/components/generics/spinner/Spinner";
import Tag from "@/components/generics/tag/Tag";
import Tooltip from "@/components/generics/tooltip/Tooltip";
import { Pagination } from "@/components/views/pagination/Pagination";
import { SortDirectionsTooltipMap, StatusMap } from "@/shared/constants";
import { GoSearch, GoDownload, GoTrash, GoTriangleUp, GoTriangleDown } from "react-icons/go";
import { useGetLogs } from "@/api/logs/hooks";
import { GetLogsRequestParams, LogItemType, SortDirections } from "@/api/logs/types";
import Documents from "../documentsPage/Documents";
import LogDetailsModal from "./LogDetailsModal";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import axios from 'axios';
import { downloadLogsAsExcel } from "@/api/logs/logsApi";
const Logs: FC = () => {
const [page, setPage] = useState<number>(0);
const [pageSize, setPageSize] = useState<number>(50);
const [userInput, setUserInput] = useState<string | undefined>(undefined);
const [user, setUser] = useState<string | undefined>(undefined);
const [chatIdFilter, setChatIdFilter] = useState<string | undefined>(undefined);
const [sort, setSort] = useState<undefined | { field: string; direction: SortDirections }[]>(undefined);
const [dateTo, setDateTo] = useState<Date | undefined>(undefined);
const [dateFrom, setDateFrom] = useState<Date | undefined>(undefined);
const { data: logsData, isLoading } = useGetLogs({
page, page_size: pageSize,
user_name: user, sort,
date_to: dateTo, date_from: dateFrom,
chat_id: chatIdFilter
});
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedLog, setSelectedLog] = useState<LogItemType | null>(null);
const [searchParams, setSearchParams] = useSearchParams();
// const handleFilterList = () => {
// const localStartDate = dateFrom ? new Date(dateFrom) : undefined;
// localStartDate?.setHours(0, 0, 0, 0);
// const localEndDate = dateTo ? new Date(dateTo) : undefined;
// localEndDate?.setHours(23, 59, 59, 999);
// setDateFrom(localStartDate ? new Date(localStartDate) : undefined);
// setDateTo(localEndDate ? new Date(localEndDate) : undefined);
// setPage(0);
// };
const openDetailsModal = (log: LogItemType) => {
setSelectedLog(log)
setIsModalOpen(true);
};
const closeModal = () => {
setSelectedLog(null);
setIsModalOpen(false);
};
const toggleSort = (field: string) => {
setSort((prevSort) => {
if (prevSort?.length && prevSort?.length > 0) {
const newSort = [...prevSort];
const existingFieldIndex = prevSort?.findIndex((e) => e.field === field);
const currentDirection = prevSort[existingFieldIndex]?.direction;
if (existingFieldIndex != null && existingFieldIndex !== -1) {
const newSort = [...prevSort];
if (currentDirection === SortDirections.asc) {
newSort[existingFieldIndex] = { field, direction: SortDirections.desc };
return newSort;
} else if (currentDirection === SortDirections.desc) {
newSort.splice(existingFieldIndex, 1);
return newSort;
}
} else {
newSort.push({ field, direction: SortDirections.asc });
}
return newSort;
} else {
return [{ field, direction: SortDirections.asc }];
}
});
};
const sorting = (field: string) => {
const currentDirection = sort?.find((e) => e.field === field)?.direction;
return (
<Tooltip text={SortDirectionsTooltipMap[currentDirection ?? "empty"]}>
<Button
onClick={() => toggleSort(field)}
icon={
<div className="sort-btn">
<GoTriangleUp viewBox="0 0 20 20" className={currentDirection === SortDirections.asc ? "active" : ""} />
<GoTriangleDown
viewBox="0 0 20 20"
className={currentDirection === SortDirections.desc ? "arrow-down active" : "arrow-down"}
/>
</div>
}
buttonType="link"
/>
</Tooltip>
);
};
// useEffect(() => {
// if (
// (!searchParams.get("datasetId") ||
// !datasetsData?.find((e) => e.id.toString() == searchParams.get("datasetId"))) &&
// datasetsData?.[0]?.id
// ) {
// setSearchParams({ datasetId: String(datasetsData?.[0]?.id) });
// }
// }, [datasetsData, searchParams, setSearchParams]);
const handleDownloadExcel = useCallback(async () => {
const params: GetLogsRequestParams = {
user_name: user,
date_from: dateFrom,
date_to: dateTo,
sort,
chat_id: chatIdFilter
};
downloadLogsAsExcel(params, "Логи запросов");
}, [user, dateFrom, dateTo, sort, chatIdFilter]);
const handleChatIdChange = (event: any) => {
setChatIdFilter(event.target.value);
};
return (
<div className="documents-page">
<div className="filters-container">
<div className="date-picker-container">
<label htmlFor="startDate">Показать логи с</label>
<DatePicker
id="startDate"
dateFormat="dd/MM/yyyy"
selected={dateFrom}
onChange={(date) => setDateFrom(date ?? undefined)}
selectsStart
isClearable={true}
showIcon
/>
</div>
<div className="date-picker-container">
<label htmlFor="endDate">по</label>
<DatePicker
id="endDate"
dateFormat="dd/MM/yyyy"
selected={dateTo}
onChange={(date) => setDateTo(date ?? undefined)}
selectsEnd
isClearable={true}
showIcon
/>
</div>
<label htmlFor="endDate">Чат ID:
<input type="text" value={chatIdFilter} onChange={handleChatIdChange} />
</label>
</div>
<div className="filters-container">
<Button name="Скачать в Excel" onClick={handleDownloadExcel} icon={<GoDownload />} />
</div>
<div className="docs-table-container" style={{ position: "relative" }}>
{isLoading && (
<div className="loading-overlay">
<Spinner />
</div>
)}
<table className="docs-table">
<thead>
<tr>
<th style={{ width: "50%" }}>
<div className="name-with-sort">
<span>Время</span>
{sorting("date_created")}
</div>
</th>
<th>
<div className="name-with-sort">
<span>Пользователь</span>
</div>
</th>
<th>
<div className="name-with-sort">
<span>Чат</span>
</div>
</th>
<th>
<div className="name-with-sort">
<span>Запрос</span>
</div>
</th>
<th>
<div className="name-with-sort">
<span>Ответ</span>
</div>
</th>
<th></th>
</tr>
</thead>
<tbody>
{logsData?.data.map((log) => (
<tr key={log.id} onClick={() => openDetailsModal(log)}>
<td>{log.date_created ? new Date(log.date_created).toLocaleDateString() : ''} {log.date_created ? new Date(log.date_created).toLocaleTimeString() : ''}</td>
<td>{log.user_name}</td>
<td className="ellipsis" style={{ maxWidth: "100px" }}>{log.chat_id}</td>
<td className="ellipsis">{log.user_request}</td>
<td className="ellipsis">{log.llm_result}</td>
</tr>
))}
</tbody>
</table>
</div>
<Pagination
total={logsData?.data.total ?? 0}
pageNumber={logsData?.data.page ? logsData?.data.page : page}
pageSize={logsData?.data.page_size ?? 50}
setPageSize={setPageSize}
setPage={setPage}
/>
{selectedLog !== null ? (<LogDetailsModal
isOpen={isModalOpen}
onRequestClose={closeModal}
log={selectedLog}
setChatIdFilter={setChatIdFilter}
/>) : ''}
</div>
);
};
export default Logs;