| |
| |
| |
|
|
| 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.', |
| }; |
|
|
| |
| let _audioCtx = null; |
| function playErrorSound() { |
| try { |
| if (!_audioCtx) _audioCtx = new (window.AudioContext || window.webkitAudioContext)(); |
| const ctx = _audioCtx; |
|
|
| |
| 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); |
| osc1.frequency.exponentialRampToValueAtTime(174, ctx.currentTime + 0.6); |
|
|
| osc2.type = 'sine'; |
| osc2.frequency.setValueAtTime(293, ctx.currentTime); |
| osc2.frequency.exponentialRampToValueAtTime(261, ctx.currentTime + 0.6); |
|
|
| |
| 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) { } |
| } |
|
|
| |
| function showError(containerId, code, extraMsg) { |
| playErrorSound(); |
| const el = document.getElementById(containerId); |
| if (!el) return; |
| const msg = ERRORS[code] || 'Eroare necunoscută.'; |
| el.innerHTML = `<span class="err-code">${code}</span> ${msg}${extraMsg ? ' — ' + extraMsg : ''}`; |
| el.classList.add('show'); |
|
|
| |
| 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'); |
| } |
|
|