refactor: enhance Sidebar component with Better Auth integration
- Replaced the use of `useConvexClient` with `useQuery` for fetching the current user. - Updated avatar URL retrieval to utilize the current user data from Better Auth. - Refactored login and logout functions to use the new `authClient` methods for improved authentication flow. - Cleaned up the component structure and styling for better readability and maintainability. - Adjusted sidebar and footer styles for consistency with the new design system.
This commit is contained in:
@@ -5,58 +5,66 @@
|
||||
import type { Snippet } from "svelte";
|
||||
import { authStore } from "$lib/stores/auth.svelte";
|
||||
import { loginModalStore } from "$lib/stores/loginModal.svelte";
|
||||
import { useConvexClient } from "convex-svelte";
|
||||
import { useQuery } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
import NotificationBell from "$lib/components/chat/NotificationBell.svelte";
|
||||
import ChatWidget from "$lib/components/chat/ChatWidget.svelte";
|
||||
import PresenceManager from "$lib/components/chat/PresenceManager.svelte";
|
||||
import { getBrowserInfo } from "$lib/utils/browserInfo";
|
||||
import { getAvatarUrl } from "$lib/utils/avatarGenerator";
|
||||
import { Menu, User, Home, UserPlus, XCircle, LogIn, Tag, Plus, Check } from "lucide-svelte";
|
||||
import {
|
||||
Menu,
|
||||
User,
|
||||
Home,
|
||||
UserPlus,
|
||||
XCircle,
|
||||
LogIn,
|
||||
Tag,
|
||||
Plus,
|
||||
Check,
|
||||
} from "lucide-svelte";
|
||||
import { authClient } from "$lib/auth";
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
|
||||
const convex = useConvexClient();
|
||||
|
||||
// Caminho atual da página
|
||||
const currentPath = $derived(page.url.pathname);
|
||||
|
||||
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
||||
|
||||
// Função para obter a URL do avatar/foto do usuário
|
||||
const avatarUrlDoUsuario = $derived(() => {
|
||||
const usuario = authStore.usuario;
|
||||
if (!usuario) return null;
|
||||
|
||||
if (!currentUser.data) return null;
|
||||
|
||||
// Prioridade: fotoPerfilUrl > avatar > fallback com nome
|
||||
if (usuario.fotoPerfilUrl) {
|
||||
return usuario.fotoPerfilUrl;
|
||||
}
|
||||
if (usuario.avatar) {
|
||||
return getAvatarUrl(usuario.avatar);
|
||||
if (currentUser.data.fotoPerfil) {
|
||||
return currentUser.data.fotoPerfil;
|
||||
}
|
||||
|
||||
// Fallback: gerar avatar baseado no nome
|
||||
return getAvatarUrl(usuario.nome);
|
||||
return getAvatarUrl(currentUser.data.nome);
|
||||
});
|
||||
|
||||
// Função para gerar classes do menu ativo
|
||||
function getMenuClasses(isActive: boolean) {
|
||||
const baseClasses = "group font-semibold flex items-center justify-center gap-2 text-center p-3.5 rounded-xl border-2 transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105";
|
||||
|
||||
const baseClasses =
|
||||
"group font-semibold flex items-center justify-center gap-2 text-center p-3.5 rounded-xl border-2 transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105";
|
||||
|
||||
if (isActive) {
|
||||
return `${baseClasses} border-primary bg-primary text-white shadow-lg scale-105`;
|
||||
}
|
||||
|
||||
return `${baseClasses} border-primary/30 bg-gradient-to-br from-base-100 to-base-200 text-base-content hover:from-primary hover:to-primary/80 hover:text-white`;
|
||||
|
||||
return `${baseClasses} border-primary/30 bg-linear-to-br from-base-100 to-base-200 text-base-content hover:from-primary hover:to-primary/80 hover:text-white`;
|
||||
}
|
||||
|
||||
// Função para gerar classes do botão "Solicitar Acesso"
|
||||
function getSolicitarClasses(isActive: boolean) {
|
||||
const baseClasses = "group font-semibold flex items-center justify-center gap-2 text-center p-3.5 rounded-xl border-2 transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105";
|
||||
|
||||
const baseClasses =
|
||||
"group font-semibold flex items-center justify-center gap-2 text-center p-3.5 rounded-xl border-2 transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105";
|
||||
|
||||
if (isActive) {
|
||||
return `${baseClasses} border-success bg-success text-white shadow-lg scale-105`;
|
||||
}
|
||||
|
||||
return `${baseClasses} border-success/30 bg-gradient-to-br from-success/10 to-success/20 text-base-content hover:from-success hover:to-success/80 hover:text-white`;
|
||||
|
||||
return `${baseClasses} border-success/30 bg-linear-to-br from-success/10 to-success/20 text-base-content hover:from-success hover:to-success/80 hover:text-white`;
|
||||
}
|
||||
|
||||
const setores = [
|
||||
@@ -113,88 +121,55 @@
|
||||
showAboutModal = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* FASE 2: Login dual - tenta Better Auth primeiro, fallback para sistema customizado
|
||||
*/
|
||||
async function handleLogin(e: Event) {
|
||||
e.preventDefault();
|
||||
erroLogin = "";
|
||||
carregandoLogin = true;
|
||||
|
||||
try {
|
||||
// FASE 2: Por enquanto, sistema customizado funciona normalmente
|
||||
// Quando Better Auth estiver configurado, tentaremos primeiro:
|
||||
//
|
||||
// try {
|
||||
// await authStore.loginWithBetterAuth(matricula, senha);
|
||||
// closeLoginModal();
|
||||
// goto("/");
|
||||
// return;
|
||||
// } catch (betterAuthError) {
|
||||
// // Fallback para sistema customizado
|
||||
// console.log("Better Auth falhou, usando sistema customizado");
|
||||
// }
|
||||
// const browserInfo = await getBrowserInfo();
|
||||
|
||||
// Sistema customizado (atual e funcionando)
|
||||
const browserInfo = await getBrowserInfo();
|
||||
|
||||
const resultado = await convex.mutation(api.autenticacao.login, {
|
||||
matriculaOuEmail: matricula.trim(),
|
||||
senha: senha,
|
||||
userAgent: browserInfo.userAgent || undefined,
|
||||
ipAddress: browserInfo.ipAddress,
|
||||
});
|
||||
const result = await authClient.signIn.email(
|
||||
{ email: matricula.trim(), password: senha },
|
||||
{
|
||||
onError: (ctx) => {
|
||||
alert(ctx.error.message);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (resultado.sucesso) {
|
||||
authStore.login(resultado.usuario, resultado.token);
|
||||
closeLoginModal();
|
||||
|
||||
// Redirecionar baseado no role
|
||||
if (resultado.usuario.role.nome === "ti" || resultado.usuario.role.nivel === 0) {
|
||||
goto("/ti/painel-administrativo");
|
||||
} else if (resultado.usuario.role.nome === "rh") {
|
||||
goto("/recursos-humanos");
|
||||
} else {
|
||||
goto("/");
|
||||
}
|
||||
} else {
|
||||
erroLogin = resultado.erro || "Erro ao fazer login";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erro ao fazer login:", error);
|
||||
erroLogin = "Erro ao conectar com o servidor. Tente novamente.";
|
||||
} finally {
|
||||
carregandoLogin = false;
|
||||
if (result.data) {
|
||||
closeLoginModal();
|
||||
goto("/");
|
||||
} else {
|
||||
erroLogin = "Erro ao fazer login";
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
if (authStore.token) {
|
||||
try {
|
||||
await convex.mutation(api.autenticacao.logout, {
|
||||
token: authStore.token,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Erro ao fazer logout:", error);
|
||||
}
|
||||
const result = await authClient.signOut();
|
||||
if (result.error) {
|
||||
console.error("Sign out error:", result.error);
|
||||
}
|
||||
authStore.logout();
|
||||
goto("/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Header Fixo acima de tudo -->
|
||||
<div class="navbar bg-gradient-to-r from-primary/30 via-primary/20 to-primary/30 backdrop-blur-sm shadow-lg border-b border-primary/10 px-6 lg:px-8 fixed top-0 left-0 right-0 z-50 min-h-24">
|
||||
<div
|
||||
class="navbar bg-linear-to-r from-primary/30 via-primary/20 to-primary/30 backdrop-blur-sm shadow-lg border-b border-primary/10 px-6 lg:px-8 fixed top-0 left-0 right-0 z-50 min-h-24"
|
||||
>
|
||||
<div class="flex-none lg:hidden">
|
||||
<label
|
||||
for="my-drawer-3"
|
||||
<label
|
||||
for="my-drawer-3"
|
||||
class="relative flex items-center justify-center w-14 h-14 rounded-2xl overflow-hidden cursor-pointer group transition-all duration-300 hover:scale-105"
|
||||
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 8px 24px -4px rgba(102, 126, 234, 0.4);"
|
||||
aria-label="Abrir menu"
|
||||
>
|
||||
<!-- Efeito de brilho no hover -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
></div>
|
||||
|
||||
<!-- Ícone de menu hambúrguer -->
|
||||
<Menu
|
||||
class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300"
|
||||
@@ -206,42 +181,54 @@
|
||||
<div class="flex-1 flex items-center gap-4 lg:gap-6">
|
||||
<!-- Logo MODERNO do Governo -->
|
||||
<div class="avatar">
|
||||
<div
|
||||
<div
|
||||
class="w-16 lg:w-20 rounded-2xl shadow-xl p-2 relative overflow-hidden group transition-all duration-300 hover:scale-105"
|
||||
style="background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); border: 2px solid rgba(102, 126, 234, 0.1);"
|
||||
>
|
||||
<!-- Efeito de brilho no hover -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-br from-primary/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
></div>
|
||||
|
||||
<!-- Logo -->
|
||||
<img
|
||||
src={logo}
|
||||
alt="Logo do Governo de PE"
|
||||
class="w-full h-full object-contain relative z-10 transition-transform duration-300 group-hover:scale-105"
|
||||
<img
|
||||
src={logo}
|
||||
alt="Logo do Governo de PE"
|
||||
class="w-full h-full object-contain relative z-10 transition-transform duration-300 group-hover:scale-105"
|
||||
style="filter: drop-shadow(0 2px 4px rgba(0,0,0,0.08));"
|
||||
/>
|
||||
|
||||
|
||||
<!-- Brilho sutil no canto -->
|
||||
<div class="absolute top-0 right-0 w-8 h-8 bg-gradient-to-br from-white/40 to-transparent rounded-bl-full opacity-70"></div>
|
||||
<div
|
||||
class="absolute top-0 right-0 w-8 h-8 bg-linear-to-br from-white/40 to-transparent rounded-bl-full opacity-70"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h1 class="text-xl lg:text-3xl font-bold text-primary tracking-tight">SGSE</h1>
|
||||
<p class="text-xs lg:text-base text-base-content/80 hidden sm:block font-medium leading-tight">
|
||||
<h1 class="text-xl lg:text-3xl font-bold text-primary tracking-tight">
|
||||
SGSE
|
||||
</h1>
|
||||
<p
|
||||
class="text-xs lg:text-base text-base-content/80 hidden sm:block font-medium leading-tight"
|
||||
>
|
||||
Sistema de Gerenciamento da<br class="lg:hidden" /> Secretaria de Esportes
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-none flex items-center gap-4 ml-auto">
|
||||
{#if authStore.autenticado}
|
||||
{#if currentUser.data}
|
||||
<!-- Sino de notificações no canto superior direito -->
|
||||
<div class="relative">
|
||||
<NotificationBell />
|
||||
<NotificationBell />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hidden lg:flex flex-col items-end mr-2">
|
||||
<span class="text-sm font-semibold text-primary">{authStore.usuario?.nome}</span>
|
||||
<span class="text-xs text-base-content/60">{authStore.usuario?.role.nome}</span>
|
||||
<span class="text-sm font-semibold text-primary"
|
||||
>{currentUser.data.nome}</span
|
||||
>
|
||||
<span class="text-xs text-base-content/60"
|
||||
>{currentUser.data.role?.nome}</span
|
||||
>
|
||||
</div>
|
||||
<div class="dropdown dropdown-end">
|
||||
<!-- Botão de Perfil ULTRA MODERNO -->
|
||||
@@ -253,11 +240,16 @@
|
||||
aria-label="Menu do usuário"
|
||||
>
|
||||
<!-- Efeito de brilho no hover -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
></div>
|
||||
|
||||
<!-- Anel de pulso sutil -->
|
||||
<div class="absolute inset-0 rounded-2xl" style="animation: pulse-ring-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 rounded-2xl"
|
||||
style="animation: pulse-ring-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;"
|
||||
></div>
|
||||
|
||||
<!-- Avatar/Foto do usuário ou ícone padrão -->
|
||||
{#if avatarUrlDoUsuario()}
|
||||
<img
|
||||
@@ -267,40 +259,55 @@
|
||||
/>
|
||||
{:else}
|
||||
<!-- Ícone de usuário moderno (fallback) -->
|
||||
<User
|
||||
class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300"
|
||||
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));"
|
||||
/>
|
||||
<User
|
||||
class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300"
|
||||
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
<!-- Badge de status online -->
|
||||
<div class="absolute top-1 right-1 w-3 h-3 bg-success rounded-full border-2 border-white shadow-lg z-20" style="animation: pulse-dot 2s ease-in-out infinite;"></div>
|
||||
<div
|
||||
class="absolute top-1 right-1 w-3 h-3 bg-success rounded-full border-2 border-white shadow-lg z-20"
|
||||
style="animation: pulse-dot 2s ease-in-out infinite;"
|
||||
></div>
|
||||
</button>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow-xl bg-base-100 rounded-box w-52 mt-4 border border-primary/20">
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content z-1 menu p-2 shadow-xl bg-base-100 rounded-box w-52 mt-4 border border-primary/20"
|
||||
>
|
||||
<li class="menu-title">
|
||||
<span class="text-primary font-bold">{authStore.usuario?.nome}</span>
|
||||
<span class="text-primary font-bold">{authStore.usuario?.nome}</span
|
||||
>
|
||||
</li>
|
||||
<li><a href="/perfil">Meu Perfil</a></li>
|
||||
<li><a href="/alterar-senha">Alterar Senha</a></li>
|
||||
<div class="divider my-0"></div>
|
||||
<li><button type="button" onclick={handleLogout} class="text-error">Sair</button></li>
|
||||
<li>
|
||||
<button type="button" onclick={handleLogout} class="text-error"
|
||||
>Sair</button
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-lg shadow-2xl hover:shadow-primary/30 transition-all duration-500 hover:scale-110 group relative overflow-hidden border-0 bg-gradient-to-br from-primary via-primary to-primary/80 hover:from-primary/90 hover:via-primary/80 hover:to-primary/70"
|
||||
class="btn btn-lg shadow-2xl hover:shadow-primary/30 transition-all duration-500 hover:scale-110 group relative overflow-hidden border-0 bg-linear-to-br from-primary via-primary to-primary/80 hover:from-primary/90 hover:via-primary/80 hover:to-primary/70"
|
||||
style="width: 4rem; height: 4rem; border-radius: 9999px;"
|
||||
onclick={() => openLoginModal()}
|
||||
aria-label="Login"
|
||||
>
|
||||
<!-- Efeito de brilho animado -->
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-r from-transparent via-white/30 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"
|
||||
></div>
|
||||
|
||||
<!-- Anel pulsante de fundo -->
|
||||
<div class="absolute inset-0 rounded-full bg-white/10 group-hover:animate-ping"></div>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 rounded-full bg-white/10 group-hover:animate-ping"
|
||||
></div>
|
||||
|
||||
<!-- Ícone de login premium -->
|
||||
<User
|
||||
class="h-8 w-8 relative z-10 text-white group-hover:scale-110 transition-all duration-500"
|
||||
@@ -314,48 +321,66 @@
|
||||
|
||||
<div class="drawer lg:drawer-open" style="margin-top: 96px;">
|
||||
<input id="my-drawer-3" type="checkbox" class="drawer-toggle" />
|
||||
<div class="drawer-content flex flex-col lg:ml-72" style="min-height: calc(100vh - 96px);">
|
||||
<div
|
||||
class="drawer-content flex flex-col lg:ml-72"
|
||||
style="min-height: calc(100vh - 96px);"
|
||||
>
|
||||
<!-- Page content -->
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer footer-center bg-gradient-to-r from-primary/30 via-primary/20 to-primary/30 backdrop-blur-sm text-base-content p-6 border-t-2 border-primary/20 shadow-inner flex-shrink-0">
|
||||
<div class="grid grid-flow-col gap-6 text-sm font-medium">
|
||||
<button type="button" class="link link-hover hover:text-primary transition-colors" onclick={() => openAboutModal()}>Sobre</button>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors">Contato</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors">Suporte</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors">Privacidade</a>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mt-2">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-lg bg-white p-1.5 shadow-md">
|
||||
<img src={logo} alt="Logo" class="w-full h-full object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<p class="text-xs font-bold text-primary">Governo do Estado de Pernambuco</p>
|
||||
<p class="text-xs text-base-content/70">Secretaria de Esportes</p>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer
|
||||
class="footer footer-center bg-linear-to-r from-primary/30 via-primary/20 to-primary/30 backdrop-blur-sm text-base-content p-6 border-t-2 border-primary/20 shadow-inner shrink-0"
|
||||
>
|
||||
<div class="grid grid-flow-col gap-6 text-sm font-medium">
|
||||
<button
|
||||
type="button"
|
||||
class="link link-hover hover:text-primary transition-colors"
|
||||
onclick={() => openAboutModal()}>Sobre</button
|
||||
>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors"
|
||||
>Contato</a
|
||||
>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors"
|
||||
>Suporte</a
|
||||
>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/" class="link link-hover hover:text-primary transition-colors"
|
||||
>Privacidade</a
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mt-2">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-lg bg-white p-1.5 shadow-md">
|
||||
<img src={logo} alt="Logo" class="w-full h-full object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-base-content/60 mt-2">© {new Date().getFullYear()} - Todos os direitos reservados</p>
|
||||
</footer>
|
||||
<div class="text-left">
|
||||
<p class="text-xs font-bold text-primary">
|
||||
Governo do Estado de Pernambuco
|
||||
</p>
|
||||
<p class="text-xs text-base-content/70">Secretaria de Esportes</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-base-content/60 mt-2">
|
||||
© {new Date().getFullYear()} - Todos os direitos reservados
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="drawer-side z-40 fixed" style="margin-top: 96px;">
|
||||
<label for="my-drawer-3" aria-label="close sidebar" class="drawer-overlay"
|
||||
></label>
|
||||
<div class="menu bg-gradient-to-b from-primary/25 to-primary/15 backdrop-blur-sm w-72 p-4 flex flex-col gap-2 h-[calc(100vh-96px)] overflow-y-auto border-r-2 border-primary/20 shadow-xl">
|
||||
<div
|
||||
class="menu bg-linear-to-b from-primary/25 to-primary/15 backdrop-blur-sm w-72 p-4 flex flex-col gap-2 h-[calc(100vh-96px)] overflow-y-auto border-r-2 border-primary/20 shadow-xl"
|
||||
>
|
||||
<!-- Sidebar menu items -->
|
||||
<ul class="flex flex-col gap-2">
|
||||
<li class="rounded-xl">
|
||||
<a
|
||||
href="/"
|
||||
class={getMenuClasses(currentPath === "/")}
|
||||
>
|
||||
<a href="/" class={getMenuClasses(currentPath === "/")}>
|
||||
<Home
|
||||
class="h-5 w-5 group-hover:scale-110 transition-transform"
|
||||
strokeWidth={2}
|
||||
@@ -376,14 +401,11 @@
|
||||
</li>
|
||||
{/each}
|
||||
<li class="rounded-xl mt-auto">
|
||||
<a
|
||||
<a
|
||||
href="/solicitar-acesso"
|
||||
class={getSolicitarClasses(currentPath === "/solicitar-acesso")}
|
||||
>
|
||||
<UserPlus
|
||||
class="h-5 w-5"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<UserPlus class="h-5 w-5" strokeWidth={2} />
|
||||
<span>Solicitar acesso</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -412,7 +434,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="font-bold text-3xl text-primary">Login</h3>
|
||||
<p class="text-sm text-base-content/60 mt-2">Acesse o sistema com suas credenciais</p>
|
||||
<p class="text-sm text-base-content/60 mt-2">
|
||||
Acesse o sistema com suas credenciais
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if erroLogin}
|
||||
@@ -452,8 +476,8 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control mt-6">
|
||||
<button
|
||||
type="submit"
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary w-full"
|
||||
disabled={carregandoLogin}
|
||||
>
|
||||
@@ -467,20 +491,34 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-center mt-4 space-y-2">
|
||||
<a href="/solicitar-acesso" class="link link-primary text-sm block" onclick={closeLoginModal}>
|
||||
<a
|
||||
href="/solicitar-acesso"
|
||||
class="link link-primary text-sm block"
|
||||
onclick={closeLoginModal}
|
||||
>
|
||||
Não tem acesso? Solicite aqui
|
||||
</a>
|
||||
<a href="/esqueci-senha" class="link link-secondary text-sm block" onclick={closeLoginModal}>
|
||||
<a
|
||||
href="/esqueci-senha"
|
||||
class="link link-secondary text-sm block"
|
||||
onclick={closeLoginModal}
|
||||
>
|
||||
Esqueceu sua senha?
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="divider text-xs text-base-content/40">Credenciais de teste</div>
|
||||
<div class="divider text-xs text-base-content/40">
|
||||
Credenciais de teste
|
||||
</div>
|
||||
<div class="bg-base-200 p-3 rounded-lg text-xs">
|
||||
<p class="font-semibold mb-1">Admin:</p>
|
||||
<p>Matrícula: <code class="bg-base-300 px-2 py-1 rounded">0000</code></p>
|
||||
<p>Senha: <code class="bg-base-300 px-2 py-1 rounded">Admin@123</code></p>
|
||||
<p>
|
||||
Matrícula: <code class="bg-base-300 px-2 py-1 rounded">0000</code>
|
||||
</p>
|
||||
<p>
|
||||
Senha: <code class="bg-base-300 px-2 py-1 rounded">Admin@123</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -495,7 +533,9 @@
|
||||
<!-- Modal Sobre -->
|
||||
{#if showAboutModal}
|
||||
<dialog class="modal modal-open">
|
||||
<div class="modal-box max-w-2xl relative overflow-hidden bg-gradient-to-br from-base-100 to-base-200">
|
||||
<div
|
||||
class="modal-box max-w-2xl relative overflow-hidden bg-linear-to-br from-base-100 to-base-200"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
||||
@@ -509,7 +549,11 @@
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<div class="avatar">
|
||||
<div class="w-24 rounded-xl bg-white p-3 shadow-lg">
|
||||
<img src={logo} alt="Logo SGSE" class="w-full h-full object-contain" />
|
||||
<img
|
||||
src={logo}
|
||||
alt="Logo SGSE"
|
||||
class="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -538,7 +582,9 @@
|
||||
|
||||
<!-- Desenvolvido por -->
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm font-medium text-base-content/60">Desenvolvido por</p>
|
||||
<p class="text-sm font-medium text-base-content/60">
|
||||
Desenvolvido por
|
||||
</p>
|
||||
<p class="text-lg font-bold text-primary">
|
||||
Secretaria de Esportes de Pernambuco
|
||||
</p>
|
||||
@@ -561,8 +607,8 @@
|
||||
|
||||
<!-- Botão OK -->
|
||||
<div class="pt-4">
|
||||
<button
|
||||
type="button"
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-lg w-full max-w-xs mx-auto shadow-lg hover:shadow-xl transition-all duration-300"
|
||||
onclick={closeAboutModal}
|
||||
>
|
||||
@@ -572,8 +618,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop" onclick={closeAboutModal} role="button" tabindex="0" onkeydown={(e) => e.key === 'Escape' && closeAboutModal()}>
|
||||
</div>
|
||||
<div
|
||||
class="modal-backdrop"
|
||||
onclick={closeAboutModal}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onkeydown={(e) => e.key === "Escape" && closeAboutModal()}
|
||||
></div>
|
||||
</dialog>
|
||||
{/if}
|
||||
|
||||
@@ -586,7 +637,8 @@
|
||||
<style>
|
||||
/* Animação de pulso sutil para o anel do botão de perfil */
|
||||
@keyframes pulse-ring-subtle {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
transform: scale(1);
|
||||
}
|
||||
@@ -598,7 +650,8 @@
|
||||
|
||||
/* Animação de pulso para o badge de status online */
|
||||
@keyframes pulse-dot {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
@@ -608,4 +661,3 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -50,10 +50,21 @@ export const getCurrentUser = query({
|
||||
.query("usuarios")
|
||||
.withIndex("authId", (q) => q.eq("authId", authUser._id))
|
||||
.unique();
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
return user;
|
||||
|
||||
if (!user.roleId) {
|
||||
return { ...user, role: null };
|
||||
}
|
||||
|
||||
const role = await ctx.db
|
||||
.query("roles")
|
||||
.withIndex("by_id", (q) => q.eq("_id", user.roleId))
|
||||
.unique();
|
||||
|
||||
return { ...user, role };
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user