diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 158bea9..eb0ae29 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -67,9 +67,14 @@ export const useAuth = () => { const { useUserStore } = await import('../stores/useUserStore'); const profilePromise = useUserStore.getState().fetchCurrentUser(session.user.id); - const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout fetchCurrentUser')), 15000)); + + let timeoutId: ReturnType; + const timeoutPromise = new Promise((_, reject) => { + timeoutId = setTimeout(() => reject(new Error('Timeout fetchCurrentUser')), 15000); + }); await Promise.race([profilePromise, timeoutPromise]); + clearTimeout(timeoutId!); console.log('✅ useAuth: Perfil carregado com sucesso'); } catch (err) { console.error('❌ useAuth: Erro/Timeout ao carregar perfil:', err); @@ -134,8 +139,14 @@ export const useAuth = () => { if (!currentUser || currentUser.id !== session.user.id) { const profilePromise = useUserStore.getState().fetchCurrentUser(session.user.id); - const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout fetchCurrentUser AuthChange')), 15000)); + + let timeoutId: ReturnType; + const timeoutPromise = new Promise((_, reject) => { + timeoutId = setTimeout(() => reject(new Error('Timeout fetchCurrentUser AuthChange')), 15000); + }); + await Promise.race([profilePromise, timeoutPromise]); + clearTimeout(timeoutId!); } } catch (err) { console.error('Erro/Timeout ao sincronizar perfil no AuthChange:', err); @@ -159,7 +170,10 @@ export const useAuth = () => { try { console.log('🔄 syncUserProfile: Sincronizando dados:', user.email); // Wrapper de timeout para operações de banco - const dbTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout syncUserProfile')), 15000)); + let fetchTimeoutId: ReturnType; + const fetchTimeout = new Promise((_, reject) => { + fetchTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile fetch')), 15000); + }); // Verificar se o usuário existe na tabela usuarios // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -170,7 +184,8 @@ export const useAuth = () => { .single(); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { data: existingUser, error: fetchError } = await Promise.race([fetchPromise, dbTimeout]) as any; + const { data: existingUser, error: fetchError } = await Promise.race([fetchPromise, fetchTimeout]) as any; + clearTimeout(fetchTimeoutId!); if (fetchError && fetchError.code !== 'PGRST116') { console.error('Erro ao buscar usuário (Sync):', fetchError); @@ -179,6 +194,11 @@ export const useAuth = () => { // Se não existe, criar registro na tabela usuarios if (!existingUser) { + let insertTimeoutId: ReturnType; + const insertTimeout = new Promise((_, reject) => { + insertTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile insert')), 15000); + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const insertPromise = (supabase as any) .from('usuarios') @@ -190,7 +210,8 @@ export const useAuth = () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { error: insertError } = await Promise.race([insertPromise, dbTimeout]) as any; + const { error: insertError } = await Promise.race([insertPromise, insertTimeout]) as any; + clearTimeout(insertTimeoutId!); if (insertError) { console.error('Erro ao criar perfil do usuário:', insertError); diff --git a/src/hooks/useSupabaseData.ts b/src/hooks/useSupabaseData.ts index b5e770e..67fdf6e 100644 --- a/src/hooks/useSupabaseData.ts +++ b/src/hooks/useSupabaseData.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { supabase } from '../lib/supabase'; // import { useConfigStore } from '../stores/configStore'; // import type { ConfigItem, CondicaoClimatica } from '../stores/configStore'; @@ -9,11 +9,11 @@ import { supabase } from '../lib/supabase'; export const useSupabaseData = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - + // const configStore = useConfigStore(); // Carregar tipos de atividade do Supabase - const loadTiposAtividade = async () => { + const loadTiposAtividade = useCallback(async () => { try { console.log('🔄 Carregando tipos de atividade do Supabase...'); const { data, error } = await supabase @@ -28,7 +28,7 @@ export const useSupabaseData = () => { } console.log('✅ Tipos de atividade carregados:', data); - + // Converter dados do Supabase para o formato da store // const tiposAtividade: ConfigItem[] = data?.map((item, index) => ({ // id: item.id.toString(), @@ -40,16 +40,16 @@ export const useSupabaseData = () => { // Atualizar store com dados do Supabase // configStore.tiposAtividade = tiposAtividade; console.log('📊 Tipos de atividade carregados:', data); - + return data || []; } catch (err) { console.error('❌ Erro ao carregar tipos de atividade:', err); throw err; } - }; + }, []); // Carregar condições climáticas do Supabase - const loadCondicoesClimaticas = async () => { + const loadCondicoesClimaticas = useCallback(async () => { try { console.log('🔄 Carregando condições climáticas do Supabase...'); const { data, error } = await supabase @@ -64,7 +64,7 @@ export const useSupabaseData = () => { } console.log('✅ Condições climáticas carregadas:', data); - + // Converter dados do Supabase para o formato da store // const condicoesClimaticas: CondicaoClimatica[] = data?.map((item, index) => ({ // id: item.id.toString(), @@ -78,20 +78,20 @@ export const useSupabaseData = () => { // Atualizar store com dados do Supabase // configStore.condicoesClimaticas = condicoesClimaticas; console.log('📊 Condições climáticas carregadas:', data); - + return data || []; } catch (err) { console.error('❌ Erro ao carregar condições climáticas:', err); throw err; } - }; + }, []); // Carregar funcionários do Supabase - const loadFuncionarios = async () => { + const loadFuncionarios = useCallback(async () => { try { console.log('🔄 Carregando funcionários do Supabase...'); const { data, error } = await supabase - .from('funcionarios') + .from('usuarios') .select('*') .eq('ativo', true) .order('nome'); @@ -107,10 +107,10 @@ export const useSupabaseData = () => { console.error('❌ Erro ao carregar funcionários:', err); throw err; } - }; + }, []); // Função principal para carregar todos os dados - const loadAllData = async () => { + const loadAllData = useCallback(async () => { try { setLoading(true); setError(null); @@ -130,12 +130,12 @@ export const useSupabaseData = () => { } finally { setLoading(false); } - }; + }, [loadTiposAtividade, loadCondicoesClimaticas, loadFuncionarios]); // Carregar dados automaticamente quando o hook é usado useEffect(() => { loadAllData(); - }, []); + }, [loadAllData]); return { loading, diff --git a/src/lib/cacheManager.ts b/src/lib/cacheManager.ts index 1514e22..1266b6c 100644 --- a/src/lib/cacheManager.ts +++ b/src/lib/cacheManager.ts @@ -15,7 +15,7 @@ const CACHE_CONFIG = { defaultTTL: 1000 * 60 * 30, // 30 minutos maxCacheSize: 50 * 1024 * 1024, // 50MB compressionThreshold: 10 * 1024, // 10KB - prefetchTables: ['obras', 'funcionarios', 'tipos_atividade', 'equipamentos'] + prefetchTables: ['obras', 'usuarios', 'tipos_atividade', 'inventario_equipamentos'] }; /** diff --git a/src/pages/AuthCallback.tsx b/src/pages/AuthCallback.tsx index d85b206..2faba9f 100644 --- a/src/pages/AuthCallback.tsx +++ b/src/pages/AuthCallback.tsx @@ -41,7 +41,8 @@ export const AuthCallback: React.FC = () => { // Garantir permissões do Super Admin if (session.user.email === 'admtracksteel@gmail.com') { console.log('👑 Super Admin detectado! Atualizando permissões...'); - await supabase.from('usuarios' as never).upsert({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await (supabase.from('usuarios') as any).upsert({ id: session.user.id, email: session.user.email, nome: session.user.user_metadata?.full_name || 'Super Admin', diff --git a/src/pages/ObraDetails.tsx b/src/pages/ObraDetails.tsx index 8506fdb..770f577 100644 --- a/src/pages/ObraDetails.tsx +++ b/src/pages/ObraDetails.tsx @@ -73,10 +73,17 @@ export default function ObraDetails() { }, [id]); const fetchData = async (obraId: string) => { - // Check for valid UUID + // Check if ID is provided + if (!obraId) { + console.error('ID da obra não fornecido'); + setIsLoading(false); + return; + } + + // Previne erro 400 Bad Request no Supabase const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; if (!uuidRegex.test(obraId)) { - console.error('ID inválido:', obraId); + console.warn('ID da obra não é um UUID padrão. Ignorando carregamento dos detalhes.'); setIsLoading(false); return; } @@ -84,7 +91,10 @@ export default function ObraDetails() { setIsLoading(true); try { // Fetch Obra Details from Supabase - const { data: obraData, error: obraError } = await (supabase.from('obras') as any).select(`*`).eq('id', obraId).single(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response: { data: any, error: any } = await (supabase.from('obras') as any).select(`*`).eq('id', obraId).single(); + const obraData = response.data; + const obraError = response.error; if (obraError) { console.error('Erro ao buscar obra:', obraError); @@ -137,14 +147,19 @@ export default function ObraDetails() { if (rdosError) { console.error('Erro ao buscar RDOs:', rdosError); } else { - const mappedRdos: RDO[] = rdosData.map((r: any) => ({ - id: r.id, - data: r.data_relatorio, - status: r.status, - responsavel: r.responsavel?.nome || 'Desconhecido', - atividades: r.rdo_atividades?.[0]?.count || 0, - ocorrencias: r.rdo_ocorrencias?.[0]?.count || 0 - })); + const mappedRdos: RDO[] = rdosData.map((r: Record | null) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rData = r as Record; + return { + id: rData.id as string, + data: rData.data_relatorio as string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + status: rData.status as any, + responsavel: rData.responsavel?.nome || 'Desconhecido', + atividades: rData.rdo_atividades?.[0]?.count || 0, + ocorrencias: rData.rdo_ocorrencias?.[0]?.count || 0 + }; + }); setRdos(mappedRdos); } @@ -169,13 +184,17 @@ export default function ObraDetails() { if (fotosError) { console.error('Erro ao buscar fotos:', fotosError); } else { - const mappedFotos: Foto[] = fotosData.map((f: any) => ({ - id: f.id, - url: f.url_storage, // This might need getPublicUrl if it's a path - data: f.created_at, - descricao: f.descricao || f.nome_arquivo, - nome_arquivo: f.nome_arquivo - })); + const mappedFotos: Foto[] = fotosData.map((f: Record | null) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const fData = f as Record; + return { + id: fData.id as string, + url: fData.url_storage as string, // This might need getPublicUrl if it's a path + data: fData.created_at as string, + descricao: (fData.descricao as string) || (fData.nome_arquivo as string), + nome_arquivo: fData.nome_arquivo as string + }; + }); // If url_storage is a path, we should transform it. // Assuming for now it is a signed url or public url if stored that way. // If it is a relative path in bucket, we need `supabase.storage.from(...).getPublicUrl(...)`. @@ -193,8 +212,8 @@ export default function ObraDetails() { const handleSaveObra = async () => { if (!editedObra || !obra) return; try { - const { error } = await (supabase - .from('obras') as any) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response: { error: any } = await (supabase.from('obras') as any) .update({ descricao: editedObra.descricao, data_inicio: editedObra.dataInicio, @@ -202,6 +221,8 @@ export default function ObraDetails() { }) .eq('id', obra.id); + const error = response.error; + if (error) throw error; setObra(editedObra); @@ -394,12 +415,8 @@ export default function ObraDetails() {
-
+
+