veza/apps/web/src/features/auth/components/UserProfile.tsx

72 lines
2.2 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { apiClient } from '@/services/api/client';
import { logger } from '@/utils/logger';
import { parseApiError } from '@/utils/apiErrorHandler';
import { Button } from '@/components/ui/button';
import { Shield, ExternalLink } from 'lucide-react';
interface SessionsResponse {
sessions: Array<{
id: string;
ip_address: string;
user_agent: string;
last_activity: string;
created_at: string;
is_current: boolean;
}>;
count: number;
}
export function UserProfile() {
const [activeSessionsCount, setActiveSessionsCount] = useState(0);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchSessionsCount();
}, []);
const fetchSessionsCount = async () => {
try {
setLoading(true);
setError(null);
const response = await apiClient.get<SessionsResponse>('/sessions');
setActiveSessionsCount(response.data.sessions.length);
} catch (error: unknown) {
const apiError = parseApiError(error);
logger.error('Failed to fetch sessions count', {
message: apiError.message,
});
setError(apiError.message);
// Ne pas bloquer l'affichage si l'erreur survient
setActiveSessionsCount(0);
} finally {
setLoading(false);
}
};
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Shield className="h-4 w-4 text-muted-foreground" />
<p className="text-sm font-medium">Active Sessions</p>
</div>
{loading ? (
<span className="text-sm text-muted-foreground">Loading...</span>
) : error ? (
<span className="text-sm text-muted-foreground">Error</span>
) : (
<span className="text-sm font-semibold">{activeSessionsCount}</span>
)}
</div>
<Link to="/settings/sessions">
<Button variant="outline" size="sm" className="w-full">
Manage Sessions
<ExternalLink className="ml-2 h-3 w-3" />
</Button>
</Link>
</div>
);
}