import { useEffect, useRef, useState } from 'react' import type { User } from '../types' function getInactivityMs(): number | undefined { if (window.matchMedia('(display-mode: standalone)').matches) return undefined const c = document.cookie.split(';').map(s => s.trim()).find(s => s.startsWith('hnf_inactivity_mins=')) if (!c) return undefined const mins = parseInt(c.split('=')[1]) return isNaN(mins) || mins <= 0 ? undefined : mins * 60 * 1000 } interface Props { children: (user: User) => React.ReactNode } const inputStyle: React.CSSProperties = { background: 'var(--navy-dark)', border: '1px solid var(--surface-2)', borderRadius: '6px', color: 'var(--text)', padding: '0.625rem 0.75rem', fontSize: '1rem', width: '100%', outline: 'none', } const btnStyle: React.CSSProperties = { background: 'var(--hk-green)', color: '#fff', border: 'none', borderRadius: '6px', padding: '0.625rem', fontSize: '1rem', fontWeight: 600, marginTop: '0.25rem', width: '100%', } export function AuthGate({ children }: Props) { const [state, setState] = useState<'checking' | 'authed' | 'login'>('checking') const [user, setUser] = useState(null) const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) const timerRef = useRef | null>(null) useEffect(() => { fetch('/api/auth/verify?app=hk-planner', { credentials: 'include' }) .then(async r => { if (r.ok) { setUser(await r.json()); setState('authed') } else setState('login') }) .catch(() => setState('login')) }, []) useEffect(() => { const ms = getInactivityMs() if (state !== 'authed' || !ms) return async function forceLogout() { await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }).catch(() => {}) setUser(null) setState('login') } function reset() { if (timerRef.current) clearTimeout(timerRef.current) timerRef.current = setTimeout(forceLogout, ms) } const events = ['mousemove', 'keydown', 'click', 'touchstart'] as const events.forEach(e => window.addEventListener(e, reset, { passive: true })) reset() return () => { if (timerRef.current) clearTimeout(timerRef.current) events.forEach(e => window.removeEventListener(e, reset)) } }, [state]) async function login(e: React.FormEvent) { e.preventDefault() setLoading(true) setError('') try { const res = await fetch('/api/auth/login', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }) if (!res.ok) { setError('Invalid email or password'); return } const verify = await fetch('/api/auth/verify?app=hk-planner', { credentials: 'include' }) if (verify.ok) { setUser(await verify.json()); setState('authed') } else setError("You don't have access to this app.") } catch { setError('Connection error — please try again') } finally { setLoading(false) } } if (state === 'checking') { return (
Loading…
) } if (state === 'login') { return (

HK Planner

{import.meta.env.VITE_HOTEL_NAME}

setEmail(e.target.value)} placeholder="Email" required autoComplete="email" style={inputStyle} /> setPassword(e.target.value)} placeholder="Password" required autoComplete="current-password" style={inputStyle} /> {error &&

{error}

}
) } return <>{children(user!)} }