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:
2025-11-05 06:14:52 -03:00
parent f6671e0f16
commit aa3e3470cd
20 changed files with 2515 additions and 1665 deletions

View File

@@ -27,6 +27,7 @@
// Estados locais para atualização imediata
let fotoPerfilLocal = $state<string | null>(null);
let avatarLocal = $state<string | null>(null);
let perfilCarregado = $state(false);
// Estados para Minhas Férias
let mostrarWizard = $state(false);
@@ -43,13 +44,32 @@
// Galeria de avatares (30 avatares profissionais 3D realistas)
const avatarGallery = generateAvatarGallery(30);
// Sincronizar com authStore
// Carregar perfil ao montar a página para garantir dados atualizados (apenas uma vez)
$effect(() => {
if (authStore.usuario?.fotoPerfilUrl !== undefined) {
fotoPerfilLocal = authStore.usuario.fotoPerfilUrl;
if (authStore.autenticado && authStore.usuario && !perfilCarregado) {
perfilCarregado = true;
// Atualizar authStore com dados mais recentes do backend
authStore.refresh().catch((error) => {
console.error("Erro ao carregar perfil:", error);
perfilCarregado = false; // Permite tentar novamente em caso de erro
});
}
if (authStore.usuario?.avatar !== undefined) {
avatarLocal = authStore.usuario.avatar;
});
// Sincronizar com authStore - atualiza automaticamente quando o authStore muda
// Isso garante que a foto/avatar seja carregada imediatamente ao abrir a página
$effect(() => {
const usuario = authStore.usuario;
if (usuario) {
// Atualizar foto de perfil (pode ser null ou string)
fotoPerfilLocal = usuario.fotoPerfilUrl ?? null;
// Atualizar avatar (pode ser undefined ou string)
avatarLocal = usuario.avatar ?? null;
} else {
// Se não há usuário, limpar estados locais
fotoPerfilLocal = null;
avatarLocal = null;
perfilCarregado = false; // Reset para permitir recarregar quando houver usuário novamente
}
});
@@ -231,13 +251,21 @@
erroUpload = "";
try {
// 1. Gerar URL de upload (NOME CORRETO DA FUNÇÃO!)
// 1. Criar preview local IMEDIATAMENTE para feedback visual
const reader = new FileReader();
reader.onload = (e) => {
fotoPerfilLocal = e.target?.result as string;
avatarLocal = null;
};
reader.readAsDataURL(file);
// 2. Gerar URL de upload
const uploadUrl = await client.mutation(
api.usuarios.uploadFotoPerfil,
{}
);
// 2. Upload do arquivo
// 3. Upload do arquivo
const response = await fetch(uploadUrl, {
method: "POST",
headers: { "Content-Type": file.type },
@@ -250,21 +278,28 @@
const { storageId } = await response.json();
// 3. Atualizar perfil com o novo storageId
// 4. Atualizar perfil com o novo storageId
await client.mutation(api.usuarios.atualizarPerfil, {
fotoPerfil: storageId,
avatar: undefined, // Remove avatar se colocar foto
});
// 4. Atualizar authStore para obter a URL da foto
// 5. Aguardar um pouco para garantir que o backend processou
await new Promise(resolve => setTimeout(resolve, 300));
// 6. Atualizar authStore para obter a URL da foto atualizada
await authStore.refresh();
// 5. Atualizar localmente IMEDIATAMENTE com a URL do authStore
// 7. Atualizar localmente com a URL do authStore (substitui o preview temporário)
if (authStore.usuario?.fotoPerfilUrl) {
fotoPerfilLocal = authStore.usuario.fotoPerfilUrl;
avatarLocal = null;
}
// 8. Limpar o input para permitir novo upload
input.value = "";
// 9. Fechar modal após sucesso
mostrarModalFoto = false;
// Toast de sucesso
@@ -275,13 +310,16 @@
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Foto de perfil atualizada!</span>
<span>Foto de perfil atualizada com sucesso!</span>
</div>
`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
} catch (e: any) {
erroUpload = e.message || "Erro ao fazer upload da foto";
// Reverter mudança local se houver erro
fotoPerfilLocal = authStore.usuario?.fotoPerfilUrl || null;
avatarLocal = authStore.usuario?.avatar || null;
} finally {
uploadandoFoto = false;
}
@@ -292,7 +330,7 @@
erroUpload = "";
try {
// 1. Atualizar localmente IMEDIATAMENTE (antes mesmo da API)
// 1. Atualizar localmente IMEDIATAMENTE para feedback visual instantâneo
avatarLocal = avatarUrl;
fotoPerfilLocal = null;
@@ -302,12 +340,22 @@
fotoPerfil: undefined, // Remove foto se colocar avatar
});
// 3. Atualizar authStore em background
authStore.refresh();
// 3. Aguardar um pouco para garantir que o backend processou
await new Promise(resolve => setTimeout(resolve, 300));
// 4. Atualizar authStore e aguardar conclusão
await authStore.refresh();
// 5. Garantir que os estados locais estão sincronizados com o authStore
if (authStore.usuario?.avatar) {
avatarLocal = authStore.usuario.avatar;
fotoPerfilLocal = null;
}
// 6. Fechar modal após sucesso
mostrarModalFoto = false;
// Toast de sucesso mais discreto
// Toast de sucesso
const toast = document.createElement("div");
toast.className = "toast toast-top toast-end";
toast.innerHTML = `
@@ -315,7 +363,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Avatar atualizado!</span>
<span>Avatar atualizado com sucesso!</span>
</div>
`;
document.body.appendChild(toast);