md / static /loguri.html
vsmdvic's picture
Upload 20 files
a322166 verified
<!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; }
/* Tip badge */
.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 pulse */
.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); }
/* Counter big */
.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 */
.log-empty { padding:30px; text-align:center; font-size:11px; color:var(--white-dim); letter-spacing:1px; }
/* Filtru butoane */
.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">
<!-- Loguri -->
<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;">&#8635; 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">&copy; 2026 Victor Roșca — v3.0</div>
</footer>
</div>
<script>
// Auth check
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>