From 59ea7622273b656e5fbad2760e56d175a39e7496 Mon Sep 17 00:00:00 2001 From: "Rodrigo B. de Sousa Martins" Date: Mon, 26 May 2025 13:08:32 -0300 Subject: [PATCH] frontend: fix PublicProvaUpload Signed-off-by: Rodrigo B. de Sousa Martins --- src/components/provas/ClientProvaUpload.tsx | 63 +++++++++++++++++---- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/components/provas/ClientProvaUpload.tsx b/src/components/provas/ClientProvaUpload.tsx index d2c893a..5a29463 100644 --- a/src/components/provas/ClientProvaUpload.tsx +++ b/src/components/provas/ClientProvaUpload.tsx @@ -3,6 +3,8 @@ import { useNavigate } from 'react-router-dom'; import { useDropzone } from 'react-dropzone'; import { toast } from 'react-toastify'; +// Página pública: NÃO requer autenticação do usuário + export function ClientProvaUpload() { const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); @@ -43,9 +45,31 @@ export function ClientProvaUpload() { } }, []); + // Validação extra do arquivo antes do upload + function validateFile(file: File | null): string | null { + if (!file) return 'Nenhum arquivo selecionado.'; + const allowedTypes = [ + 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf' + ]; + if (!allowedTypes.includes(file.type)) { + return 'Tipo de arquivo não suportado. Use JPG, PNG, GIF, WEBP ou PDF.'; + } + if (file.size > 10 * 1024 * 1024) { + return 'Arquivo excede o tamanho máximo de 10MB.'; + } + return null; + } + const onDrop = useCallback(async (acceptedFiles: File[]) => { if (acceptedFiles.length > 0) { const selectedFile = acceptedFiles[0]; + const validationError = validateFile(selectedFile); + if (validationError) { + toast.error(validationError); + setFile(null); + setFilePreview(null); + return; + } setFile(selectedFile); // Criar preview para imagens @@ -77,6 +101,12 @@ export function ClientProvaUpload() { toast.error('Por favor, selecione um arquivo primeiro'); return; } + // Validação extra antes do envio + const validationError = validateFile(file); + if (validationError) { + toast.error(validationError); + return; + } setUploading(true); setUploadProgress(0); @@ -120,7 +150,7 @@ export function ClientProvaUpload() { // Adicionar timestamp preciso do cliente formData.append('client_timestamp', new Date().toISOString()); - // Criar simulação de progresso + // Simulação de progresso const progressInterval = setInterval(() => { setUploadProgress(prev => { if (prev >= 95) { @@ -131,10 +161,18 @@ export function ClientProvaUpload() { }); }, 300); - const response = await fetch('/api/provas/upload', { - method: 'POST', - body: formData, - }); + // Upload sem autenticação (página pública) + let response: Response; + try { + response = await fetch('/api/provas/upload', { + method: 'POST', + body: formData, + }); + } catch (networkError) { + clearInterval(progressInterval); + setUploadProgress(0); + throw new Error('Erro de conexão. Verifique sua internet e tente novamente.'); + } clearInterval(progressInterval); setUploadProgress(100); @@ -155,9 +193,14 @@ export function ClientProvaUpload() { } // Ler o corpo da resposta - const result = await response.json(); - - if (result.id) { + let result: any; + try { + result = await response.json(); + } catch { + throw new Error('Resposta inesperada do servidor.'); + } + + if (result && result.id) { toast.success('Arquivo enviado com sucesso!'); // Navegar para a página de status navigate(`/public/provas/status/${result.id}`); @@ -165,10 +208,10 @@ export function ClientProvaUpload() { throw new Error('ID do documento não encontrado na resposta'); } - } catch (error) { + } catch (error: any) { console.error('Erro detalhado no upload:', error); setError(error.message || 'Erro ao enviar arquivo. Por favor, tente novamente.'); - toast.error('Falha no upload: ' + error.message); + toast.error('Falha no upload: ' + (error.message || 'Erro desconhecido')); } finally { setUploading(false); }