import React, { useEffect, useState, useRef } from 'react'; import { createPortal } from 'react-dom'; import { ShieldAlert, Clock, ArrowRight, X, AlertTriangle, LogOut } from 'lucide-react'; import { clearSessionAndRedirectToLanding, dismissSubordinateModals } from '../../services/sessionExpiration'; interface SessionExpiredModalProps { isOpen: boolean; onDismiss?: () => void; reason?: string | null; /** Total countdown time in seconds (defaults to 8 seconds) */ countdownSeconds?: number; } export const SessionExpiredModal: React.FC = ({ isOpen, onDismiss, reason, countdownSeconds = 8, }) => { const [timeLeftMs, setTimeLeftMs] = useState(countdownSeconds * 1000); const [isPaused, setIsPaused] = useState(false); const totalDurationMs = countdownSeconds * 1000; const isClearingRef = useRef(false); // Close background modals when session expires useEffect(() => { if (isOpen) { dismissSubordinateModals(); } }, [isOpen]); // Reset state if session expiration is cancelled useEffect(() => { const handleCancelled = () => { setTimeLeftMs(countdownSeconds * 1000); setIsPaused(false); isClearingRef.current = false; }; window.addEventListener('pfm:session-expired-cancelled', handleCancelled); return () => { window.removeEventListener('pfm:session-expired-cancelled', handleCancelled); }; }, [countdownSeconds]); // Reset or start countdown when modal opens useEffect(() => { if (!isOpen) { setTimeLeftMs(countdownSeconds * 1000); setIsPaused(false); isClearingRef.current = false; return; } const intervalMs = 50; const interval = setInterval(() => { setTimeLeftMs((prev) => { if (isPaused) return prev; const next = prev - intervalMs; if (next <= 0) { clearInterval(interval); handleAction(); return 0; } return next; }); }, intervalMs); return () => clearInterval(interval); }, [isOpen, isPaused, countdownSeconds]); const handleAction = () => { if (isClearingRef.current) return; isClearingRef.current = true; if (onDismiss) { onDismiss(); } else { clearSessionAndRedirectToLanding(); } }; if (!isOpen) return null; const secondsRemaining = Math.max(0, Math.ceil(timeLeftMs / 1000)); const progressPercent = Math.max(0, Math.min(100, (timeLeftMs / totalDurationMs) * 100)); const modalContent = (
setIsPaused(true)} onMouseLeave={() => setIsPaused(false)} role="dialog" aria-modal="true" aria-labelledby="session-expired-title" >

Session Expired

Authorization credentials expired

Your active session has ended to protect account security. Please sign in again to continue working.

{reason && (
{reason}
)}
Redirecting in {secondsRemaining}s {isPaused && ( Paused )}
); return typeof document !== 'undefined' ? createPortal(modalContent, document.body) : modalContent; };