// ================================================ // SGE — Error System + Sound Engine // ================================================ const ERRORS = { 'err-001': 'Conexiune Firebase eșuată. Verifică internetul.', 'err-002': 'Elevul nu a fost găsit în baza de date.', 'err-003': 'Cod VPass incorect.', 'err-004': 'Cont neînregistrat. Apasă ÎNREGISTRARE.', 'err-005': 'Cerere de înregistrare deja în așteptare.', 'err-006': 'Cod de confirmare expirat. Solicită unul nou.', 'err-007': 'Cod de confirmare incorect.', 'err-008': 'Parola trebuie să aibă minimum 6 caractere.', 'err-009': 'Upload eșuat — eroare rețea.', 'err-010': 'Fișierul depășește limita de 50MB.', 'err-011': 'Niciun fișier selectat.', 'err-012': 'Selectează o materie înainte de upload.', 'err-013': 'Sesiune expirată. Reconectează-te.', 'err-014': 'Acces neautorizat.', 'err-015': 'Timeout server. Încearcă din nou.', 'err-016': 'B2 autentificare eșuată.', 'err-017': 'B2 upload URL indisponibil.', 'err-018': 'Listare fișiere eșuată.', 'err-019': 'Ștergere fișier eșuată.', 'err-020': 'Parolă administrator incorectă.', 'err-021': 'Câmpuri obligatorii incomplete.', 'err-022': 'PIN-ul trebuie să fie exact 6 cifre.', 'err-023': 'Elev deja înregistrat. Folosește login normal.', 'err-024': 'Cerere respinsă de administrator.', 'err-025': 'Eroare scriere Firestore.', 'err-026': 'Eroare citire Firestore.', 'err-027': 'Eroare rețea generală.', 'err-028': 'Notificare negăsită.', 'err-029': 'Codul de confirmare trebuie să fie 4 cifre.', 'err-030': 'Sesiune signup expirată. Revino la login.', }; // Warm ambient error sound — 220Hz sine wave, soft fade let _audioCtx = null; function playErrorSound() { try { if (!_audioCtx) _audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const ctx = _audioCtx; // Two oscillators for warm, mysterious feel const osc1 = ctx.createOscillator(); const osc2 = ctx.createOscillator(); const gain1 = ctx.createGain(); const gain2 = ctx.createGain(); const master = ctx.createGain(); osc1.type = 'sine'; osc1.frequency.setValueAtTime(196, ctx.currentTime); // G3 osc1.frequency.exponentialRampToValueAtTime(174, ctx.currentTime + 0.6); // F3 osc2.type = 'sine'; osc2.frequency.setValueAtTime(293, ctx.currentTime); // D4 — minor feel osc2.frequency.exponentialRampToValueAtTime(261, ctx.currentTime + 0.6); // Envelope: soft attack, slow decay master.gain.setValueAtTime(0, ctx.currentTime); master.gain.linearRampToValueAtTime(0.12, ctx.currentTime + 0.08); master.gain.linearRampToValueAtTime(0.06, ctx.currentTime + 0.35); master.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.9); gain1.gain.value = 0.7; gain2.gain.value = 0.3; osc1.connect(gain1); gain1.connect(master); osc2.connect(gain2); gain2.connect(master); master.connect(ctx.destination); osc1.start(ctx.currentTime); osc2.start(ctx.currentTime); osc1.stop(ctx.currentTime + 1); osc2.stop(ctx.currentTime + 1); } catch(e) { /* silently fail on browsers without AudioContext */ } } // Show error with code, sound, and description function showError(containerId, code, extraMsg) { playErrorSound(); const el = document.getElementById(containerId); if (!el) return; const msg = ERRORS[code] || 'Eroare necunoscută.'; el.innerHTML = `${code} ${msg}${extraMsg ? ' — ' + extraMsg : ''}`; el.classList.add('show'); // ── Trimite eroarea automat la logurile adminului ── try { const vpass = sessionStorage.getItem('vs_vpass') || sessionStorage.getItem('vs_uid') || '?'; const role = sessionStorage.getItem('vs_role') || '?'; const desc = `${code} — ${msg}${extraMsg ? ' — ' + extraMsg : ''}`; fetch('/api/log', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ tip: 'eroare', vpass, role, cod: code, desc }) }).catch(() => {}); } catch(e) {} } function hideError(containerId) { const el = document.getElementById(containerId); if (el) el.classList.remove('show'); }