import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { AlertCircle, Info, X } from 'lucide-react' import { cn } from '@/lib/utils' import { useAuthSessionExpiry } from '@/hooks/useAuthSessionExpiry' import { authApi } from '@/api/auth' import { useAuthStore } from '@/store/authStore' /** * Top-of-app notice that fires when the session is within 5 minutes of * idle OR absolute expiry. Behavior differs by which window is closer * (per docs/plans/2026-05-13-session-expiration-policy.md §4.8): * * - Idle: warning-amber tone, "Stay signed in" button hits /auth/refresh. * - Absolute: info-cyan tone, no action — re-auth is required. * * Persists until the user dismisses, refreshes, or the window expires. */ export function SessionExpiryToast() { const { warning, reason, idleExpiresAt, absoluteExpiresAt } = useAuthSessionExpiry() const setTokens = useAuthStore((s) => s.setTokens) const navigate = useNavigate() const [busy, setBusy] = useState(false) const [dismissed, setDismissed] = useState(false) if (warning !== 'soon' || dismissed) return null const handleStay = async () => { setBusy(true) try { const refreshed = await authApi.refresh() localStorage.setItem('access_token', refreshed.access_token) localStorage.setItem('refresh_token', refreshed.refresh_token) setTokens(refreshed) setDismissed(true) } catch { // The axios interceptor handles the redirect on session_expired_*; // if we land here, something else went wrong — just close the toast. setDismissed(true) } finally { setBusy(false) } } const handleSignInNow = () => navigate('/login') // ── Format the deadline for the absolute case ── const deadline = reason === 'idle' ? idleExpiresAt : absoluteExpiresAt const deadlineLabel = deadline ? deadline.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }) : '' const isIdle = reason === 'idle' const Icon = isIdle ? AlertCircle : Info return (
{isIdle ? 'Your session times out in 5 minutes.' : `Your session ends at ${deadlineLabel} for security.`}
{isIdle ? 'Click to stay signed in.' : "You'll need to sign in again."}