import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; 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'; import { formatDateTime } from '../lib/utils'; import DataTable, { Column } from '../components/ui/DataTable'; import Pagination from '../components/ui/Pagination'; import Modal from '../components/ui/Modal'; import ConfirmDialog from '../components/ui/ConfirmDialog'; import SearchableSelect from '../components/ui/SearchableSelect'; import { digitsOnly } from '../lib/utils'; const LEVEL_META: Record = { emergency: { label: 'اضطراری', cls: 'red' }, alert: { label: 'هشدار جدی', cls: 'red' }, critical: { label: 'بحرانی', cls: 'red' }, error: { label: 'خطا', cls: 'red' }, warning: { label: 'اخطار', cls: 'amber' }, notice: { label: 'اطلاع', cls: 'gray' }, info: { label: 'اطلاعات', cls: 'gray' }, debug: { label: 'دیباگ', cls: 'gray' }, }; const LEVEL_FILTER_OPTIONS = [ { value: '', label: 'همه سطوح' }, { value: 'error', label: 'خطا' }, { value: 'warning', label: 'اخطار' }, { value: 'critical', label: 'بحرانی' }, ]; type Tab = 'logs' | 'settings'; export default function LogsPage() { const [tab, setTab] = useState('logs'); const [page, setPage] = useState(1); const [level, setLevel] = useState(''); const [search, setSearch] = useState(''); const [viewLog, setViewLog] = useState(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: () => api.get>( `/api/v1/admin/logs?page=${page}&limit=${limit}` + (level ? `&level=${level}` : '') + (search ? `&search=${encodeURIComponent(search)}` : ''), ), }); const clearMutation = useMutation({ mutationFn: () => api.delete>('/api/v1/admin/logs'), onSuccess: () => { setConfirmClear(false); setPage(1); queryClient.invalidateQueries({ queryKey: ['admin-logs'] }); }, }); const columns: Column[] = [ { key: 'created_at', header: 'زمان', render: (l) => formatDateTime(l.created_at) }, { key: 'level', header: 'سطح', render: (l) => { const meta = LEVEL_META[l.level] ?? { label: l.level, cls: 'gray' }; return {meta.label}; }, }, { key: 'message', header: 'پیام', render: (l) => ( ), }, { key: 'path', header: 'مسیر', render: (l) => {l.path ?? '—'} }, ]; return (

لاگ‌ها

رویدادهای ثبت‌شده‌ی سیستم (اخطار و بالاتر)
{tab === 'logs' && (
)}
{tab === 'logs' && (
{ setSearch(e.target.value); setPage(1); }} />
{ setLevel(v ? String(v) : ''); setPage(1); }} placeholder="همه سطوح" />
columns={columns} data={logsQuery.data?.data ?? []} loading={logsQuery.isLoading} emptyMessage="لاگی یافت نشد" />
)} {tab === 'settings' && } setViewLog(null)}> {viewLog && (
سطح: {LEVEL_META[viewLog.level]?.label ?? viewLog.level}
زمان: {formatDateTime(viewLog.created_at)}
{viewLog.path &&
مسیر: {viewLog.path}
}
{viewLog.message}
{viewLog.context && (
context
                  {viewLog.context}
                
)}
)}
clearMutation.mutate()} onCancel={() => setConfirmClear(false)} />
); } function RetentionSettings() { const queryClient = useQueryClient(); const [days, setDays] = useState(''); const [touched, setTouched] = useState(false); const settingsQuery = useQuery({ queryKey: ['admin-settings'], queryFn: () => api.get>>('/api/v1/admin/settings'), }); const current = String(settingsQuery.data?.data?.log_retention_days ?? ''); const value = touched ? days : current; const saveMutation = useMutation({ mutationFn: (retentionDays: string) => api.patch>>('/api/v1/admin/settings', { log_retention_days: retentionDays, }), onSuccess: () => { setTouched(false); queryClient.invalidateQueries({ queryKey: ['admin-settings'] }); }, }); return (
{ setDays(digitsOnly(e.target.value)); setTouched(true); }} style={{ maxWidth: 200 }} />
لاگ‌های قدیمی‌تر از این مدت به‌صورت روزانه به‌صورت خودکار حذف می‌شوند. مقدار ۰ یعنی نگهداری نامحدود.
); }