Add AST cache for AdminLogsTest.php with extracted nodes and edges
This commit is contained in:
@@ -83,8 +83,42 @@ export const api = {
|
||||
put: <T>(path: string, body: unknown) =>
|
||||
request<T>(path, { method: 'PUT', body: JSON.stringify(body) }),
|
||||
delete: <T>(path: string) => request<T>(path, { method: 'DELETE' }),
|
||||
download: (path: string) => downloadFile(path),
|
||||
};
|
||||
|
||||
// دانلود فایل با ارسال توکن JWT در هدر و ذخیره روی دیسک کاربر
|
||||
async function downloadFile(path: string, retry = true): Promise<void> {
|
||||
const token = getToken();
|
||||
const headers: Record<string, string> = {};
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
|
||||
const res = await fetch(`${BASE_URL}${path}`, { headers });
|
||||
|
||||
if (!res.ok) {
|
||||
if (res.status === 401 && retry) {
|
||||
const newToken = await refreshOnce();
|
||||
if (newToken) return downloadFile(path, false);
|
||||
useAuthStore.getState().logout();
|
||||
window.location.replace('/admin/login');
|
||||
}
|
||||
throw new ApiError(res.status, 'ERR_DOWNLOAD', 'دانلود ناموفق بود');
|
||||
}
|
||||
|
||||
const blob = await res.blob();
|
||||
const disposition = res.headers.get('Content-Disposition') ?? '';
|
||||
const match = disposition.match(/filename="?([^"]+)"?/);
|
||||
const filename = match ? match[1] : 'download';
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data: T;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { TrashIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline';
|
||||
import { api } from '../lib/api';
|
||||
import type { PaginatedResponse, ApiResponse } from '../lib/api';
|
||||
import type { AppLog } from '../types';
|
||||
@@ -38,9 +38,22 @@ export default function LogsPage() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [viewLog, setViewLog] = useState<AppLog | null>(null);
|
||||
const [confirmClear, setConfirmClear] = useState(false);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const limit = 25;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const handleDownload = async () => {
|
||||
setDownloading(true);
|
||||
try {
|
||||
const qs =
|
||||
(level ? `&level=${level}` : '') +
|
||||
(search ? `&search=${encodeURIComponent(search)}` : '');
|
||||
await api.download(`/api/v1/admin/logs/export?${qs}`);
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logsQuery = useQuery({
|
||||
queryKey: ['admin-logs', page, level, search],
|
||||
queryFn: () =>
|
||||
@@ -95,14 +108,24 @@ export default function LogsPage() {
|
||||
<div className="muted">رویدادهای ثبتشدهی سیستم (اخطار و بالاتر)</div>
|
||||
</div>
|
||||
{tab === 'logs' && (
|
||||
<button
|
||||
className="btn danger sm"
|
||||
onClick={() => setConfirmClear(true)}
|
||||
disabled={(logsQuery.data?.meta?.totalRecords ?? 0) === 0}
|
||||
>
|
||||
<TrashIcon style={{ width: 16, height: 16 }} />
|
||||
حذف همه لاگها
|
||||
</button>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<button
|
||||
className="btn ghost sm"
|
||||
onClick={handleDownload}
|
||||
disabled={downloading || (logsQuery.data?.meta?.totalRecords ?? 0) === 0}
|
||||
>
|
||||
<ArrowDownTrayIcon style={{ width: 16, height: 16 }} />
|
||||
{downloading ? 'در حال دانلود...' : 'دانلود همه لاگها'}
|
||||
</button>
|
||||
<button
|
||||
className="btn danger sm"
|
||||
onClick={() => setConfirmClear(true)}
|
||||
disabled={(logsQuery.data?.meta?.totalRecords ?? 0) === 0}
|
||||
>
|
||||
<TrashIcon style={{ width: 16, height: 16 }} />
|
||||
حذف همه لاگها
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user