Files
advx/src/components/provas/SessionReport.tsx
T
2025-07-27 18:27:11 -03:00

228 lines
8.4 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { useParams, Link } from 'react-router-dom';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
interface SessionData {
id: string;
startTime: string;
endTime?: string;
ipAddress: string;
dnsInfo: string;
userAgent: string;
geolocation?: {
latitude: number;
longitude: number;
};
screenshots: string[];
videoPath?: string;
metadata: any;
}
export const SessionReport: React.FC = () => {
const { sessionId } = useParams<{ sessionId: string }>();
const [session, setSession] = useState<SessionData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchSession = async () => {
try {
const response = await fetch(`/api/sessions/${sessionId}`);
if (!response.ok) {
throw new Error('Sessão não encontrada');
}
const data = await response.json();
setSession(data.session);
} catch (error) {
console.error('Erro ao buscar sessão:', error);
setError(error.message);
} finally {
setLoading(false);
}
};
if (sessionId) {
fetchSession();
}
}, [sessionId]);
const formatDuration = (start: string, end?: string) => {
if (!end) return 'Em andamento';
const startTime = new Date(start);
const endTime = new Date(end);
const diffMs = endTime.getTime() - startTime.getTime();
const hours = Math.floor(diffMs / (1000 * 60 * 60));
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diffMs % (1000 * 60)) / 1000);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};
if (loading) {
return (
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow p-6">
<div className="animate-pulse">
<div className="h-6 bg-gray-300 rounded w-3/4 mb-4"></div>
<div className="h-4 bg-gray-300 rounded w-1/2 mb-2"></div>
<div className="h-4 bg-gray-300 rounded w-2/3"></div>
</div>
</div>
</div>
</div>
);
}
if (error || !session) {
return (
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow p-6 text-center">
<div className="text-red-600 text-xl font-bold mb-4">
Erro ao carregar sessão
</div>
<p className="text-gray-600 mb-6">{error || 'Sessão não encontrada'}</p>
<Link
to="/sessions/new"
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
Nova Sessão
</Link>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow overflow-hidden">
{/* Header */}
<div className="bg-green-600 text-white px-6 py-4">
<h1 className="text-2xl font-bold">Relatório de Sessão de Coleta de Prova</h1>
<p className="text-green-100">ID da Sessão: {session.id}</p>
</div>
{/* Resumo da sessão */}
<div className="p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Resumo da Sessão</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 className="text-sm font-medium text-gray-500">Data/Hora de Início</h3>
<p className="text-lg text-gray-900">
{format(new Date(session.startTime), 'dd/MM/yyyy HH:mm:ss', { locale: ptBR })}
</p>
</div>
{session.endTime && (
<div>
<h3 className="text-sm font-medium text-gray-500">Data/Hora de Fim</h3>
<p className="text-lg text-gray-900">
{format(new Date(session.endTime), 'dd/MM/yyyy HH:mm:ss', { locale: ptBR })}
</p>
</div>
)}
<div>
<h3 className="text-sm font-medium text-gray-500">Duração</h3>
<p className="text-lg text-gray-900">
{formatDuration(session.startTime, session.endTime)}
</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500">Screenshots Capturados</h3>
<p className="text-lg text-gray-900">{session.screenshots.length}</p>
</div>
</div>
</div>
{/* Informações de rede */}
<div className="p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Informações de Rede</h2>
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-500">Endereço IP</h3>
<p className="text-gray-900 font-mono">{session.ipAddress}</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500">DNS Reverso</h3>
<p className="text-gray-900 font-mono">{session.dnsInfo}</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500">User-Agent</h3>
<p className="text-gray-900 font-mono text-sm break-all">{session.userAgent}</p>
</div>
</div>
</div>
{/* Geolocalização */}
{session.geolocation && (
<div className="p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Geolocalização</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h3 className="text-sm font-medium text-gray-500">Latitude</h3>
<p className="text-gray-900 font-mono">{session.geolocation.latitude}</p>
</div>
<div>
<h3 className="text-sm font-medium text-gray-500">Longitude</h3>
<p className="text-gray-900 font-mono">{session.geolocation.longitude}</p>
</div>
</div>
</div>
)}
{/* Metadados adicionais */}
<div className="p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Metadados do Sistema</h2>
<div className="bg-gray-50 rounded-lg p-4">
<pre className="text-sm text-gray-700 overflow-x-auto">
{JSON.stringify(session.metadata, null, 2)}
</pre>
</div>
</div>
{/* Ações */}
<div className="p-6 flex flex-col sm:flex-row gap-4">
<a
href={`/api/sessions/${session.id}/report`}
className="inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
>
Baixar Relatório PDF
</a>
{session.videoPath && (
<button className="inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
Visualizar Vídeo
</button>
)}
<Link
to="/sessions/new"
className="inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Nova Sessão
</Link>
</div>
</div>
<div className="mt-4 text-center text-sm text-gray-500">
Relatório gerado pelo sistema AdvX Provas - Coleta de Evidências Digitais
</div>
</div>
</div>
);
};
export default SessionReport;