From 4ffa403c46e89e246b08f25641178e75a3492265 Mon Sep 17 00:00:00 2001 From: killer-cf Date: Thu, 13 Nov 2025 00:12:16 -0300 Subject: [PATCH] Refactor FileUpload component and improve type safety - Rename imported File icon to FileIcon to avoid naming conflicts - Update onUpload type to use globalThis.File - Reformat loadExistingFile and related code for better readability - Add stricter typing for funcionarioId and related data in documentos and editar pages - Improve error handling and response validation in file upload logic - Add keyed each blocks for better Svelte list rendering stability - Fix minor formatting issues in breadcrumb links --- apps/web/src/lib/components/FileUpload.svelte | 97 ++++++++----------- .../[funcionarioId]/documentos/+page.svelte | 54 +++++++---- .../[funcionarioId]/editar/+page.svelte | 4 +- 3 files changed, 81 insertions(+), 74 deletions(-) diff --git a/apps/web/src/lib/components/FileUpload.svelte b/apps/web/src/lib/components/FileUpload.svelte index 442fdbc..f02260a 100644 --- a/apps/web/src/lib/components/FileUpload.svelte +++ b/apps/web/src/lib/components/FileUpload.svelte @@ -3,7 +3,7 @@ import { ExternalLink, FileText, - File, + File as FileIcon, Upload, Trash2, Eye, @@ -16,7 +16,7 @@ value?: string; // storageId disabled?: boolean; required?: boolean; - onUpload: (file: File) => Promise; + onUpload: (file: globalThis.File) => Promise; onRemove: () => Promise; } @@ -48,38 +48,33 @@ "image/png", ]; - // Buscar URL do arquivo quando houver um storageId - $effect(() => { - if (value && !fileName) { - // Tem storageId mas não é um upload recente - loadExistingFile(value); - } - }); - - async function loadExistingFile(storageId: string) { - try { - const url = await client.storage.getUrl(storageId as any); - if (url) { - fileUrl = url; - fileName = "Documento anexado"; - // Detectar tipo pelo URL ou assumir PDF - if (url.includes(".pdf") || url.includes("application/pdf")) { - fileType = "application/pdf"; - } else { - fileType = "image/jpeg"; - previewUrl = url; // Para imagens, a URL serve como preview - } - } - } catch (err) { - console.error("Erro ao carregar arquivo existente:", err); - } - } + // Buscar URL do arquivo quando houver um storageId + $effect(() => { + if (value && !fileName) { + // Tem storageId mas não é um upload recente + loadExistingFile(value); + } + }); + async function loadExistingFile(storageId: string) { + try { + const url = await client.storage.getUrl(storageId as any); + if (url) { async function handleFileSelect(event: Event) { const target = event.target as HTMLInputElement; const file = target.files?.[0]; - - if (!file) return; + // Detectar tipo pelo URL ou assumir PDF + if (url.includes('.pdf') || url.includes('application/pdf')) { + fileType = 'application/pdf'; + } else { + fileType = 'image/jpeg'; + previewUrl = url; // Para imagens, a URL serve como preview + } + } + } catch (err) { + console.error('Erro ao carregar arquivo existente:', err); + } + } error = null; @@ -187,32 +182,22 @@ {disabled} /> - {#if value || fileName} -
- -
- {#if previewUrl} - Preview - {:else if fileType === "application/pdf" || fileName.endsWith(".pdf")} -
- -
- {:else} -
- -
- {/if} -
+ {#if value || fileName} +
+ +
+ {#if previewUrl} + Preview + {:else if fileType === 'application/pdf' || fileName.endsWith('.pdf')} +
+ +
+ {:else} +
+ +
+ {/if} +
diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/documentos/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/documentos/+page.svelte index dc4b83f..9741b79 100644 --- a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/documentos/+page.svelte +++ b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/documentos/+page.svelte @@ -11,15 +11,16 @@ categoriasDocumentos, getDocumentosByCategoria } from '$lib/utils/documentos'; + import type { Id, Doc } from '@sgse-app/backend/convex/_generated/dataModel'; const client = useConvexClient(); - let funcionarioId = $derived($page.params.funcionarioId as string); + let funcionarioId = $derived($page.params.funcionarioId as Id<'funcionarios'>); - let funcionario = $state(null); + let funcionario = $state | null>(null); let documentosStorage = $state>({}); let loading = $state(true); - let filtro = $state('todos'); // todos, enviados, pendentes + let filtro = $state<'todos' | 'enviados' | 'pendentes'>('todos'); // todos, enviados, pendentes async function load() { try { @@ -27,7 +28,7 @@ // Carregar dados do funcionário const data = await client.query(api.funcionarios.getById, { - id: funcionarioId as any + id: funcionarioId }); if (!data) { @@ -39,8 +40,10 @@ // Mapear storage IDs dos documentos documentos.forEach((doc) => { - if ((data as any)[doc.campo]) { - documentosStorage[doc.campo] = (data as any)[doc.campo]; + const campo = doc.campo as keyof Doc<'funcionarios'>; + const valor = data[campo]; + if (typeof valor === 'string') { + documentosStorage[doc.campo] = valor; } }); } catch (err) { @@ -63,13 +66,23 @@ body: file }); - const { storageId } = await result.json(); + const uploadResponse = await result.json(); + if ( + !uploadResponse || + typeof uploadResponse !== 'object' || + !('storageId' in uploadResponse) || + typeof uploadResponse.storageId !== 'string' + ) { + throw new Error('Resposta inválida ao fazer upload'); + } + + const storageId = uploadResponse.storageId; // Atualizar documento no funcionário await client.mutation(api.documentos.updateDocumento, { - funcionarioId: funcionarioId as any, + funcionarioId, campo, - storageId: storageId as any + storageId: storageId as Id<'_storage'> }); // Atualizar localmente @@ -77,8 +90,11 @@ // Recarregar await load(); - } catch (err: any) { - throw new Error(err?.message || 'Erro ao fazer upload'); + } catch (err) { + if (err instanceof Error && err.message) { + throw err; + } + throw new Error('Erro ao fazer upload'); } } @@ -86,7 +102,7 @@ try { // Atualizar documento no funcionário (set to null) await client.mutation(api.documentos.updateDocumento, { - funcionarioId: funcionarioId as any, + funcionarioId, campo, storageId: null }); @@ -96,8 +112,10 @@ // Recarregar await load(); - } catch (err: any) { - alert('Erro ao remover documento: ' + (err?.message || '')); + } catch (err) { + const mensagem = + err instanceof Error && err.message ? err.message : 'Erro ao remover documento'; + alert('Erro ao remover documento: ' + mensagem); } } @@ -130,7 +148,9 @@