muryshev commited on
Commit
d0ee71f
·
1 Parent(s): 893662b
src/api/logs/logsApi.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { query } from "@/shared/api/query";
2
  import { GetLogsRequestParams, GetLogsResponseType } from "./types";
 
3
 
4
  export const getLogs = async (params: GetLogsRequestParams): Promise<GetLogsResponseType> => {
5
  const response = await query<GetLogsResponseType>({
@@ -13,3 +14,19 @@ export const getLogs = async (params: GetLogsRequestParams): Promise<GetLogsResp
13
 
14
  return response.data;
15
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { query } from "@/shared/api/query";
2
  import { GetLogsRequestParams, GetLogsResponseType } from "./types";
3
+ import { downloadFile } from "@/shared/utils/downloadFile";
4
 
5
  export const getLogs = async (params: GetLogsRequestParams): Promise<GetLogsResponseType> => {
6
  const response = await query<GetLogsResponseType>({
 
14
 
15
  return response.data;
16
  };
17
+
18
+ export const downloadLogsAsExcel = async (params: GetLogsRequestParams, filename: string): Promise<Blob> => {
19
+ const response = await query<Blob>({
20
+ url: `/logs/excel`,
21
+ method: "get",
22
+ params: params,
23
+ responseType: "blob",
24
+ });
25
+
26
+ if ("error" in response) {
27
+ throw new Error(`Ошибка: ${response.error.status}`);
28
+ }
29
+ downloadFile(response, filename);
30
+
31
+ return response.data;
32
+ };
src/api/logs/types.ts CHANGED
@@ -7,12 +7,14 @@ export type LogItemType = {
7
  llm_result: string;
8
  llm_settings: string;
9
  user_name: string;
 
10
  error: string;
11
  };
12
 
13
  export type GetLogsRequestParams = {
14
  page?: number;
15
  user_name?: string;
 
16
  date_to?: Date;
17
  date_from?: Date;
18
  page_size?: number;
 
7
  llm_result: string;
8
  llm_settings: string;
9
  user_name: string;
10
+ chat_id: string;
11
  error: string;
12
  };
13
 
14
  export type GetLogsRequestParams = {
15
  page?: number;
16
  user_name?: string;
17
+ chat_id?: string;
18
  date_to?: Date;
19
  date_from?: Date;
20
  page_size?: number;
src/components/pages/logs/LogDetailsModal.scss CHANGED
@@ -74,35 +74,6 @@
74
  input {
75
  max-width: 100%;
76
  }
77
-
78
- input[type="text"],
79
- input[type="number"] {
80
- padding: 8px 12px;
81
- border: 1px solid #ccc;
82
- border-radius: 5px;
83
- font-size: 1rem;
84
- color: #333;
85
- background-color: #f9f9f9;
86
- transition: border-color 0.2s;
87
-
88
- &:focus {
89
- border-color: #007bff;
90
- outline: none;
91
- background-color: #fff;
92
- }
93
-
94
- &:disabled {
95
- background-color: #e9ecef;
96
- color: #666;
97
- }
98
- }
99
-
100
- input[type="checkbox"] {
101
- width: 20px;
102
- height: 20px;
103
- margin: 0;
104
- cursor: pointer;
105
- }
106
  }
107
 
108
  .button-group {
 
74
  input {
75
  max-width: 100%;
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
79
  .button-group {
src/components/pages/logs/LogDetailsModal.tsx CHANGED
@@ -10,6 +10,7 @@ interface LogDetailsModalProps {
10
  isOpen: boolean;
11
  onRequestClose: () => void;
12
  log: LogItemType;
 
13
  }
14
 
15
  const customStyles = {
@@ -26,6 +27,7 @@ const LogDetailsModal: React.FC<LogDetailsModalProps> = ({
26
  isOpen,
27
  onRequestClose,
28
  log,
 
29
  }) => {
30
 
31
  let qeResult = null;
@@ -37,6 +39,12 @@ const LogDetailsModal: React.FC<LogDetailsModalProps> = ({
37
  console.error('Ошибка парсинга JSON:', error);
38
  }
39
 
 
 
 
 
 
 
40
  return (
41
  <Modal
42
  isOpen={isOpen}
@@ -60,6 +68,17 @@ const LogDetailsModal: React.FC<LogDetailsModalProps> = ({
60
  <span className='title'>Пользователь:</span>
61
  <input type="text" value={log.user_name} readOnly={true} />
62
  </label>
 
 
 
 
 
 
 
 
 
 
 
63
  <label>
64
  <span className='title'>Время:</span>
65
  <input type="text" value={(log.date_created ? new Date(log.date_created).toLocaleDateString() : '') + ' ' + (log.date_created ? new Date(log.date_created).toLocaleTimeString() : '')} readOnly={true} />
 
10
  isOpen: boolean;
11
  onRequestClose: () => void;
12
  log: LogItemType;
13
+ setChatIdFilter: (value: string | undefined) => void;
14
  }
15
 
16
  const customStyles = {
 
27
  isOpen,
28
  onRequestClose,
29
  log,
30
+ setChatIdFilter
31
  }) => {
32
 
33
  let qeResult = null;
 
39
  console.error('Ошибка парсинга JSON:', error);
40
  }
41
 
42
+ // Обработчик клика на chat_id
43
+ const handleChatIdClick = () => {
44
+ setChatIdFilter(log.chat_id);
45
+ onRequestClose();
46
+ };
47
+
48
  return (
49
  <Modal
50
  isOpen={isOpen}
 
68
  <span className='title'>Пользователь:</span>
69
  <input type="text" value={log.user_name} readOnly={true} />
70
  </label>
71
+ <label>
72
+ <span className='title'>Чат:</span>
73
+ {/* <input type="text" value={log.chat_id} readOnly={true} /> */}
74
+ <span
75
+ className="chat-id clickable"
76
+ onClick={handleChatIdClick}
77
+ style={{ cursor: 'pointer', color: '#007bff', textDecoration: 'underline' }}
78
+ >
79
+ {log.chat_id}
80
+ </span>
81
+ </label>
82
  <label>
83
  <span className='title'>Время:</span>
84
  <input type="text" value={(log.date_created ? new Date(log.date_created).toLocaleDateString() : '') + ' ' + (log.date_created ? new Date(log.date_created).toLocaleTimeString() : '')} readOnly={true} />
src/components/pages/logs/Logs.scss CHANGED
@@ -82,7 +82,7 @@ td.ellipsis {
82
  max-width: 400px;
83
  }
84
 
85
- .date-filter {
86
  display: flex;
87
  gap: 20px;
88
  margin-bottom: 20px;
@@ -102,6 +102,9 @@ td.ellipsis {
102
  border-radius: 4px;
103
  }
104
 
 
 
 
105
  // .react-datepicker__input-container {
106
 
107
  // input,
 
82
  max-width: 400px;
83
  }
84
 
85
+ .filters-container {
86
  display: flex;
87
  gap: 20px;
88
  margin-bottom: 20px;
 
102
  border-radius: 4px;
103
  }
104
 
105
+ .filter-secondary-container{
106
+
107
+ }
108
  // .react-datepicker__input-container {
109
 
110
  // input,
src/components/pages/logs/Logs.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { FC, useCallback, useEffect, useMemo, useState } from "react";
2
  import { useSearchParams } from "react-router-dom";
3
  import Select from "react-select";
4
 
@@ -30,19 +30,23 @@ import Documents from "../documentsPage/Documents";
30
  import LogDetailsModal from "./LogDetailsModal";
31
  import DatePicker from "react-datepicker";
32
  import "react-datepicker/dist/react-datepicker.css";
 
 
33
 
34
  const Logs: FC = () => {
35
  const [page, setPage] = useState<number>(0);
36
  const [pageSize, setPageSize] = useState<number>(50);
37
  const [userInput, setUserInput] = useState<string | undefined>(undefined);
38
  const [user, setUser] = useState<string | undefined>(undefined);
 
39
  const [sort, setSort] = useState<undefined | { field: string; direction: SortDirections }[]>(undefined);
40
  const [dateTo, setDateTo] = useState<Date | undefined>(undefined);
41
  const [dateFrom, setDateFrom] = useState<Date | undefined>(undefined);
42
  const { data: logsData, isLoading } = useGetLogs({
43
  page, page_size: pageSize,
44
  user_name: user, sort,
45
- date_to: dateTo, date_from: dateFrom
 
46
  });
47
  const [isModalOpen, setIsModalOpen] = useState(false);
48
  const [selectedLog, setSelectedLog] = useState<LogItemType | null>(null);
@@ -129,9 +133,25 @@ const Logs: FC = () => {
129
  // }
130
  // }, [datasetsData, searchParams, setSearchParams]);
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  return (
133
  <div className="documents-page">
134
- <div className="date-filter">
135
  <div className="date-picker-container">
136
  <label htmlFor="startDate">Показать логи с</label>
137
  <DatePicker
@@ -156,7 +176,14 @@ const Logs: FC = () => {
156
  showIcon
157
  />
158
  </div>
159
- {/* <Button name={"Показать"} onClick={handleFilterList} loading={isLoading} /> */}
 
 
 
 
 
 
 
160
  </div>
161
  <div className="docs-table-container" style={{ position: "relative" }}>
162
  {isLoading && (
@@ -178,6 +205,11 @@ const Logs: FC = () => {
178
  <span>Пользователь</span>
179
  </div>
180
  </th>
 
 
 
 
 
181
  <th>
182
  <div className="name-with-sort">
183
  <span>Запрос</span>
@@ -196,6 +228,7 @@ const Logs: FC = () => {
196
  <tr key={log.id} onClick={() => openDetailsModal(log)}>
197
  <td>{log.date_created ? new Date(log.date_created).toLocaleDateString() : ''} {log.date_created ? new Date(log.date_created).toLocaleTimeString() : ''}</td>
198
  <td>{log.user_name}</td>
 
199
  <td className="ellipsis">{log.user_request}</td>
200
  <td className="ellipsis">{log.llm_result}</td>
201
  </tr>
@@ -214,6 +247,7 @@ const Logs: FC = () => {
214
  isOpen={isModalOpen}
215
  onRequestClose={closeModal}
216
  log={selectedLog}
 
217
  />) : ''}
218
 
219
  </div>
 
1
+ import { ChangeEventHandler, FC, useCallback, useEffect, useMemo, useState } from "react";
2
  import { useSearchParams } from "react-router-dom";
3
  import Select from "react-select";
4
 
 
30
  import LogDetailsModal from "./LogDetailsModal";
31
  import DatePicker from "react-datepicker";
32
  import "react-datepicker/dist/react-datepicker.css";
33
+ import axios from 'axios';
34
+ import { downloadLogsAsExcel } from "@/api/logs/logsApi";
35
 
36
  const Logs: FC = () => {
37
  const [page, setPage] = useState<number>(0);
38
  const [pageSize, setPageSize] = useState<number>(50);
39
  const [userInput, setUserInput] = useState<string | undefined>(undefined);
40
  const [user, setUser] = useState<string | undefined>(undefined);
41
+ const [chatIdFilter, setChatIdFilter] = useState<string | undefined>(undefined);
42
  const [sort, setSort] = useState<undefined | { field: string; direction: SortDirections }[]>(undefined);
43
  const [dateTo, setDateTo] = useState<Date | undefined>(undefined);
44
  const [dateFrom, setDateFrom] = useState<Date | undefined>(undefined);
45
  const { data: logsData, isLoading } = useGetLogs({
46
  page, page_size: pageSize,
47
  user_name: user, sort,
48
+ date_to: dateTo, date_from: dateFrom,
49
+ chat_id: chatIdFilter
50
  });
51
  const [isModalOpen, setIsModalOpen] = useState(false);
52
  const [selectedLog, setSelectedLog] = useState<LogItemType | null>(null);
 
133
  // }
134
  // }, [datasetsData, searchParams, setSearchParams]);
135
 
136
+ const handleDownloadExcel = useCallback(async () => {
137
+ const params: GetLogsRequestParams = {
138
+ user_name: user,
139
+ date_from: dateFrom,
140
+ date_to: dateTo,
141
+ sort,
142
+ chat_id: chatIdFilter
143
+ };
144
+
145
+ downloadLogsAsExcel(params, "Логи запросов");
146
+ }, [user, dateFrom, dateTo, sort, chatIdFilter]);
147
+
148
+ const handleChatIdChange = (event: any) => {
149
+ setChatIdFilter(event.target.value);
150
+ };
151
+
152
  return (
153
  <div className="documents-page">
154
+ <div className="filters-container">
155
  <div className="date-picker-container">
156
  <label htmlFor="startDate">Показать логи с</label>
157
  <DatePicker
 
176
  showIcon
177
  />
178
  </div>
179
+ <label htmlFor="endDate">Чат ID:
180
+ <input type="text" value={chatIdFilter} onChange={handleChatIdChange} />
181
+ </label>
182
+
183
+
184
+ </div>
185
+ <div className="filters-container">
186
+ <Button name="Скачать в Excel" onClick={handleDownloadExcel} icon={<GoDownload />} />
187
  </div>
188
  <div className="docs-table-container" style={{ position: "relative" }}>
189
  {isLoading && (
 
205
  <span>Пользователь</span>
206
  </div>
207
  </th>
208
+ <th>
209
+ <div className="name-with-sort">
210
+ <span>Чат</span>
211
+ </div>
212
+ </th>
213
  <th>
214
  <div className="name-with-sort">
215
  <span>Запрос</span>
 
228
  <tr key={log.id} onClick={() => openDetailsModal(log)}>
229
  <td>{log.date_created ? new Date(log.date_created).toLocaleDateString() : ''} {log.date_created ? new Date(log.date_created).toLocaleTimeString() : ''}</td>
230
  <td>{log.user_name}</td>
231
+ <td className="ellipsis" style={{ maxWidth: "100px" }}>{log.chat_id}</td>
232
  <td className="ellipsis">{log.user_request}</td>
233
  <td className="ellipsis">{log.llm_result}</td>
234
  </tr>
 
247
  isOpen={isModalOpen}
248
  onRequestClose={closeModal}
249
  log={selectedLog}
250
+ setChatIdFilter={setChatIdFilter}
251
  />) : ''}
252
 
253
  </div>
src/index.scss CHANGED
@@ -107,3 +107,33 @@ button:focus:not(:focus-visible) {
107
  font-size: 2.4rem;
108
  }
109
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  font-size: 2.4rem;
108
  }
109
  }
110
+
111
+
112
+ input[type="text"],
113
+ input[type="number"] {
114
+ padding: 8px 12px;
115
+ border: 1px solid #ccc;
116
+ border-radius: 5px;
117
+ font-size: 1rem;
118
+ color: #333;
119
+ background-color: #f9f9f9;
120
+ transition: border-color 0.2s;
121
+
122
+ &:focus {
123
+ border-color: #007bff;
124
+ outline: none;
125
+ background-color: #fff;
126
+ }
127
+
128
+ &:disabled {
129
+ background-color: #e9ecef;
130
+ color: #666;
131
+ }
132
+ }
133
+
134
+ input[type="checkbox"] {
135
+ width: 20px;
136
+ height: 20px;
137
+ margin: 0;
138
+ cursor: pointer;
139
+ }