md / static /signup.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 | Înregistrare</title>
<link rel="stylesheet" href="style.css">
<script src="errors.js"></script>
<style>
body { display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:100dvh; padding:20px 14px; }
.wrap { width:100%; max-width:380px; }
.steps { display:flex; align-items:center; justify-content:center; gap:0; margin-bottom:28px; }
.step { display:flex; flex-direction:column; align-items:center; gap:5px; }
.step-dot {
width:28px; height:28px; border-radius:50%;
border:1.5px solid rgba(255,255,255,0.15);
display:flex; align-items:center; justify-content:center;
font-size:10px; color:var(--white-dim); letter-spacing:0;
transition:all 0.4s ease;
}
.step-dot.active { border-color:var(--white); color:var(--white); background:rgba(255,255,255,0.08); }
.step-dot.done { border-color:rgba(60,120,60,0.6); background:rgba(20,50,20,0.4); color:#5a9a5a; }
.step-label { font-size:8px; letter-spacing:1px; color:var(--white-faint); white-space:nowrap; }
.step-label.active { color:var(--white-dim); }
.step-line { width:28px; height:1px; background:rgba(255,255,255,0.08); margin:0 4px; margin-bottom:14px; }
.vpass-card {
background:rgba(255,255,255,0.04);
backdrop-filter:blur(1px); -webkit-backdrop-filter:blur(1px);
border:1px solid rgba(255,255,255,0.09);
padding:18px 16px; margin-bottom:16px;
display:flex; align-items:center; gap:14px;
}
.vpass-card .vc-icon img { width:28px; height:28px; opacity:0.6; }
.vpass-card .vc-name { font-family:'Cormorant Garamond',serif; font-size:18px; font-weight:600; }
.vpass-card .vc-id { font-size:10px; color:var(--white-dim); letter-spacing:2px; margin-top:2px; }
.waiting-indicator {
display:flex; align-items:center; gap:10px;
padding:14px 16px; background:rgba(255,255,255,0.03);
border:1px solid rgba(255,255,255,0.07); margin-bottom:14px;
}
.pulse-dot {
width:8px; height:8px; border-radius:50%;
background:rgba(255,255,255,0.4);
animation:pulse 2s ease-in-out infinite; flex-shrink:0;
}
@keyframes pulse {
0%,100% { opacity:0.3; transform:scale(0.8); }
50% { opacity:1; transform:scale(1.2); }
}
.waiting-indicator .wi-text { font-size:11px; color:var(--white-dim); letter-spacing:1px; }
.waiting-indicator .wi-code { font-size:10px; color:var(--white-faint); margin-top:2px; }
/* Telefon input */
.phone-wrap { display:flex; gap:0; border:1px solid rgba(255,255,255,0.12); }
.phone-prefix {
background:rgba(255,255,255,0.06); border:none; border-right:1px solid rgba(255,255,255,0.1);
color:var(--white); padding:10px 12px; font-family:'DM Mono',monospace;
font-size:12px; letter-spacing:1px; flex-shrink:0; display:flex; align-items:center;
}
.phone-input {
flex:1; background:transparent; border:none; color:var(--white);
padding:10px 12px; font-family:'DM Mono',monospace; font-size:13px;
letter-spacing:2px; outline:none;
}
.phone-input::placeholder { color:var(--white-faint); letter-spacing:1px; font-size:11px; }
.phone-format { font-size:9px; color:var(--white-faint); letter-spacing:1px; margin-top:5px; }
/* Waiting SMS box */
.sms-waiting-box {
text-align:center; padding:20px 16px;
background:rgba(255,255,255,0.03);
border:1px solid rgba(255,255,255,0.07); margin-bottom:14px;
}
.sms-waiting-box .sw-icon { font-size:28px; margin-bottom:10px; }
.sms-waiting-box .sw-title { font-family:'Cormorant Garamond',serif; font-size:17px; letter-spacing:2px; margin-bottom:6px; }
.sms-waiting-box .sw-sub { font-size:10px; color:var(--white-dim); letter-spacing:1px; line-height:1.8; }
.sms-waiting-box .sw-phone { font-size:13px; color:var(--white); letter-spacing:2px; margin-top:8px; font-family:'DM Mono',monospace; }
.phase { display:none; }
.phase.active { display:block; }
.success-anim { text-align:center; padding:20px 0; }
.success-anim svg { width:40px; height:40px; margin:0 auto 12px; display:block; }
.err-code { font-size:9px; opacity:0.6; margin-right:4px; letter-spacing:1px; }
.footer-mini { text-align:center; margin-top:18px; font-size:9px; color:var(--white-faint); letter-spacing:1px; line-height:2; }
</style>
<script>if(localStorage.getItem('sge_maintenance')==='1'){window.location.replace('404.html');}</script>
</head>
<body>
<div class="wrap">
<div style="text-align:center;margin-bottom:22px;" class="fade-in">
<img src="logo.svg" style="width:36px;height:36px;margin:0 auto 10px;display:block;">
<div style="font-family:'Cormorant Garamond',serif;font-size:22px;letter-spacing:3px;">SGE</div>
<div style="font-size:9px;letter-spacing:2px;color:var(--white-dim);margin-top:3px;">ÎNREGISTRARE CONT</div>
</div>
<!-- STEPS: 4 pasi -->
<div class="steps fade-in-2">
<div class="step">
<div class="step-dot active" id="s1-dot">1</div>
<div class="step-label active" id="s1-lbl">IDENTITATE</div>
</div>
<div class="step-line"></div>
<div class="step">
<div class="step-dot" id="s2-dot">2</div>
<div class="step-label" id="s2-lbl">TELEFON</div>
</div>
<div class="step-line"></div>
<div class="step">
<div class="step-dot" id="s3-dot">3</div>
<div class="step-label" id="s3-lbl">COD SMS</div>
</div>
<div class="step-line"></div>
<div class="step">
<div class="step-dot" id="s4-dot">4</div>
<div class="step-label" id="s4-lbl">PAROLĂ</div>
</div>
</div>
<!-- PHASE 1: Confirmare identitate -->
<div class="phase active fade-in-3" id="phase-1">
<div class="vpass-card">
<div class="vc-icon"><img src="logo.svg" alt=""></div>
<div>
<div class="vc-name" id="ph1-name"></div>
<div class="vc-id" id="ph1-vpass"></div>
</div>
</div>
<div class="card" style="padding:18px 16px;">
<div class="card-title" style="font-size:15px;">Ești tu?</div>
<p style="font-size:11px;color:var(--white-dim);margin-bottom:16px;line-height:1.9;">
Verifică că datele de mai sus îți aparțin. La pasul următor vei introduce numărul tău de telefon pentru a primi codul de confirmare prin SMS.
</p>
<button class="btn-primary" onclick="goToPhone()" style="width:100%;letter-spacing:2px;">DA, SUNT EU — CONTINUĂ →</button>
<div class="alert error" id="err-1" style="margin-top:10px;"></div>
</div>
<div style="text-align:center;margin-top:12px;">
<a href="index.html" style="font-size:10px;color:var(--white-dim);letter-spacing:1px;text-decoration:none;">← înapoi la login</a>
</div>
</div>
<!-- PHASE 2: Introducere număr telefon -->
<div class="phase" id="phase-2">
<div class="vpass-card">
<div class="vc-icon"><img src="logo.svg" alt=""></div>
<div>
<div class="vc-name" id="ph2-name"></div>
<div class="vc-id" id="ph2-vpass"></div>
</div>
</div>
<div class="card" style="padding:18px 16px;">
<div class="card-title" style="font-size:15px;">Număr de telefon</div>
<p style="font-size:11px;color:var(--white-dim);margin-bottom:16px;line-height:1.9;">
Introdu numărul tău de telefon. Administratorul îți va transmite codul de confirmare VPass pe acest număr.
</p>
<div class="field">
<label>Număr de telefon</label>
<div class="phone-wrap">
<div class="phone-prefix">+373</div>
<input class="phone-input" type="tel" id="phone-input" maxlength="13" placeholder="(69) 048 176" inputmode="tel" oninput="formatPhone(this)">
</div>
<div class="phone-format">Format: +373 (##) ### ### &nbsp;·&nbsp; Ex: +373 (69) 048 176</div>
</div>
<button class="btn-primary" onclick="requestCode()" id="btn-req" style="width:100%;letter-spacing:2px;margin-top:4px;">SOLICITĂ COD →</button>
<div class="alert error" id="err-2" style="margin-top:10px;"></div>
</div>
</div>
<!-- PHASE 3: Asteapta SMS + introdu codul -->
<div class="phase" id="phase-3">
<div class="sms-waiting-box">
<div class="sw-icon">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="1.5" stroke-linecap="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
</div>
<div class="sw-title">SMS trimis de administrator</div>
<div class="sw-sub">Administratorul a primit cererea ta și va trimite<br>codul de confirmare pe numărul:</div>
<div class="sw-phone" id="phone-display"></div>
</div>
<div class="waiting-indicator">
<div class="pulse-dot"></div>
<div>
<div class="wi-text">Așteptăm validarea administratorului...</div>
<div class="wi-code" id="wait-timer"></div>
</div>
</div>
<div class="card" style="padding:18px 16px;">
<div class="card-title" style="font-size:15px;">Introdu codul primit prin SMS</div>
<p style="font-size:11px;color:var(--white-dim);margin-bottom:14px;line-height:1.9;">
Introduceți cele 4 cifre primite în SMS-ul de confirmare VPass.
</p>
<div class="field">
<input type="text" id="confirm-input" maxlength="4" placeholder="• • • •"
inputmode="numeric" autocomplete="one-time-code"
style="font-size:28px;letter-spacing:12px;text-align:center;padding:14px;">
</div>
<button class="btn-primary" onclick="verifyCode()" style="width:100%;letter-spacing:2px;margin-top:4px;">VERIFICĂ COD →</button>
<div class="alert error" id="err-3" style="margin-top:10px;"></div>
<div style="margin-top:12px;text-align:center;">
<button class="btn-ghost" onclick="requestNewCode()" style="font-size:9px;letter-spacing:1px;">Nu am primit SMS — Cod nou</button>
</div>
</div>
</div>
<!-- PHASE 4: Setare parola -->
<div class="phase" id="phase-4">
<div class="vpass-card">
<div class="vc-icon"><img src="logo.svg" alt=""></div>
<div>
<div class="vc-name" id="ph4-name"></div>
<div class="vc-id" id="ph4-vpass" style="color:#5a9a5a;letter-spacing:2px;">✓ CONFIRMAT PRIN SMS</div>
</div>
</div>
<div class="card" style="padding:18px 16px;">
<div class="card-title" style="font-size:15px;">Setează parola VPass</div>
<p style="font-size:11px;color:var(--white-dim);margin-bottom:14px;line-height:1.9;">
Alege o parolă de minimum 6 cifre. Aceasta va fi codul tău permanent de autentificare în sistem.
</p>
<div class="field">
<label>Parolă nouă (min. 6 cifre)</label>
<input type="password" id="new-pin" maxlength="6" placeholder="••••••" inputmode="numeric" autocomplete="new-password">
</div>
<div class="field">
<label>Confirmă parola</label>
<input type="password" id="new-pin2" maxlength="6" placeholder="••••••" inputmode="numeric" autocomplete="new-password">
</div>
<button class="btn-primary" onclick="setPassword()" style="width:100%;letter-spacing:2px;margin-top:4px;">ACTIVEAZĂ CONTUL →</button>
<div class="alert error" id="err-4" style="margin-top:10px;"></div>
</div>
</div>
<!-- PHASE 5: Done -->
<div class="phase" id="phase-5">
<div class="card" style="padding:28px 20px;">
<div class="success-anim">
<svg viewBox="0 0 24 24" fill="none" stroke="#5a9a5a" stroke-width="1.5" stroke-linecap="round">
<path d="M22 11.08V12a10 10 0 11-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
<div style="font-family:'Cormorant Garamond',serif;font-size:22px;letter-spacing:2px;margin-bottom:6px;">Cont activat</div>
<div style="font-size:11px;color:var(--white-dim);letter-spacing:1px;" id="ph5-name"></div>
<div style="font-size:10px;color:var(--white-faint);letter-spacing:2px;margin-top:4px;" id="ph5-vpass"></div>
</div>
<div style="margin-top:20px;">
<button class="btn-primary" onclick="window.location.href='index.html'" style="width:100%;letter-spacing:2px;">MERGI LA LOGIN →</button>
</div>
</div>
</div>
<div class="footer-mini">SGE &copy;2026 &mdash; Victor Rosca</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js";
import { getFirestore, collection, addDoc, getDocs, updateDoc, doc, query, where, serverTimestamp }
from "https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore.js";
const cfg = {
apiKey:"AIzaSyB9--Onx3-_YjD-YzblhZjaWSVVqTQJ1lU", authDomain:"vservers1.firebaseapp.com",
projectId:"vservers1", storageBucket:"vservers1.firebasestorage.app",
messagingSenderId:"42433037358", appId:"1:42433037358:web:fde70fec79542428b60bbf"
};
const app = initializeApp(cfg);
const db = getFirestore(app);
const elevId = sessionStorage.getItem('su_elevId');
const elevNume = sessionStorage.getItem('su_name');
const elevVpass = sessionStorage.getItem('su_vpass');
if (!elevId || !elevNume) { window.location.href = 'index.html'; }
// Populate identity fields
['ph1-name','ph2-name','ph4-name','ph5-name'].forEach(id => {
const el = document.getElementById(id); if(el) el.textContent = elevNume;
});
['ph1-vpass','ph2-vpass','ph5-vpass'].forEach(id => {
const el = document.getElementById(id); if(el) el.textContent = elevVpass;
});
let generatedCode = null; // SECRET — nu e afișat elevului
let requestDocId = null;
let pollInterval = null;
let codeExpiry = null;
let phoneNumber = null;
// ── STEPS ──
function setPhase(n) {
document.querySelectorAll('.phase').forEach(p => p.classList.remove('active'));
document.getElementById('phase-'+n).classList.add('active');
window.scrollTo({top:0,behavior:'smooth'});
for (let i=1; i<=4; i++) {
const dot = document.getElementById(`s${i}-dot`);
if (i < n) { dot.classList.remove('active'); dot.classList.add('done'); dot.innerHTML='✓'; }
else if (i===n) { dot.classList.add('active'); dot.classList.remove('done'); }
else { dot.classList.remove('active','done'); }
}
}
// ── PHONE FORMAT ──
window.formatPhone = function(input) {
let v = input.value.replace(/\D/g,'');
if (v.length > 9) v = v.slice(0,9);
let out = '';
if (v.length >= 2) out = '(' + v.slice(0,2) + ') ';
else out = v;
if (v.length > 2) out += v.slice(2,5);
if (v.length > 5) out += ' ' + v.slice(5,8);
if (v.length > 8) out += v.slice(8,9);
input.value = out;
};
// ── PHASE 1 → 2 ──
window.goToPhone = function() { setPhase(2); };
// ── SOLICITA COD (PHASE 2) ──
window.requestCode = async function() {
hideError('err-2');
const raw = document.getElementById('phone-input').value.replace(/\D/g,'');
if (raw.length < 8) { showError('err-2','err-021','Număr de telefon incomplet.'); return; }
let formatted = '+373 (' + raw.slice(0,2) + ') ' + raw.slice(2,5);
if (raw.length > 5) formatted += ' ' + raw.slice(5,8);
if (raw.length > 8) formatted += raw.slice(8,9);
phoneNumber = formatted;
document.getElementById('btn-req').disabled = true;
// Verifica pending
try {
const q = query(collection(db,'signup_requests'), where('elevId','==',elevId), where('status','==','pending'));
const ex = await getDocs(q);
if (!ex.empty) { showError('err-2','err-005'); document.getElementById('btn-req').disabled=false; return; }
} catch(e) { showError('err-2','err-026'); document.getElementById('btn-req').disabled=false; return; }
// Verifica deja inregistrat
try {
const q2 = query(collection(db,'elevi'), where('vpassId','==',elevVpass));
const snap = await getDocs(q2);
if (!snap.empty && snap.docs[0].data().pin !== null) {
showError('err-2','err-023'); document.getElementById('btn-req').disabled=false; return;
}
} catch(e) {}
// Genereaza cod
generatedCode = String(Math.floor(1000 + Math.random() * 9000));
try {
const ref = await addDoc(collection(db,'signup_requests'), {
elevId, elevVpass, elevNume, telefon: phoneNumber,
confirmCode: generatedCode,
status: 'approved', // AUTO-APROBAT imediat
timestamp: serverTimestamp()
});
requestDocId = ref.id;
// Notificare pentru admin (informativa)
await addDoc(collection(db,'notificari'), {
tip: 'signup_request',
elevId, elevVpass, elevNume,
telefon: phoneNumber,
confirmCode: generatedCode,
requestId: ref.id,
status: 'approved',
citita: false,
timestamp: serverTimestamp()
});
document.getElementById('phone-display').textContent = phoneNumber;
setPhase(3);
startTimer();
startPolling();
} catch(e) { showError('err-2','err-025'); document.getElementById('btn-req').disabled=false; }
};
function startTimer() {
codeExpiry = Date.now() + 10 * 60 * 1000;
const el = document.getElementById('wait-timer');
const iv = setInterval(() => {
const left = Math.max(0, codeExpiry - Date.now());
const m = Math.floor(left/60000);
const s = Math.floor((left%60000)/1000);
el.textContent = `Codul expiră în ${m}:${String(s).padStart(2,'0')}`;
if (left <= 0) { clearInterval(iv); el.textContent = 'Cod expirat — solicită unul nou'; }
}, 1000);
}
function startPolling() {
if (pollInterval) clearInterval(pollInterval);
pollInterval = setInterval(async () => {
if (!requestDocId) return;
try {
const q = query(collection(db,'signup_requests'),
where('elevId','==',elevId), where('status','==','rejected'));
const snap = await getDocs(q);
if (!snap.empty) { clearInterval(pollInterval); showError('err-3','err-024'); }
} catch(e) {}
}, 4000);
}
// ── VERIFICA COD SMS ──
window.verifyCode = function() {
hideError('err-3');
const input = document.getElementById('confirm-input').value.replace(/\s/g,'');
if (!/^\d{4}$/.test(input)) { showError('err-3','err-029'); return; }
if (codeExpiry && Date.now() > codeExpiry) { showError('err-3','err-006'); return; }
if (input !== generatedCode) { showError('err-3','err-007'); return; }
if (pollInterval) clearInterval(pollInterval);
setPhase(4);
};
window.requestNewCode = async function() {
if (pollInterval) clearInterval(pollInterval);
generatedCode = null; requestDocId = null;
document.getElementById('confirm-input').value = '';
document.getElementById('phone-input').value = '';
document.getElementById('btn-req').disabled = false;
setPhase(2);
};
// ── SETEAZA PAROLA ──
window.setPassword = async function() {
hideError('err-4');
const p1 = document.getElementById('new-pin').value;
const p2 = document.getElementById('new-pin2').value;
if (p1.length < 6) { showError('err-4','err-008'); return; }
if (p1 !== p2) { showError('err-4','err-008','Parolele nu coincid.'); return; }
try {
const q = query(collection(db,'elevi'), where('vpassId','==',elevVpass));
const snap = await getDocs(q);
if (snap.empty) { showError('err-4','err-002'); return; }
await updateDoc(doc(db,'elevi',snap.docs[0].id), { pin: p1, confirmed: true });
if (requestDocId) {
try { await updateDoc(doc(db,'signup_requests',requestDocId),{ status:'completed' }); } catch(e){}
}
sessionStorage.removeItem('su_elevId');
sessionStorage.removeItem('su_name');
sessionStorage.removeItem('su_vpass');
setPhase(5);
} catch(e) { showError('err-4','err-025'); }
};
</script>
<a href="vhelp.html" class="vhelp-fab" title="VHelp">
<svg viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.9)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
<circle cx="9" cy="10" r="0.5" fill="rgba(255,255,255,0.9)"/>
<circle cx="12" cy="10" r="0.5" fill="rgba(255,255,255,0.9)"/>
<circle cx="15" cy="10" r="0.5" fill="rgba(255,255,255,0.9)"/>
</svg>
</a>
</body>
</html>