/
Pitonx
/
MyFinance
Обзор
Документация
Войти
/
Pitonx
/
MyFinance
Код
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/components/LogViewer.js
128 строк
5 KB
Сергей
v10.8.23-11.2.1. Вошедшие изменения см. в CHANGELOG.md
13 май 2026, 11:44
13 май 2026, 11:44
a3c0783
Код
Авторство
О чём код?
import React, { useState, useEffect } from 'react'; const API_BASE = (() => { try { return require('../config/apiConfig').default.API_BASE; } catch { return process.env.REACT_APP_API_URL || 'http://localhost:8000'; } })(); const LEVEL_COLORS = { ERROR: '#c62828', WARNING: '#f57c00', INFO: '#1565c0', DEBUG: '#757575', UNKNOWN: '#9e9e9e', }; export default function LogViewer() { const [lines, setLines] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [levelFilter, setLevelFilter] = useState(''); const [linesCount, setLinesCount] = useState(200); const fetchLogs = async () => { setLoading(true); setError(null); try { const token = localStorage.getItem('auth_token'); const params = new URLSearchParams(); params.set('lines', linesCount); if (levelFilter) params.set('level', levelFilter); const res = await fetch(`${API_BASE}/api/v1/admin/logs/?${params.toString()}`, { headers: token ? { 'Authorization': `Token ${token}` } : {}, }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || `HTTP ${res.status}`); setLines(data.lines || []); } catch (err) { setError(err.message); } finally { setLoading(false); } }; useEffect(() => { fetchLogs(); }, [levelFilter, linesCount]); return ( <div style={{ padding: 16 }}> <h3 style={{ margin: '0 0 16px', fontSize: 16 }}>Логи ошибок</h3> <div style={{ display: 'flex', gap: 12, alignItems: 'center', marginBottom: 16, flexWrap: 'wrap' }}> <label style={{ fontSize: 13 }}> Уровень: <select value={levelFilter} onChange={e => setLevelFilter(e.target.value)} style={{ marginLeft: 6, fontSize: 13 }}> <option value="">Все</option> <option value="ERROR">ERROR</option> <option value="WARNING">WARNING</option> <option value="INFO">INFO</option> <option value="DEBUG">DEBUG</option> </select> </label> <label style={{ fontSize: 13 }}> Строк: <select value={linesCount} onChange={e => setLinesCount(e.target.value)} style={{ marginLeft: 6, fontSize: 13 }}> <option value={50}>50</option> <option value={100}>100</option> <option value={200}>200</option> <option value={500}>500</option> <option value={1000}>1000</option> </select> </label> <button className="btn" onClick={fetchLogs} disabled={loading} style={{ fontSize: 12 }}> {loading ? '⏳' : '🔁 Обновить'} </button> <span style={{ fontSize: 12, color: '#888' }}>Всего: {lines.length}</span> </div> {error && ( <div style={{ padding: '8px 12px', background: '#ffebee', border: '1px solid #ef9a9a', borderRadius: 4, color: '#c62828', fontSize: 13, marginBottom: 12 }}> Ошибка: {error} </div> )} {lines.length === 0 && !loading && !error && ( <p style={{ fontSize: 13, color: '#888' }}>Логов нет.</p> )} {lines.length > 0 && ( <div style={{ maxHeight: 'calc(100vh - 220px)', overflow: 'auto', border: '1px solid #ddd', borderRadius: 4 }}> <table className="data-table" style={{ fontSize: 12 }}> <thead> <tr> <th style={{ width: 140 }}>Время</th> <th style={{ width: 70 }}>Уровень</th> <th style={{ width: 100 }}>Модуль</th> <th>Сообщение</th> </tr> </thead> <tbody> {lines.map((line, i) => ( <tr key={i}> <td style={{ whiteSpace: 'nowrap', color: '#666' }}>{line.timestamp || '—'}</td> <td> <span style={{ background: LEVEL_COLORS[line.level] || '#9e9e9e', color: '#fff', padding: '2px 6px', borderRadius: 3, fontSize: 10, fontWeight: 600, }}> {line.level} </span> </td> <td style={{ color: '#666' }}>{line.module || '—'}</td> <td style={{ wordBreak: 'break-word' }}>{line.message}</td> </tr> ))} </tbody> </table> </div> )} </div> ); }