'use strict'; /* admin → user-detail (Phase 6) — deep page for a single user (#users/:id). * * Replaces the legacy `.user-panel` overlay. Lazy-init via * AdminSections['user-detail'].init(id, subTab) * where subTab ∈ 'overview' | 'sessions' | 'classes' | 'audit'. * * Reuses existing user-related modals (openEditUserModal, openUserPermsModal, * etc.) — they live in sections/users.js and operate on `window.activeUid`, * which we set before opening any of them. */ (function () { 'use strict'; /* ── one-time CSS injection ── */ function ensureUdStyles() { if (document.getElementById('user-detail-style')) return; const s = document.createElement('style'); s.id = 'user-detail-style'; s.textContent = ` .ud-wrap { padding: 4px 2px 24px; } .ud-back { display:inline-flex; align-items:center; gap:6px; font-size:0.82rem; color:var(--text-3); text-decoration:none; padding:6px 10px; border-radius:8px; margin-bottom:16px; transition:background .12s, color .12s; cursor:pointer; background:transparent; border:0; font-family:inherit; } .ud-back:hover { background:rgba(155,93,229,.07); color:var(--violet); } .ud-back svg { width:14px; height:14px; } .ud-header { display:flex; align-items:flex-start; gap:20px; padding:24px 26px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); margin-bottom:20px; flex-wrap:wrap; } .ud-avatar { width:64px; height:64px; border-radius:18px; display:flex; align-items:center; justify-content:center; font-family:'Unbounded',sans-serif; font-size:1.1rem; font-weight:800; color:#fff; flex-shrink:0; } .ud-avatar.banned { filter:grayscale(1); opacity:.6; } .ud-id-block { flex:1; min-width:200px; } .ud-name { font-family:'Unbounded',sans-serif; font-size:1.25rem; font-weight:800; line-height:1.2; display:flex; align-items:center; gap:10px; flex-wrap:wrap; } .ud-name .ud-role-badge { font-size:0.7rem; padding:3px 9px; border-radius:var(--r-pill); font-weight:700; letter-spacing:.02em; vertical-align:middle; } .ud-name .ud-banned-tag { font-size:0.66rem; padding:2px 7px; border-radius:4px; background:rgba(239,68,68,.12); color:#EF4444; font-weight:700; } .ud-email { font-size:0.88rem; color:var(--text-3); margin-top:6px; } .ud-meta-row { display:flex; gap:18px; margin-top:10px; font-size:0.76rem; color:var(--text-3); flex-wrap:wrap; } .ud-meta-row strong { color:var(--text-2); font-weight:600; } .ud-actions { display:flex; flex-wrap:wrap; gap:6px; align-items:flex-start; margin-left:auto; } .ud-actions .btn-edit-q, .ud-actions .btn-del-q { white-space:nowrap; } .ud-tabs { display:flex; gap:2px; border-bottom:1px solid var(--border); margin-bottom:20px; overflow-x:auto; } .ud-tab-btn { background:transparent; border:0; padding:11px 18px; font-family:inherit; font-size:0.86rem; font-weight:600; color:var(--text-3); cursor:pointer; border-bottom:2px solid transparent; transition:color .12s, border-color .12s; white-space:nowrap; } .ud-tab-btn:hover { color:var(--text-2); } .ud-tab-btn.active { color:var(--violet); border-bottom-color:var(--violet); } .ud-tab-pane { display:none; } .ud-tab-pane.active { display:block; } .ud-stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:14px; margin-bottom:24px; } .ud-stat { padding:18px 18px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); } .ud-stat-val { font-family:'Unbounded',sans-serif; font-size:1.5rem; font-weight:800; line-height:1.1; } .ud-stat-val.pct-hi { color:var(--green); } .ud-stat-val.pct-mid { color:var(--amber); } .ud-stat-val.pct-lo { color:var(--pink); } .ud-stat-label { font-size:0.74rem; color:var(--text-3); font-weight:600; text-transform:uppercase; letter-spacing:.03em; margin-top:6px; } .ud-sess-list { display:flex; flex-direction:column; gap:6px; } .ud-sess-row { display:flex; align-items:center; gap:14px; padding:12px 16px; background:var(--surface); border:1px solid var(--border); border-radius:12px; cursor:pointer; transition:border-color .12s, background .12s; } .ud-sess-row:hover { border-color:rgba(155,93,229,.35); background:rgba(155,93,229,.04); } .ud-sess-pct { font-family:'Unbounded',sans-serif; font-weight:800; font-size:0.9rem; width:50px; text-align:center; padding:6px 0; border-radius:8px; } .ud-sess-pct.pct-hi { color:var(--green); background:rgba(16,185,129,.1); } .ud-sess-pct.pct-mid { color:var(--amber); background:rgba(255,179,71,.12); } .ud-sess-pct.pct-lo { color:var(--pink); background:rgba(241,91,181,.1); } .ud-sess-info { flex:1; min-width:0; } .ud-sess-subj { font-weight:600; font-size:0.9rem; } .ud-sess-meta { font-size:0.76rem; color:var(--text-3); margin-top:2px; } .ud-sess-score { font-weight:700; font-size:0.88rem; } .ud-sess-chev { color:var(--text-3); flex-shrink:0; } .ud-empty { padding:30px; text-align:center; color:var(--text-3); font-size:0.88rem; background:var(--surface); border:1px dashed var(--border); border-radius:var(--r-lg); } .ud-audit-list { display:flex; flex-direction:column; gap:6px; } .ud-audit-row { display:flex; gap:14px; padding:10px 14px; background:var(--surface); border:1px solid var(--border); border-radius:10px; font-size:0.84rem; align-items:center; flex-wrap:wrap; } .ud-audit-when { font-size:0.74rem; color:var(--text-3); min-width:140px; } .ud-audit-action { font-weight:700; font-size:0.78rem; } .ud-audit-detail { color:var(--text-3); font-size:0.78rem; flex:1; min-width:140px; overflow:hidden; text-overflow:ellipsis; } .ud-chart-card { padding:18px 20px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); margin-top:20px; } .ud-chart-title { font-size:0.78rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--text-3); margin-bottom:12px; } .ud-bars { display:flex; flex-direction:column; gap:8px; } .ud-bar-row { display:flex; align-items:center; gap:10px; } .ud-bar-name { font-size:0.84rem; min-width:120px; } .ud-bar-track { flex:1; height:18px; background:rgba(15,23,42,.06); border-radius:6px; overflow:hidden; position:relative; } .ud-bar-fill { height:100%; border-radius:6px; transition:width .3s; } .ud-bar-fill.pct-hi { background:var(--green); } .ud-bar-fill.pct-mid { background:var(--amber); } .ud-bar-fill.pct-lo { background:var(--pink); } .ud-bar-val { font-family:'Unbounded',sans-serif; font-size:0.82rem; font-weight:700; min-width:48px; text-align:right; } @media (max-width: 640px) { .ud-header { padding:18px 16px; gap:14px; } .ud-actions { margin-left:0; width:100%; } .ud-actions .btn-edit-q, .ud-actions .btn-del-q { font-size:0.78rem; padding:6px 10px; } .ud-sess-row { padding:10px 12px; gap:10px; } .ud-sess-meta { font-size:0.72rem; } } `; document.head.appendChild(s); } const ROLE_LABEL = { student:'Ученик', free_student:'Своб. ученик', teacher:'Учитель', admin:'Админ' }; const ROLE_BG = { admin: 'linear-gradient(135deg,#9B5DE5,#c084fc)', teacher: 'linear-gradient(135deg,#06D6E0,#9B5DE5)', free_student: 'linear-gradient(135deg,#10B981,#059669)', student: 'linear-gradient(135deg,#8898AA,#3D4F6B)', }; const ROLE_BADGE_BG = { admin: 'rgba(155,93,229,.14)', teacher: 'rgba(6,214,224,.14)', free_student: 'rgba(16,185,129,.14)', student: 'rgba(136,152,170,.14)', }; const ROLE_BADGE_FG = { admin: 'var(--violet)', teacher: '#05aab3', free_student: 'var(--green)', student: 'var(--text-2)', }; /* SVG icons */ const ICONS = { arrowLeft: '', chev: '', ban: '', }; /* State */ let _userId = null; let _userData = null; // last fetched user object let _sessions = []; // last fetched sessions array let _activeSubTab = 'overview'; /* ── Public init: called by admin.js dispatch ── */ async function init(id, subTab) { ensureUdStyles(); const newId = Number(id); if (!Number.isFinite(newId) || newId <= 0) { renderError('Некорректный ID пользователя'); return; } _activeSubTab = subTab || 'overview'; // Make user-related modal handlers (openEditUserModal etc.) work — they read window.activeUid. window.activeUid = newId; if (_userId === newId && _userData) { // Same user — just switch sub-tab without re-fetch renderShell(); switchSubTab(_activeSubTab, /*pushUrl*/ false); return; } _userId = newId; _userData = null; _sessions = []; renderLoading(); try { const data = await LS.adminGetUserSessions(newId); _userData = data.user; _sessions = Array.isArray(data.sessions) ? data.sessions : []; // Sync globals used by overlay-era modal helpers (still live in users.js). window.activeUid = newId; window.activeUserRole = _userData?.role || null; renderShell(); switchSubTab(_activeSubTab, /*pushUrl*/ false); } catch (e) { renderError(e.message || String(e)); } } function renderLoading() { const el = document.getElementById('user-detail-content'); if (!el) return; el.innerHTML = '