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:
@@ -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);
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { useQuery, useConvexClient } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
import type { Doc } from "@sgse-app/backend/convex/_generated/dataModel";
|
||||
|
||||
type EmailDetalhes = Doc<"notificacoesEmail"> | null;
|
||||
|
||||
let autoRefresh = $state(true);
|
||||
let refreshInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let processando = $state(false);
|
||||
let modalDetalhesAberto = $state(false);
|
||||
let emailSelecionado = $state<EmailDetalhes>(null);
|
||||
|
||||
const client = useConvexClient();
|
||||
const estatisticas = useQuery(api.email.obterEstatisticasFilaEmails, {});
|
||||
const filaEmails = useQuery(api.email.listarFilaEmails, { limite: 50 });
|
||||
|
||||
// Criar uma chave reativa para forçar atualização das queries
|
||||
let refreshKey = $state(0);
|
||||
|
||||
// Usar refreshKey nos argumentos para forçar recarregamento quando mudar
|
||||
// O backend ignora esse parâmetro, mas força o Convex Svelte a reexecutar a query
|
||||
const estatisticas = useQuery(api.email.obterEstatisticasFilaEmails, { _refresh: refreshKey });
|
||||
const filaEmails = useQuery(api.email.listarFilaEmails, { limite: 50, _refresh: refreshKey });
|
||||
|
||||
// Função para forçar refresh das queries
|
||||
function refreshQueries() {
|
||||
refreshKey++;
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (autoRefresh) {
|
||||
refreshInterval = setInterval(() => {
|
||||
// Forçar refresh das queries invalidando o cache
|
||||
// As queries do Convex Svelte atualizam automaticamente
|
||||
refreshQueries();
|
||||
}, 5000); // Refresh a cada 5 segundos
|
||||
} else {
|
||||
if (refreshInterval) {
|
||||
@@ -26,6 +41,7 @@
|
||||
return () => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
refreshInterval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -39,6 +55,8 @@
|
||||
|
||||
if (resultado.sucesso) {
|
||||
alert(`✅ Processados: ${resultado.processados}, Falhas: ${resultado.falhas}`);
|
||||
// Forçar atualização após processar
|
||||
refreshQueries();
|
||||
} else {
|
||||
alert(`❌ Erro: ${resultado.erro || "Erro desconhecido"}`);
|
||||
}
|
||||
@@ -84,6 +102,16 @@
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
function abrirModalDetalhes(email: Doc<"notificacoesEmail">) {
|
||||
emailSelecionado = email;
|
||||
modalDetalhesAberto = true;
|
||||
}
|
||||
|
||||
function fecharModalDetalhes() {
|
||||
modalDetalhesAberto = false;
|
||||
emailSelecionado = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto p-6">
|
||||
@@ -181,6 +209,7 @@
|
||||
<th>Criado em</th>
|
||||
<th>Última tentativa</th>
|
||||
<th>Erro</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -206,18 +235,31 @@
|
||||
</td>
|
||||
<td>
|
||||
{#if email.erroDetalhes}
|
||||
<div
|
||||
class="tooltip tooltip-left"
|
||||
data-tip={email.erroDetalhes}
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
onclick={() => abrirModalDetalhes(email)}
|
||||
>
|
||||
<span class="text-error text-xs cursor-help">
|
||||
⚠️ Ver erro
|
||||
</span>
|
||||
</div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
Ver erro
|
||||
</button>
|
||||
{:else}
|
||||
<span class="text-base-content/50">-</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-ghost btn-xs"
|
||||
onclick={() => abrirModalDetalhes(email)}
|
||||
title="Ver detalhes"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
@@ -263,3 +305,159 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de Detalhes do Email -->
|
||||
{#if modalDetalhesAberto && emailSelecionado}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box max-w-4xl">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="font-bold text-2xl flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Detalhes do Email
|
||||
</h3>
|
||||
<button class="btn btn-sm btn-circle btn-ghost" onclick={fecharModalDetalhes}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Status -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-semibold">Status:</span>
|
||||
<span class={getStatusBadgeClass(emailSelecionado.status)}>
|
||||
{getStatusLabel(emailSelecionado.status)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Informações Principais -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Destinatário</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
{emailSelecionado.destinatario}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Tentativas</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
{emailSelecionado.tentativas || 0}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assunto -->
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Assunto</span>
|
||||
</label>
|
||||
<div class="input input-bordered">
|
||||
{emailSelecionado.assunto}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Datas -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Criado em</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{formatarData(emailSelecionado.criadoEm)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if emailSelecionado.ultimaTentativa}
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Última tentativa</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-base-content/50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
{formatarData(emailSelecionado.ultimaTentativa)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if emailSelecionado.enviadoEm}
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Enviado em</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-success" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<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>
|
||||
{formatarData(emailSelecionado.enviadoEm)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if emailSelecionado.agendadaPara}
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Agendado para</span>
|
||||
</label>
|
||||
<div class="input input-bordered flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-info" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{formatarData(emailSelecionado.agendadaPara)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Erro Detalhado -->
|
||||
{#if emailSelecionado.erroDetalhes}
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold text-error">Detalhes do Erro</span>
|
||||
</label>
|
||||
<div class="bg-error/10 border border-error/20 rounded-lg p-4">
|
||||
<pre class="text-sm text-error whitespace-pre-wrap break-words">{emailSelecionado.erroDetalhes}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Corpo do Email (Preview) -->
|
||||
{#if emailSelecionado.corpo}
|
||||
<div>
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold">Preview do Email</span>
|
||||
</label>
|
||||
<div class="border border-base-300 rounded-lg p-4 bg-base-200 max-h-64 overflow-y-auto">
|
||||
{@html emailSelecionado.corpo}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-primary" onclick={fecharModalDetalhes}>
|
||||
Fechar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop" onclick={fecharModalDetalhes}></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -814,7 +814,7 @@
|
||||
if (usarTemplate && templateId) {
|
||||
const template = templateSelecionado;
|
||||
if (template) {
|
||||
resultadoEmail = await client.mutation(
|
||||
const emailId = await client.action(
|
||||
api.email.enviarEmailComTemplate,
|
||||
{
|
||||
destinatario: destinatario.email,
|
||||
@@ -824,11 +824,11 @@
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula,
|
||||
},
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
enviadoPor: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
},
|
||||
);
|
||||
if (resultadoEmail?.sucesso && resultadoEmail?.emailId) {
|
||||
if (emailId) {
|
||||
if (agendadaPara) {
|
||||
const dataFormatada = format(
|
||||
new Date(agendadaPara),
|
||||
@@ -840,7 +840,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
`Email agendado para ${dataFormatada}`,
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
} else {
|
||||
adicionarLog(
|
||||
@@ -848,7 +848,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
"Email enfileirado para envio",
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -868,18 +868,19 @@
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resultadoEmail = await client.mutation(
|
||||
const emailId = await client.mutation(
|
||||
api.email.enfileirarEmail,
|
||||
{
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
enviadoPor: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
},
|
||||
);
|
||||
if (resultadoEmail?.sucesso && resultadoEmail?.emailId) {
|
||||
if (emailId) {
|
||||
resultadoEmail = { sucesso: true, emailId };
|
||||
if (agendadaPara) {
|
||||
const dataFormatada = format(
|
||||
new Date(agendadaPara),
|
||||
@@ -891,7 +892,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
`Email agendado para ${dataFormatada}`,
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
} else {
|
||||
adicionarLog(
|
||||
@@ -899,7 +900,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
"Email enfileirado para envio",
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -1064,7 +1065,7 @@
|
||||
if (usarTemplate && templateId) {
|
||||
const template = templateSelecionado;
|
||||
if (template) {
|
||||
const resultadoEmail = await client.mutation(
|
||||
const emailId = await client.action(
|
||||
api.email.enviarEmailComTemplate,
|
||||
{
|
||||
destinatario: destinatario.email,
|
||||
@@ -1074,11 +1075,11 @@
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula || "",
|
||||
},
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
enviadoPor: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
},
|
||||
);
|
||||
if (resultadoEmail?.sucesso && resultadoEmail?.emailId) {
|
||||
if (emailId) {
|
||||
if (agendadaPara) {
|
||||
const dataFormatada = format(
|
||||
new Date(agendadaPara),
|
||||
@@ -1090,7 +1091,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
`Agendado para ${dataFormatada}`,
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
} else {
|
||||
adicionarLog(
|
||||
@@ -1098,7 +1099,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
"Enfileirado para envio",
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
}
|
||||
sucessosEmail++;
|
||||
@@ -1121,18 +1122,19 @@
|
||||
falhasEmail++;
|
||||
}
|
||||
} else {
|
||||
const resultadoEmail = await client.mutation(
|
||||
const emailId = await client.mutation(
|
||||
api.email.enfileirarEmail,
|
||||
{
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
enviadoPor: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
},
|
||||
);
|
||||
if (resultadoEmail?.sucesso && resultadoEmail?.emailId) {
|
||||
if (emailId) {
|
||||
resultadoEmail = { sucesso: true, emailId };
|
||||
if (agendadaPara) {
|
||||
const dataFormatada = format(
|
||||
new Date(agendadaPara),
|
||||
@@ -1144,7 +1146,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
`Agendado para ${dataFormatada}`,
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
} else {
|
||||
adicionarLog(
|
||||
@@ -1152,7 +1154,7 @@
|
||||
destinatario.nome,
|
||||
"fila",
|
||||
"Enfileirado para envio",
|
||||
resultadoEmail.emailId,
|
||||
emailId,
|
||||
);
|
||||
}
|
||||
sucessosEmail++;
|
||||
|
||||
Reference in New Issue
Block a user