// static/js/admin.Analytics.jsx function AdminAnalytics({ token }) { const [tests, setTests] = useState([]); const [selectedTestId, setSelectedTestId] = useState(""); const [detail, setDetail] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); // Загружаем список тестов для выбора useEffect(() => { let cancelled = false; const loadTests = async () => { try { setError(""); const data = await API.adminGetTests(token); if (!cancelled) { setTests(data); } } catch (e) { console.error(e); if (!cancelled) { setError("Не удалось загрузить список тестов"); } } }; loadTests(); return () => { cancelled = true; }; }, [token]); const loadDetail = async (testId) => { if (!testId) { setDetail(null); return; } setLoading(true); setError(""); try { const data = await API.adminGetTestAnalytics(token, testId); setDetail(data); } catch (e) { console.error(e); setError(e.message || "Ошибка при загрузке аналитики"); } finally { setLoading(false); } }; const handleSelectChange = (e) => { const val = e.target.value; setSelectedTestId(val); if (val) { loadDetail(val); } else { setDetail(null); } }; // Скачивание CSV по выбранному тесту const downloadCsv = async () => { if (!selectedTestId) { alert("Сначала выберите тест"); return; } try { const blob = await API.adminDownloadTestCsv(token, selectedTestId); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = detail ? `test_${detail.test.id}_results.csv` : "results.csv"; a.click(); window.URL.revokeObjectURL(url); } catch (e) { console.error(e); alert(e.message || "Ошибка при скачивании отчета"); } }; // Безопасно достаем список тех, кто не проходил тест const neverAttempted = detail && Array.isArray(detail.never_attempted_users) ? detail.never_attempted_users : []; return (
Выберите тест, чтобы посмотреть статистику и выгрузить отчет в Excel.
Отчеты строятся только по завершенным попыткам (статус "completed").
ID теста: {detail.test.id}
| Сотрудник | Попыток | 1-я успешная попытка | Лучшая % | Последняя % | Прошел в последней | Суммарное время (мин) | |
|---|---|---|---|---|---|---|---|
| {u.name} | {u.email} | {u.attempts} | {u.first_pass_attempt ? `${u.first_pass_attempt}-я` : "не прошел"} | {u.best_percent}% | {u.last_percent}% | {u.last_passed ? ( ✅ прошел ) : ( ❌ не прошел )} | {u.total_time_minutes} |
| Сотрудник | |
|---|---|
| {u.name} | {u.email} |
| Вопрос | Ответов | Правильных | % правильных | Среднее время (сек) |
|---|---|---|---|---|
| {q.text} | {q.total_answers} | {q.correct_answers} | {q.correct_percent}% | {q.avg_time_seconds} |