From 422dc6f022b962096a91e249230ec5c6f5bc51ab Mon Sep 17 00:00:00 2001 From: deyvisonwanderley Date: Tue, 18 Nov 2025 06:34:55 -0300 Subject: [PATCH] refactor: enhance ProtectedRoute and dashboard components for improved access control and user experience - Updated the ProtectedRoute component to optimize access checking logic, preventing unnecessary re-checks and improving authentication flow. - Enhanced the dashboard page to automatically open the login modal for authentication errors and refined loading states for better user feedback. - Improved UI elements across various components for consistency and visual appeal, including updated tab styles and enhanced alert messages. - Removed redundant footer from the vacation management page to streamline the interface. --- .../src/lib/components/ProtectedRoute.svelte | 53 +- apps/web/src/routes/(dashboard)/+page.svelte | 21 +- .../routes/(dashboard)/perfil/+page.svelte | 792 +++++++----------- .../recursos-humanos/ferias/+page.svelte | 6 - packages/backend/convex/monitoramento.ts | 67 +- 5 files changed, 398 insertions(+), 541 deletions(-) diff --git a/apps/web/src/lib/components/ProtectedRoute.svelte b/apps/web/src/lib/components/ProtectedRoute.svelte index 3e41367..0bbe10f 100644 --- a/apps/web/src/lib/components/ProtectedRoute.svelte +++ b/apps/web/src/lib/components/ProtectedRoute.svelte @@ -21,11 +21,26 @@ let isChecking = $state(true); let hasAccess = $state(false); let timeoutId: ReturnType | null = null; + let hasCheckedOnce = $state(false); + let lastUserState = $state(undefined); const currentUser = useQuery(api.auth.getCurrentUser, {}); - // Usar $effect para reagir às mudanças na query + // Usar $effect para reagir apenas às mudanças na query currentUser $effect(() => { - checkAccess(); + // Não verificar novamente se já tem acesso concedido e usuário está autenticado + if (hasAccess && currentUser?.data) { + lastUserState = currentUser; + return; + } + + // Evitar loop: só verificar se currentUser realmente mudou + // Comparar dados, não o objeto proxy + const currentData = currentUser?.data; + const lastData = lastUserState?.data; + if (currentData !== lastData || (currentUser === undefined) !== (lastUserState === undefined)) { + lastUserState = currentUser; + checkAccess(); + } }); function checkAccess() { @@ -42,6 +57,9 @@ return; } + // Marcar que já verificou pelo menos uma vez + hasCheckedOnce = true; + // Se a query retornou dados, verificar autenticação if (currentUser?.data) { // Verificar roles @@ -67,20 +85,29 @@ return; } - // Se não tem dados e requer autenticação, aguardar um pouco antes de redirecionar - // (pode estar carregando ainda) + // Se não tem dados e requer autenticação if (requireAuth && !currentUser?.data) { + // Se a query já retornou (não está mais undefined), finalizar estado + if (currentUser !== undefined) { + const currentPath = window.location.pathname; + // Evitar redirecionamento em loop - verificar se já está na URL de erro + const urlParams = new URLSearchParams(window.location.search); + if (!urlParams.has('error')) { + // Só redirecionar se não estiver em loop + if (!hasCheckedOnce || currentUser === null) { + window.location.href = `${redirectTo}?error=auth_required&redirect=${encodeURIComponent(currentPath)}`; + return; + } + } + // Se já tem erro na URL, permitir renderização para mostrar o alerta + isChecking = false; + hasAccess = true; + return; + } + + // Se ainda está carregando (undefined), aguardar isChecking = true; hasAccess = false; - - // Aguardar 3 segundos antes de redirecionar (dar tempo para a query carregar) - timeoutId = setTimeout(() => { - // Verificar novamente antes de redirecionar - if (!currentUser?.data) { - const currentPath = window.location.pathname; - window.location.href = `${redirectTo}?error=auth_required&redirect=${encodeURIComponent(currentPath)}`; - } - }, 3000); return; } diff --git a/apps/web/src/routes/(dashboard)/+page.svelte b/apps/web/src/routes/(dashboard)/+page.svelte index ad73bc2..1264509 100644 --- a/apps/web/src/routes/(dashboard)/+page.svelte +++ b/apps/web/src/routes/(dashboard)/+page.svelte @@ -9,12 +9,13 @@ import { UserPlus, Mail } from "lucide-svelte"; import { useAuth } from "@mmailaender/convex-better-auth-svelte/svelte"; import ProtectedRoute from "$lib/components/ProtectedRoute.svelte"; + import { loginModalStore } from "$lib/stores/loginModal.svelte"; let { data } = $props(); const auth = useAuth(); - const isLoading = $derived(auth.isLoading && !data.currentUser); - const isAuthenticated = $derived(auth.isAuthenticated || !!data.currentUser); + const isLoading = $derived(auth.isLoading && !data?.currentUser); + const isAuthenticated = $derived(auth.isAuthenticated || !!data?.currentUser); $inspect({ isLoading, isAuthenticated }); @@ -56,6 +57,11 @@ redirectRoute = route; showAlert = true; + // Se for erro de autenticação, abrir modal de login automaticamente + if (error === "auth_required") { + loginModalStore.open(route || to.url.pathname); + } + // Limpar URL usando SvelteKit (após router estar inicializado) try { replaceState(to.url.pathname, {}); @@ -75,6 +81,17 @@ onMount(() => { mounted = true; + // Verificar se há erro na URL ao carregar a página + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("error")) { + const error = urlParams.get("error"); + const route = urlParams.get("route") || urlParams.get("redirect") || ""; + + if (error === "auth_required") { + loginModalStore.open(route || window.location.pathname); + } + } + // Atualizar relógio e forçar refresh das queries a cada segundo const interval = setInterval(() => { currentTime = new Date(); diff --git a/apps/web/src/routes/(dashboard)/perfil/+page.svelte b/apps/web/src/routes/(dashboard)/perfil/+page.svelte index 95ea561..003cfa6 100644 --- a/apps/web/src/routes/(dashboard)/perfil/+page.svelte +++ b/apps/web/src/routes/(dashboard)/perfil/+page.svelte @@ -11,7 +11,7 @@ import ProtectedRoute from '$lib/components/ProtectedRoute.svelte'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import type { FunctionReturnType } from 'convex/server'; - import { X, Calendar } from 'lucide-svelte'; + import { X, Calendar, Users, Clock, CheckCircle2, Eye, FileCheck, CalendarDays, User, Mail, Shield, Briefcase, Hash, CreditCard, Building2, CheckCircle, ListChecks, Info } from 'lucide-svelte'; import TicketCard from '$lib/components/chamados/TicketCard.svelte'; import TicketTimeline from '$lib/components/chamados/TicketTimeline.svelte'; import { chamadosStore } from '$lib/stores/chamados'; @@ -680,48 +680,26 @@
@@ -729,46 +707,20 @@ @@ -776,26 +728,13 @@