Spaces:
Running
Running
| 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; | |