/* Authentication: Welcome, Login select, shared Verification (email+phone OTP) */ (function () { const { useState, useEffect, useRef } = React; const Icon = window.Icon, Logo = window.Logo; const D = window.YEIDA; /* ---- Brand panel (navy decorative side) ---- */ function BrandPanel({ compact }) { return (
Official Allottee Portal

Your complete Yamuna Expressway property portal.

Verify your allotment, view official authority records, track construction and manage documents — securely.

{[['7+', 'Active Sectors'], ['62,000+', 'Allottees'], ['100%', 'Digital']].map((x, i) => (
{x[0]}
{x[1]}
))}
); } function AuthShell({ wide, children }) { return (
{children}
); } /* ---------------- WELCOME ---------------- */ function Welcome({ go }) { return (
Government of Uttar Pradesh · Official Portal

Your Complete Yamuna Expressway Property Management Portal

Verify your allotment, view official authority payment records, track construction approvals and manage documents — all in one secure place.

SSL Secured OTP Verified Access DigiLocker Ready
); } function HeroCard() { const plots = [[20, 18], [64, 18], [108, 18], [186, 18], [230, 18], [274, 18], [20, 116], [64, 116], [186, 116], [230, 116], [274, 116]]; return (
Plot B-47, Sector 18
Residential · 300 sq.m
{plots.map((p, i) => )} B-47
); } /* ---------------- LOGIN SELECTION ---------------- */ function LoginSelect({ go }) { const [mode, setMode] = useState('allotment'); const opts = [ { k: 'allotment', icon: 'building', title: 'Allotment Number', desc: 'Login with your YEIDA allotment number' }, { k: 'invite', icon: 'key', title: 'Invitation Code', desc: 'Access via authority-issued invite code' }, ]; return (
Welcome back

Sign in to your portal

Verify yourself using your allotment number or an invitation code.

{opts.map(o => ( ))}
{mode === 'allotment' && }
New allottee or representative?{' '}
); } /* ---------------- SHARED VERIFICATION (email + phone, same screen) ---------------- */ // VerifyPanel renders both channels and reports completion via onChange(bothVerified) function VerifyPanel({ email, phone, onChange }) { const [cc, setCc] = useState(D.countryCodes[0]); const [state, setState] = useState({ email: false, phone: false }); return (
setState(s => { const n = { ...s, email: v }; onChange && onChange(n.email && n.phone); return n; })} /> setState(s => { const n = { ...s, phone: v }; onChange && onChange(n.email && n.phone); return n; })} />
); } // channel that lifts verified state up to the panel function VerifyChannelLifted({ kind, value, cc, setCc, onVerified }) { const [sent, setSent] = useState(false); const [otp, setOtp] = useState(['', '', '', '', '', '']); const [verified, setVerified] = useState(false); const [cooldown, setCooldown] = useState(0); const [checking, setChecking] = useState(false); const [api, setApi] = useState('idle'); // idle|checking|ok|invalid_email|mailbox_full|bounce_risk|invalid_mobile const [otpErr, setOtpErr] = useState(false); const [field, setField] = useState(value || ''); const refs = useRef([]); useEffect(() => { if (cooldown <= 0) return; const t = setInterval(() => setCooldown(s => s - 1), 1000); return () => clearInterval(t); }, [cooldown]); useEffect(() => { onVerified && onVerified(verified); }, [verified]); const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field || ''); const digits = (field || '').replace(/\D/g, '').length; const phoneOk = digits >= (cc ? cc.digits : 10); const valid = kind === 'email' ? emailOk : phoneOk; // simulate an email/phone validation API — keywords in the value trigger states (demo) const runApi = () => { setApi('checking'); setChecking(true); setTimeout(() => { setChecking(false); const v = (field || '').toLowerCase(); if (kind === 'email') { if (!emailOk) return setApi('invalid_email'); if (v.includes('full')) return setApi('mailbox_full'); if (v.includes('bounce') || v.includes('temp')) return setApi('bounce_risk'); setApi('ok'); setSent(true); setCooldown(60); setTimeout(() => refs.current[0] && refs.current[0].focus(), 50); } else { if (!phoneOk) return setApi('invalid_mobile'); setApi('ok'); setSent(true); setCooldown(60); setTimeout(() => refs.current[0] && refs.current[0].focus(), 50); } }, 950); }; const API_MSG = { checking: { tone: 'blue', icon: 'clock', text: kind === 'email' ? 'Validating email via API…' : 'Validating mobile via API…' }, ok: { tone: 'green', icon: 'checkCircle', text: kind === 'email' ? 'Email deliverable · OTP sent' : 'Mobile valid · OTP sent' }, invalid_email: { tone: 'red', icon: 'alert', text: 'Invalid email address' }, mailbox_full: { tone: 'amber', icon: 'alert', text: 'Mailbox full — try another email' }, bounce_risk: { tone: 'amber', icon: 'alert', text: 'High bounce risk — verify spelling' }, invalid_mobile: { tone: 'red', icon: 'alert', text: `Invalid mobile number for ${cc ? cc.label : 'IN'}` }, }; const set = (i, v) => { if (!/^\d?$/.test(v)) return; const d = otp.slice(); d[i] = v; setOtp(d); setOtpErr(false); if (v && i < 5) refs.current[i + 1] && refs.current[i + 1].focus(); if (d.every(x => x !== '')) { // demo: "000000" simulates an incorrect OTP if (d.join('') === '000000') { setOtpErr(true); setOtp(['', '', '', '', '', '']); setTimeout(() => refs.current[0] && refs.current[0].focus(), 50); } else setVerified(true); } }; const onKey = (i, e) => { if (e.key === 'Backspace' && !otp[i] && i > 0) refs.current[i - 1] && refs.current[i - 1].focus(); }; const apiMsg = API_MSG[api]; const toneColor = { red: 'var(--red-600)', amber: 'var(--amber-600)', blue: 'var(--blue-600)', green: 'var(--emerald-700)' }; const toneBg = { red: 'var(--red-50)', amber: 'var(--amber-50)', blue: 'var(--blue-50)', green: 'var(--emerald-50)' }; return (
{kind === 'email' ? 'Verify Email' : 'Verify Phone Number'}
{verified ? 'Verified successfully' : sent ? 'Enter the 6-digit OTP' : 'OTP verification required'}
{verified && Verified}
{kind === 'email' ? (
{ setField(e.target.value); setApi('idle'); }} disabled={sent} />
) : (
{ setField(e.target.value); setApi('idle'); }} disabled={sent} />
)} {/* API validation status banner */} {apiMsg && (
{api === 'checking' ? : } {apiMsg.text}
)} {!sent ? ( ) : (
{otp.map((d, i) => ( refs.current[i] = el} value={d} inputMode="numeric" maxLength={1} disabled={verified} onChange={e => set(i, e.target.value)} onKeyDown={e => onKey(i, e)} style={{ width: 44, height: 50, textAlign: 'center', fontSize: 20, fontWeight: 700, fontFamily: 'var(--font-display)', border: '1.5px solid ' + (otpErr ? 'var(--red-500)' : d ? (verified ? '#7fd3ad' : 'var(--blue-400)') : 'var(--line)'), borderRadius: 11, color: 'var(--ink)', outline: 'none', background: otpErr ? 'var(--red-50)' : d ? (verified ? '#fff' : 'var(--blue-50)') : '#fff' }} /> ))}
{otpErr &&
OTP incorrect — please try again
} {!verified && (
Didn't get the code?
{kind === 'email' && cooldown <= 0 && } {cooldown > 0 ? Resend in 0:{String(cooldown).padStart(2, '0')} : }
)}
)}
); } /* standalone Verify screen (login path) */ function Verify({ go }) { const [both, setBoth] = useState(false); return (

Verify it's you

Confirm both your email and mobile to securely access your records.

Demo: enter any 6 digits to verify · type "000000" for an incorrect-OTP state · use an email with "full"/"bounce" to see API states

); } Object.assign(window, { Welcome, LoginSelect, Verify, AuthShell, VerifyPanel, BrandPanel }); const s = document.createElement('style'); s.textContent = '@keyframes ping{0%{transform:scale(.8);opacity:.6}100%{transform:scale(1.8);opacity:0}}'; document.head.appendChild(s); })();