Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { Lock, LogIn, ShieldAlert, UserCheck } from 'lucide-react'; | |
| import { Radar } from 'react-chartjs-2'; | |
| export default function ModeratorTab({ | |
| twitchConfigured, | |
| auth, | |
| handleLogin, | |
| TWITCH_CHANNEL, | |
| modProfiles, | |
| selectedMod, | |
| setSelectedMod | |
| }) { | |
| return ( | |
| <div className="tab-content"> | |
| {twitchConfigured && !auth.loggedIn ? ( | |
| <div className="login-overlay"> | |
| <div className="login-card"> | |
| <Lock size={48} style={{ color: 'var(--color-brand)', marginBottom: '1.5rem' }} /> | |
| <h2 className="login-title">Доступ Ограничен</h2> | |
| <p className="login-description"> | |
| Лог действий модераторов и статистика банов/удалений доступны только стримеру и официальным модераторам канала **winx_prinx**. | |
| Пожалуйста, авторизуйтесь через Twitch для проверки прав. | |
| </p> | |
| <button className="btn" onClick={handleLogin}> | |
| <LogIn size={16} /> Войти через Twitch | |
| </button> | |
| </div> | |
| </div> | |
| ) : twitchConfigured && (auth.user?.role !== 'streamer' && auth.user?.role !== 'moderator' && auth.user?.role !== 'admin') ? ( | |
| <div className="status-msg"> | |
| <ShieldAlert size={48} className="status-msg-icon" /> | |
| <h2>Недостаточно прав</h2> | |
| <p>Вы успешно вошли как <strong>{auth.user?.displayName}</strong>, но вы не являетесь модератором канала {TWITCH_CHANNEL}.</p> | |
| </div> | |
| ) : ( | |
| <div> | |
| {modProfiles.length <= 2 && ( | |
| <div style={{ background: 'rgba(145, 70, 255, 0.1)', border: '1px solid rgba(145, 70, 255, 0.3)', borderRadius: '8px', padding: '1rem', marginBottom: '1.5rem', display: 'flex', gap: '1rem', alignItems: 'center' }}> | |
| <ShieldAlert size={24} style={{ color: 'var(--color-brand)' }} /> | |
| <div> | |
| <h4 style={{ margin: '0 0 0.25rem 0', color: 'var(--color-text-main)', fontSize: '0.95rem' }}>Данные накапливаются</h4> | |
| <p style={{ margin: 0, fontSize: '0.85rem', color: 'var(--color-text-muted)' }}>Информация о модераторах появляется по мере их активности в выбранном стриме.</p> | |
| </div> | |
| </div> | |
| )} | |
| {/* TOP 3 PODIUM */} | |
| <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: '1.5rem', marginBottom: '2rem' }}> | |
| {modProfiles.slice(0, 3).map((mod, idx) => { | |
| const trophyColor = idx === 0 ? '#FFD700' : idx === 1 ? '#C0C0C0' : '#CD7F32'; | |
| const glowStyle = { | |
| border: `1px solid ${trophyColor}40`, | |
| boxShadow: `0 0 15px ${trophyColor}12`, | |
| position: 'relative', | |
| overflow: 'hidden' | |
| }; | |
| return ( | |
| <div key={idx} className="panel" style={glowStyle}> | |
| <div style={{ position: 'absolute', top: '-10px', right: '-10px', fontSize: '5rem', opacity: 0.08, color: trophyColor, fontWeight: 900 }}> | |
| #{idx + 1} | |
| </div> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}> | |
| <div style={{ | |
| width: '44px', | |
| height: '44px', | |
| borderRadius: '50%', | |
| background: 'var(--color-bg-base)', | |
| border: `2px solid ${trophyColor}`, | |
| display: 'flex', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| fontWeight: 700, | |
| fontSize: '1.1rem', | |
| color: trophyColor | |
| }}> | |
| {idx === 0 ? '👑' : idx === 1 ? '🥈' : '🥉'} | |
| </div> | |
| <div> | |
| <h4 style={{ margin: 0, fontSize: '1rem', fontWeight: 700 }}>{mod.moderator}</h4> | |
| <span style={{ fontSize: '0.7rem', color: 'var(--color-text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em' }}> | |
| {idx === 0 ? 'Глава патруля' : idx === 1 ? 'Старший мод' : 'Защитник чата'} | |
| </span> | |
| </div> | |
| </div> | |
| <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1.25rem', fontSize: '0.8rem' }}> | |
| <div> | |
| <div style={{ color: 'var(--color-text-muted)' }}>Действий</div> | |
| <div style={{ fontSize: '1.1rem', fontWeight: 700, color: 'var(--color-text-accent)' }}>{mod.total_actions}</div> | |
| </div> | |
| <div> | |
| <div style={{ color: 'var(--color-text-muted)' }}>Ср. Реакция</div> | |
| <div style={{ fontSize: '1.1rem', fontWeight: 700, color: '#00F5D4' }}> | |
| {mod.reaction_time_avg ? `${mod.reaction_time_avg}с` : '—'} | |
| </div> | |
| </div> | |
| <div> | |
| <div style={{ color: 'var(--color-text-muted)' }}>КПД Активности</div> | |
| <div style={{ fontSize: '1.1rem', fontWeight: 700, color: '#A370F7' }}>{mod.scores.activity}%</div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| <div className="grid-2col" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', gap: '1.5rem', alignItems: 'start' }}> | |
| {/* Left: Mod List */} | |
| <div className="panel"> | |
| <div className="panel-header"> | |
| <h3 className="panel-title"><ShieldAlert size={20} /> Рейтинг модераторов</h3> | |
| </div> | |
| {modProfiles.length === 0 ? ( | |
| <div className="status-msg"> | |
| <ShieldAlert size={40} className="status-msg-icon" /> | |
| <p>Модераторы еще не совершали действий в этом периоде</p> | |
| </div> | |
| ) : ( | |
| <div className="table-wrapper"> | |
| <table className="data-table"> | |
| <thead> | |
| <tr> | |
| <th>Ранг</th> | |
| <th>Никнейм</th> | |
| <th>Действий</th> | |
| <th>Ср. Реакция</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {modProfiles.map((mod, idx) => ( | |
| <tr | |
| key={idx} | |
| onClick={() => setSelectedMod(mod)} | |
| style={{ | |
| cursor: 'pointer', | |
| background: selectedMod && selectedMod.moderator === mod.moderator ? 'rgba(163, 112, 247, 0.08)' : 'transparent', | |
| borderLeft: selectedMod && selectedMod.moderator === mod.moderator ? '3px solid var(--color-brand)' : 'none' | |
| }} | |
| > | |
| <td>#{idx + 1}</td> | |
| <td style={{ fontWeight: 600 }}>{mod.moderator}</td> | |
| <td>{mod.total_actions}</td> | |
| <td style={{ color: '#00F5D4', fontWeight: 600 }}>{mod.reaction_time_avg ? `${mod.reaction_time_avg}с` : '—'}</td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| )} | |
| </div> | |
| {/* Right: Mod Dossier */} | |
| <div className="panel"> | |
| <div className="panel-header"> | |
| <h3 className="panel-title"><UserCheck size={20} /> Личное досье модератора</h3> | |
| </div> | |
| {!selectedMod ? ( | |
| <div className="status-msg"> | |
| <UserCheck size={40} className="status-msg-icon" /> | |
| <p>Выберите модератора слева для просмотра досье</p> | |
| </div> | |
| ) : ( | |
| <div> | |
| <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}> | |
| <div> | |
| <h3 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-brand)' }}>{selectedMod.moderator}</h3> | |
| <p style={{ margin: 0, fontSize: '0.75rem', color: 'var(--color-text-muted)' }}>Анализ стиля модерирования и характеристик</p> | |
| </div> | |
| </div> | |
| {/* Radar chart */} | |
| <div style={{ height: '280px', display: 'flex', justifyContent: 'center', marginBottom: '1.25rem' }}> | |
| <Radar | |
| data={{ | |
| labels: ['Скорость', 'Активность', 'Жесткость', 'Внимательность'], | |
| datasets: [{ | |
| data: [ | |
| selectedMod.scores.speed, | |
| selectedMod.scores.activity, | |
| selectedMod.scores.harshness, | |
| selectedMod.scores.watchfulness | |
| ], | |
| backgroundColor: 'rgba(163, 112, 247, 0.2)', | |
| borderColor: '#A370F7', | |
| borderWidth: 2, | |
| pointBackgroundColor: '#A370F7', | |
| pointBorderColor: '#FFF', | |
| pointHoverBackgroundColor: '#FFF', | |
| pointHoverBorderColor: '#A370F7' | |
| }] | |
| }} | |
| options={{ | |
| plugins: { legend: { display: false } }, | |
| scales: { | |
| r: { | |
| angleLines: { color: '#2F2F35' }, | |
| grid: { color: '#2F2F35' }, | |
| pointLabels: { color: '#ADADB8', font: { size: 10, weight: 'bold' } }, | |
| ticks: { display: false }, | |
| min: 0, | |
| max: 100 | |
| } | |
| } | |
| }} | |
| /> | |
| </div> | |
| {/* Actions distribution */} | |
| <div style={{ marginBottom: '1rem' }}> | |
| <h4 style={{ margin: '0 0 0.5rem 0', fontSize: '0.85rem', fontWeight: 600 }}>Распределение наказаний:</h4> | |
| <div style={{ height: '16px', display: 'flex', borderRadius: '4px', overflow: 'hidden', background: 'var(--color-bg-base)' }}> | |
| {selectedMod.bans_count > 0 && ( | |
| <div | |
| style={{ width: `${(selectedMod.bans_count / selectedMod.total_actions) * 100}%`, background: '#FF0055', height: '100%' }} | |
| title={`Баны: ${selectedMod.bans_count}`} | |
| /> | |
| )} | |
| {selectedMod.timeouts_count > 0 && ( | |
| <div | |
| style={{ width: `${(selectedMod.timeouts_count / selectedMod.total_actions) * 100}%`, background: '#FFAA00', height: '100%' }} | |
| title={`Муты/Таймауты: ${selectedMod.timeouts_count}`} | |
| /> | |
| )} | |
| {selectedMod.deletions_count > 0 && ( | |
| <div | |
| style={{ width: `${(selectedMod.deletions_count / selectedMod.total_actions) * 100}%`, background: '#00AAFF', height: '100%' }} | |
| title={`Удаления сообщений: ${selectedMod.deletions_count}`} | |
| /> | |
| )} | |
| {selectedMod.unbans_count > 0 && ( | |
| <div | |
| style={{ width: `${(selectedMod.unbans_count / selectedMod.total_actions) * 100}%`, background: '#00FF88', height: '100%' }} | |
| title={`Разбаны: ${selectedMod.unbans_count}`} | |
| /> | |
| )} | |
| </div> | |
| {/* Legend */} | |
| <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem 1.25rem', marginTop: '0.5rem', fontSize: '0.7rem', color: 'var(--color-text-muted)' }}> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}> | |
| <span style={{ display: 'inline-block', width: '6px', height: '6px', borderRadius: '50%', background: '#FF0055' }}></span> | |
| Баны ({selectedMod.bans_count}) | |
| </div> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}> | |
| <span style={{ display: 'inline-block', width: '6px', height: '6px', borderRadius: '50%', background: '#FFAA00' }}></span> | |
| Муты ({selectedMod.timeouts_count}) | |
| </div> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}> | |
| <span style={{ display: 'inline-block', width: '6px', height: '6px', borderRadius: '50%', background: '#00AAFF' }}></span> | |
| Удаления ({selectedMod.deletions_count}) | |
| </div> | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}> | |
| <span style={{ display: 'inline-block', width: '6px', height: '6px', borderRadius: '50%', background: '#00FF88' }}></span> | |
| Разбаны ({selectedMod.unbans_count}) | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |