| <!DOCTYPE html> |
| <html lang="ro"> |
| <head> |
| <link rel="icon" type="image/svg+xml" href="favicon.svg"> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> |
| <title>SGE | Loguri</title> |
| <link rel="stylesheet" href="style.css"> |
| <style> |
| .log-row { |
| display:grid; grid-template-columns:80px 90px 1fr 90px; |
| gap:10px; padding:9px 12px; |
| border-bottom:1px solid rgba(255,255,255,0.03); |
| align-items:center; font-size:11px; |
| transition:background 0.15s; |
| } |
| .log-row:hover { background:rgba(255,255,255,0.02); } |
| .log-row:last-child { border-bottom:none; } |
| |
| |
| .log-type { |
| font-size:8px; letter-spacing:2px; padding:2px 7px; |
| border:1px solid; display:inline-block; white-space:nowrap; |
| } |
| .log-type.login { border-color:rgba(60,120,80,0.5); color:rgba(80,200,120,0.8); } |
| .log-type.logout { border-color:rgba(255,255,255,0.12); color:var(--white-dim); } |
| .log-type.upload { border-color:rgba(60,80,180,0.5); color:rgba(100,140,255,0.8); } |
| .log-type.err_pass { border-color:rgba(180,60,60,0.5); color:rgba(220,100,100,0.8); } |
| .log-type.cleanup { border-color:rgba(180,140,40,0.5); color:rgba(220,180,60,0.8); } |
| .log-type.signup { border-color:rgba(140,60,180,0.4); color:rgba(180,100,220,0.8); } |
| .log-type.default { border-color:rgba(255,255,255,0.08); color:var(--white-faint); } |
| |
| |
| .online-card { |
| background:var(--glass); border:1px solid var(--glass-border); |
| padding:12px 14px; margin-bottom:8px; |
| display:flex; align-items:center; gap:10px; transition:border-color 0.2s; |
| } |
| .online-card:hover { border-color:rgba(255,255,255,0.15); } |
| .live-dot { |
| width:8px; height:8px; border-radius:50%; flex-shrink:0; |
| animation:blink 2s ease-in-out infinite; |
| } |
| .live-dot.elev { background:#4a8a4a; box-shadow:0 0 6px rgba(74,138,74,0.8); } |
| .live-dot.profesor { background:rgba(100,140,255,0.9); box-shadow:0 0 6px rgba(100,140,255,0.7); } |
| .oc-name { font-size:13px; } |
| .oc-vpass { font-size:9px; color:var(--white-dim); letter-spacing:2px; margin-top:2px; } |
| .oc-role { margin-left:auto; font-size:8px; letter-spacing:2px; color:var(--white-dim); } |
| |
| |
| .online-counter { |
| display:flex; gap:12px; margin-bottom:20px; |
| } |
| .oc-box { |
| flex:1; background:var(--glass); border:1px solid var(--glass-border); |
| padding:14px 12px; text-align:center; |
| } |
| .oc-num { font-family:'Cormorant Garamond',serif; font-size:36px; font-weight:600; } |
| .oc-lbl { font-size:8px; letter-spacing:3px; color:var(--white-dim); margin-top:2px; } |
| |
| |
| .log-empty { padding:30px; text-align:center; font-size:11px; color:var(--white-dim); letter-spacing:1px; } |
| |
| |
| .filter-row { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:12px; } |
| .filter-btn { |
| background:transparent; border:1px solid var(--glass-border); |
| color:var(--white-dim); padding:5px 10px; |
| font-family:'DM Mono',monospace; font-size:9px; letter-spacing:1px; |
| cursor:pointer; transition:all 0.2s; |
| } |
| .filter-btn:hover { border-color:rgba(255,255,255,0.3); color:var(--white); } |
| .filter-btn.active { background:var(--white); color:var(--black); border-color:var(--white); } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="topbar"> |
| <a href="admin-dashboard.html" class="topbar-logo" style="text-decoration:none;"> |
| <img src="logo.svg" alt="VS"> |
| <span class="topbar-name">SGE</span> |
| </a> |
| <div class="topbar-divider"></div> |
| <span class="topbar-section">LOGURI</span> |
| <div class="topbar-right"> |
| <div class="online-dot"></div> |
| <span class="role-tag" style="color:var(--white);border-color:rgba(255,255,255,0.2);">ADMIN</span> |
| <button class="btn-ghost" onclick="window.location.href='admin-dashboard.html'" style="font-size:9px;">← Admin</button> |
| </div> |
| </div> |
|
|
| <div class="main"> |
|
|
| |
| <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;" class="fade-in"> |
| <div class="label" style="margin:0;">Istoric activitate</div> |
| <button class="btn-ghost" onclick="loadLogs()" style="font-size:9px;">↻ Reîncarcă</button> |
| </div> |
|
|
| <div class="filter-row fade-in-2"> |
| <button class="filter-btn active" onclick="setFilter('all',this)">TOATE</button> |
| <button class="filter-btn" onclick="setFilter('login',this)">LOGIN</button> |
| <button class="filter-btn" onclick="setFilter('upload',this)">UPLOAD</button> |
| <button class="filter-btn" onclick="setFilter('err_pass',this)">PAROLA GRESITA</button> |
| <button class="filter-btn" onclick="setFilter('signup',this)">INREGISTRARE</button> |
| <button class="filter-btn" onclick="setFilter('cleanup',this)">CLEANUP</button> |
| </div> |
|
|
| <div class="data-table fade-in-3"> |
| <div class="dt-head" style="grid-template-columns:80px 90px 1fr 90px;"> |
| <div>TIP</div><div>CINE</div><div>DETALII</div><div>CAND</div> |
| </div> |
| <div id="logs-list"> |
| <div class="log-empty">Se incarca logurile...</div> |
| </div> |
| </div> |
|
|
| <footer class="footer"> |
| <div class="footer-top"><img src="logo.svg" alt=""><span>SGE</span></div> |
| <div class="footer-divider"></div> |
| <div class="footer-meta">93.117.161.226 · Mîndrești, Telenești, Moldova</div> |
| <div class="footer-copy">© 2026 Victor Roșca — v3.0</div> |
| </footer> |
| </div> |
|
|
| <script> |
| |
| if (sessionStorage.getItem('vs_role') !== 'admin') { |
| window.location.href = 'index.html'; |
| } |
| |
| let _allLogs = []; |
| let _filter = 'all'; |
| |
| function tipClass(tip) { |
| if (!tip) return 'default'; |
| if (tip === 'login') return 'login'; |
| if (tip === 'logout') return 'logout'; |
| if (tip === 'upload') return 'upload'; |
| if (tip === 'err_pass') return 'err_pass'; |
| if (tip.includes('signup') || tip.includes('reset')) return 'signup'; |
| if (tip.includes('cleanup')) return 'cleanup'; |
| return 'default'; |
| } |
| function tipLabel(tip) { |
| const map = {'login':'LOGIN','logout':'LOGOUT','upload':'UPLOAD','err_pass':'PAROLA GRESIT','signup_request':'SIGNUP','reset_request':'RESET','cleanup_delete':'CLEANUP','signup':'SIGNUP'}; |
| return map[tip] || (tip||'LOG').toUpperCase().slice(0,10); |
| } |
| function tsToStr(ts) { |
| if (!ts) return '—'; |
| const d = new Date(parseInt(ts)*1000); |
| return d.toLocaleString('ro',{day:'2-digit',month:'short',hour:'2-digit',minute:'2-digit'}); |
| } |
| async function loadLogs() { |
| try { |
| const r = await fetch('/api/logs'); |
| const data = await r.json(); |
| _allLogs = data.logs || []; |
| renderLogs(); |
| } catch(e) { |
| document.getElementById('logs-list').innerHTML = '<div class="log-empty">Eroare la incarcarea logurilor.</div>'; |
| } |
| } |
| function renderLogs() { |
| const el = document.getElementById('logs-list'); |
| el.innerHTML = ''; |
| const filtered = _filter==='all' ? _allLogs : _allLogs.filter(l=>tipClass(l.tip)===_filter||l.tip===_filter); |
| if (!filtered.length) { el.innerHTML='<div class="log-empty">Niciun log disponibil.</div>'; return; } |
| filtered.forEach(l => { |
| const row = document.createElement('div'); |
| row.className = 'log-row'; |
| let detalii = ''; |
| if (l.tip==='upload') detalii=`<strong>${l.elevVpass||l.elevNume||'?'}</strong> a incarcat <em>${l.fisier||'?'}</em> la ${l.materie||'?'}`; |
| else if (l.tip==='login') detalii=`<strong>${l.vpass||l.name||'?'}</strong> s-a conectat (${l.role||'?'})`; |
| else if (l.tip==='logout') detalii=`<strong>${l.vpass||'?'}</strong> s-a deconectat`; |
| else if (l.tip==='err_pass') detalii=`Parola gresita pentru <strong>${l.vpass||'?'}</strong>`; |
| else if (l.tip==='signup_request'||l.tip==='signup') detalii=`Cerere inregistrare: <strong>${l.elevNume||l.elevVpass||'?'}</strong>`; |
| else if (l.tip==='reset_request') detalii=`Reset parola: <strong>${l.elevNume||l.elevVpass||'?'}</strong>`; |
| else if (l.tip==='cleanup_delete') detalii=`Fisier expirat sters: <em>${l.fisier||l.fileId||'?'}</em>`; |
| else detalii=JSON.stringify(l).slice(0,80); |
| row.innerHTML=` |
| <div><span class="log-type ${tipClass(l.tip)}">${tipLabel(l.tip)}</span></div> |
| <div style="font-size:10px;color:var(--white-dim);letter-spacing:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${l.elevVpass||l.vpass||'sistem'}</div> |
| <div style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${detalii}</div> |
| <div style="font-size:9px;color:var(--white-faint);letter-spacing:1px;">${tsToStr(l.ts)}</div>`; |
| el.appendChild(row); |
| }); |
| } |
| function setFilter(f,btn) { |
| _filter=f; |
| document.querySelectorAll('.filter-btn').forEach(b=>b.classList.remove('active')); |
| btn.classList.add('active'); |
| renderLogs(); |
| } |
| loadLogs(); |
| </script> |
| </body> |
| </html> |
|
|