Spaces:
Sleeping
Sleeping
| // ================================================ | |
| // VSERVERS — 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 = `<span class="err-code">${code}</span> ${msg}${extraMsg ? ' — ' + extraMsg : ''}`; | |
| el.classList.add('show'); | |
| } | |
| function hideError(containerId) { | |
| const el = document.getElementById(containerId); | |
| if (el) el.classList.remove('show'); | |
| } | |