feat: enhance push notification management and error handling
- Implemented error handling for unhandled promise rejections related to message channels, improving stability during push notification operations. - Updated the PushNotificationManager component to manage push subscription registration with timeouts, preventing application hangs. - Enhanced the sidebar and chat components to display user avatars, improving user experience and visual consistency. - Refactored email processing logic to support scheduled email sending, integrating new backend functionalities for better email management. - Improved overall error handling and logging across components to reduce console spam and enhance debugging capabilities.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
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";
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
|
||||
@@ -19,6 +20,22 @@
|
||||
// Caminho atual da página
|
||||
const currentPath = $derived(page.url.pathname);
|
||||
|
||||
// Função para obter a URL do avatar/foto do usuário
|
||||
const avatarUrlDoUsuario = $derived(() => {
|
||||
const usuario = authStore.usuario;
|
||||
if (!usuario) return null;
|
||||
|
||||
// Prioridade: fotoPerfilUrl > avatar > fallback com nome
|
||||
if (usuario.fotoPerfilUrl) {
|
||||
return usuario.fotoPerfilUrl;
|
||||
}
|
||||
if (usuario.avatar) {
|
||||
return getAvatarUrl(usuario.avatar);
|
||||
}
|
||||
// Fallback: gerar avatar baseado no nome
|
||||
return getAvatarUrl(usuario.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";
|
||||
@@ -209,12 +226,14 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-none flex items-center gap-4">
|
||||
<div class="flex-none flex items-center gap-4 ml-auto">
|
||||
{#if authStore.autenticado}
|
||||
<!-- Sino de notificações -->
|
||||
<NotificationBell />
|
||||
<!-- Sino de notificações no canto superior direito -->
|
||||
<div class="relative">
|
||||
<NotificationBell />
|
||||
</div>
|
||||
|
||||
<div class="hidden lg:flex flex-col items-end">
|
||||
<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>
|
||||
</div>
|
||||
@@ -233,19 +252,28 @@
|
||||
<!-- 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>
|
||||
|
||||
<!-- Ícone de usuário moderno -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
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));"
|
||||
>
|
||||
<path fill-rule="evenodd" d="M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<!-- Avatar/Foto do usuário ou ícone padrão -->
|
||||
{#if avatarUrlDoUsuario()}
|
||||
<img
|
||||
src={avatarUrlDoUsuario()}
|
||||
alt={authStore.usuario?.nome || "Usuário"}
|
||||
class="w-full h-full object-cover relative z-10"
|
||||
/>
|
||||
{:else}
|
||||
<!-- Ícone de usuário moderno (fallback) -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
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));"
|
||||
>
|
||||
<path fill-rule="evenodd" d="M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{/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" 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">
|
||||
@@ -295,36 +323,36 @@
|
||||
|
||||
<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="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?.()}
|
||||
|
||||
<!-- 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 mt-8">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-base-content/60 mt-2">© {new Date().getFullYear()} - Todos os direitos reservados</p>
|
||||
</footer>
|
||||
</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>
|
||||
</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"
|
||||
|
||||
Reference in New Issue
Block a user