'use client'; import React, { useState, useEffect } from 'react'; import type { Session } from '../types/api'; import { API_PATHS } from '../types/api'; interface SessionManagerProps { onSessionRevoke?: (sessionId: string) => void; onRefreshSessions?: () => void; className?: string; } export default function SessionManager({ onSessionRevoke, onRefreshSessions, className = '', }: SessionManagerProps) { const [sessions, setSessions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [currentSessionId, setCurrentSessionId] = useState(null); // Fetch sessions const fetchSessions = async () => { try { setIsLoading(true); setError(null); const response = await fetch(API_PATHS.AUTH_SESSIONS, { headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error('Failed to fetch sessions'); } const data = await response.json(); setSessions(data.sessions || []); setCurrentSessionId(data.currentSessionId); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } finally { setIsLoading(false); } }; // Revoke session const revokeSession = async (sessionId: string) => { try { const revokePath = API_PATHS.AUTH_REVOKE_SESSION.replace(':id', sessionId); const response = await fetch(revokePath, { method: 'DELETE', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error('Failed to revoke session'); } // Remove session from list setSessions(prev => prev.filter(s => s.id !== sessionId)); onSessionRevoke?.(sessionId); // If we revoked the current session, we should log out if (sessionId === currentSessionId) { // Trigger logout window.location.href = '/login?reason=session_revoked'; } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to revoke session'); } }; // Format device info const formatDeviceInfo = (deviceInfo?: Record): string => { if (!deviceInfo) return 'Unknown Device'; const browser = deviceInfo.browser as string; const os = deviceInfo.os as string; const platform = deviceInfo.platform as string; if (browser && os) { return `${browser} on ${os}`; } else if (platform) { return platform; } return 'Unknown Device'; }; // Format last activity const formatLastActivity = (lastActivity?: Date): string => { if (!lastActivity) return 'Never'; const now = new Date(); const diff = now.getTime() - new Date(lastActivity).getTime(); const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { return `${days} day${days > 1 ? 's' : ''} ago`; } else if (hours > 0) { return `${hours} hour${hours > 1 ? 's' : ''} ago`; } else if (minutes > 0) { return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; } else { return 'Just now'; } }; // Get device icon const getDeviceIcon = (userAgent?: string): React.ReactNode => { const ua = userAgent?.toLowerCase() || ''; if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) { return ( ); } else if (ua.includes('tablet') || ua.includes('ipad')) { return ( ); } else { return ( ); } }; // Get location from IP (placeholder) const getLocation = (ipAddress?: string): string => { if (!ipAddress) return 'Unknown Location'; return ipAddress; // In production, you'd use a geolocation service }; // Refresh sessions const handleRefresh = () => { fetchSessions(); onRefreshSessions?.(); }; // Load sessions on mount useEffect(() => { fetchSessions(); }, []); return (

Active Sessions

{/* Error state */} {error && (

{error}

)} {/* Loading state */} {isLoading && sessions.length === 0 && (
)} {/* Sessions list */} {!isLoading && sessions.length === 0 && !error && (

No active sessions

Sign in to see your active sessions here

)} {sessions.length > 0 && (
{sessions.map(session => (
{/* Device icon */}
{getDeviceIcon(session.userAgent)}
{/* Session info */}

{formatDeviceInfo(session.deviceInfo)}

{/* Current session badge */} {session.id === currentSessionId && ( Current session )}
{session.ipAddress && (

IP: {getLocation(session.ipAddress)}

)}

Last active: {formatLastActivity(session.lastActivity)}

Signed in: {new Date(session.createdAt).toLocaleDateString()}

{/* Revoke button */} {session.id !== currentSessionId && ( )}
))}
)} {/* Info text */} {sessions.length > 0 && (

About sessions

These are the devices where your account is currently signed in. Revoking a session will sign you out of that device. Be careful not to revoke your current session unless you want to sign out.

)}
); }