const { useState: useStateA, useEffect: useEffectA, useRef: useRefA } = React; // ── PIN Security ────────────────────────────────────────────── const PIN_HASH = "e68259a977d847c9ed26fc47c6855021c9a51eac28a63f9a0c012f7bbac0b556"; const MAX_ATTEMPTS = 3; const LOCKOUT_MS = 5 * 60 * 1000; async function hashPin(pin) { const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(pin)); return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,"0")).join(""); } function getLockout() { try { return JSON.parse(localStorage.getItem("trix.lockout") || "null") || { attempts: 0, until: 0 }; } catch { return { attempts: 0, until: 0 }; } } function setLockout(d) { try { localStorage.setItem("trix.lockout", JSON.stringify(d)); } catch {} } function clearLockout() { try { localStorage.removeItem("trix.lockout"); } catch {} } function PinStep({ onSuccess }) { const [pin, setPin] = useStateA(""); const [error, setError] = useStateA(false); const [errMsg, setErrMsg] = useStateA(""); const [locked, setLocked] = useStateA(false); const [remaining, setRemaining] = useStateA(0); const timerRef = useRefA(null); useEffectA(() => { function tick() { const { until, attempts } = getLockout(); const now = Date.now(); if (until > now) { setLocked(true); setRemaining(Math.ceil((until - now) / 1000)); } else if (locked) { setLocked(false); setRemaining(0); if (attempts >= MAX_ATTEMPTS) setLockout({ attempts: 0, until: 0 }); } } tick(); timerRef.current = setInterval(tick, 1000); return () => clearInterval(timerRef.current); }, [locked]); async function pressKey(k) { if (locked) return; if (k === "del") { setPin(p => p.slice(0,-1)); return; } if (pin.length >= 4) return; const next = pin + k; setPin(next); if (next.length === 4) { const h = await hashPin(next); if (h === PIN_HASH) { clearLockout(); onSuccess(); } else { setError(true); setTimeout(() => { setError(false); setPin(""); }, 500); const state = getLockout(); const attempts = state.attempts + 1; if (attempts >= MAX_ATTEMPTS) { const until = Date.now() + LOCKOUT_MS; setLockout({ attempts, until }); setLocked(true); setErrMsg("تم الإغلاق · حاول بعد ٥ دقائق"); } else { setLockout({ attempts, until: 0 }); setErrMsg(`محاولة خاطئة · تبقى ${MAX_ATTEMPTS - attempts} محاولة`); } } } } const mins = String(Math.floor(remaining / 60)).padStart(2,"0"); const secs = String(remaining % 60).padStart(2,"0"); return (