feat: update email notification handling to use scheduler for template sending, with improved error handling for fallback scenarios
This commit is contained in:
@@ -0,0 +1,267 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||||
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
|
import type { FunctionReference } from 'convex/server';
|
||||||
|
import type { Doc } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
|
const client = useConvexClient();
|
||||||
|
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
|
||||||
|
const templatesQuery = useQuery(api.templatesMensagens.listarTemplates, {});
|
||||||
|
|
||||||
|
const params = $derived($page.params);
|
||||||
|
const templateIdParam = $derived(params.id ?? '');
|
||||||
|
|
||||||
|
const templates = $derived.by(() => {
|
||||||
|
if (templatesQuery === undefined || templatesQuery === null) return [];
|
||||||
|
if ('data' in templatesQuery && templatesQuery.data !== undefined) {
|
||||||
|
return Array.isArray(templatesQuery.data) ? templatesQuery.data : [];
|
||||||
|
}
|
||||||
|
if (Array.isArray(templatesQuery)) return templatesQuery;
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const template = $derived.by(() => {
|
||||||
|
const lista = templates as Doc<'templatesMensagens'>[];
|
||||||
|
return lista.find((t) => String(t._id) === templateIdParam) ?? null;
|
||||||
|
});
|
||||||
|
|
||||||
|
let nome = $state('');
|
||||||
|
let titulo = $state('');
|
||||||
|
let corpo = $state('');
|
||||||
|
let categoria = $state<'email' | 'chat' | 'ambos'>('email');
|
||||||
|
let variaveisTexto = $state('');
|
||||||
|
let tagsTexto = $state('');
|
||||||
|
let salvando = $state(false);
|
||||||
|
let mensagem = $state<{
|
||||||
|
tipo: 'success' | 'error' | 'info';
|
||||||
|
texto: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (template) {
|
||||||
|
nome = template.nome ?? '';
|
||||||
|
titulo = template.titulo ?? '';
|
||||||
|
corpo = template.corpo ?? '';
|
||||||
|
categoria = (template.categoria as 'email' | 'chat' | 'ambos') ?? 'email';
|
||||||
|
variaveisTexto = (template.variaveis ?? []).join(', ');
|
||||||
|
tagsTexto = (template.tags ?? []).join(', ');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function mostrarMensagem(tipo: 'success' | 'error' | 'info', texto: string) {
|
||||||
|
mensagem = { tipo, texto };
|
||||||
|
setTimeout(() => {
|
||||||
|
mensagem = null;
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLista(input: string): string[] {
|
||||||
|
return input
|
||||||
|
.split(/[;,\n]/)
|
||||||
|
.map((v) => v.trim())
|
||||||
|
.filter((v) => v.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function salvar() {
|
||||||
|
if (!template) {
|
||||||
|
mostrarMensagem('error', 'Template não encontrado.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!currentUser.data) {
|
||||||
|
mostrarMensagem('error', 'Usuário não autenticado.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!nome.trim() || !titulo.trim() || !corpo.trim()) {
|
||||||
|
mostrarMensagem('error', 'Preencha todos os campos obrigatórios.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variaveis = parseLista(variaveisTexto);
|
||||||
|
const tags = parseLista(tagsTexto);
|
||||||
|
|
||||||
|
try {
|
||||||
|
salvando = true;
|
||||||
|
const resultado = await client.mutation(api.templatesMensagens.editarTemplate, {
|
||||||
|
templateId: template._id,
|
||||||
|
nome: nome.trim(),
|
||||||
|
titulo: titulo.trim(),
|
||||||
|
corpo: corpo.trim(),
|
||||||
|
variaveis,
|
||||||
|
categoria,
|
||||||
|
tags,
|
||||||
|
editadoPorId: currentUser.data._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resultado.sucesso) {
|
||||||
|
mostrarMensagem('success', 'Template atualizado com sucesso!');
|
||||||
|
await goto(resolve('/ti/notificacoes/templates'));
|
||||||
|
} else {
|
||||||
|
mostrarMensagem('error', resultado.erro || 'Erro ao atualizar template.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const erro = error instanceof Error ? error.message : 'Erro desconhecido';
|
||||||
|
mostrarMensagem('error', `Erro ao atualizar template: ${erro}`);
|
||||||
|
} finally {
|
||||||
|
salvando = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container mx-auto max-w-4xl px-4 py-6">
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div class="bg-info/10 rounded-xl p-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="text-info h-8 w-8"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-base-content text-3xl font-bold">Editar Template</h1>
|
||||||
|
<p class="text-base-content/60 mt-1">
|
||||||
|
Atualize as informações do template selecionado. Templates de sistema não podem ser
|
||||||
|
editados.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-ghost"> Voltar </a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if !template}
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<span>Template não encontrado.</span>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
{#if mensagem}
|
||||||
|
<div
|
||||||
|
class="alert mb-6 shadow-lg"
|
||||||
|
class:alert-success={mensagem.tipo === 'success'}
|
||||||
|
class:alert-error={mensagem.tipo === 'error'}
|
||||||
|
class:alert-info={mensagem.tipo === 'info'}
|
||||||
|
>
|
||||||
|
<span class="font-medium">{mensagem.texto}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-circle btn-ghost"
|
||||||
|
onclick={() => {
|
||||||
|
mensagem = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body space-y-4">
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="nome">
|
||||||
|
<span class="label-text font-medium">Nome *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="nome"
|
||||||
|
type="text"
|
||||||
|
bind:value={nome}
|
||||||
|
class="input input-bordered"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="titulo">
|
||||||
|
<span class="label-text font-medium">Título *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="titulo"
|
||||||
|
type="text"
|
||||||
|
bind:value={titulo}
|
||||||
|
class="input input-bordered"
|
||||||
|
maxlength="200"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="categoria">
|
||||||
|
<span class="label-text font-medium">Categoria</span>
|
||||||
|
</label>
|
||||||
|
<select id="categoria" bind:value={categoria} class="select select-bordered max-w-xs">
|
||||||
|
<option value="email">Email</option>
|
||||||
|
<option value="chat">Chat</option>
|
||||||
|
<option value="ambos">Ambos</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="corpo">
|
||||||
|
<span class="label-text font-medium">Corpo da Mensagem *</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="corpo"
|
||||||
|
bind:value={corpo}
|
||||||
|
class="textarea textarea-bordered h-40"
|
||||||
|
placeholder="Digite o conteúdo da mensagem. Você pode usar {{variavel}} para valores dinâmicos."
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="variaveis">
|
||||||
|
<span class="label-text font-medium">Variáveis (opcional)</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="variaveis"
|
||||||
|
type="text"
|
||||||
|
bind:value={variaveisTexto}
|
||||||
|
class="input input-bordered"
|
||||||
|
placeholder="nome, data, valor"
|
||||||
|
/>
|
||||||
|
<label class="label" for="variaveis">
|
||||||
|
<span class="label-text-alt">
|
||||||
|
Liste as variáveis que podem ser usadas no corpo (separadas por vírgula ou ponto e
|
||||||
|
vírgula).
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="tags">
|
||||||
|
<span class="label-text font-medium">Tags (opcional)</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="tags"
|
||||||
|
type="text"
|
||||||
|
bind:value={tagsTexto}
|
||||||
|
class="input input-bordered"
|
||||||
|
placeholder="avisos, chamados, rh"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex justify-end gap-3">
|
||||||
|
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-ghost"> Cancelar </a>
|
||||||
|
<button class="btn btn-primary" onclick={salvar} disabled={salvando}>
|
||||||
|
{#if salvando}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
Salvando...
|
||||||
|
{:else}
|
||||||
|
Salvar Alterações
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||||
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
|
import type { FunctionReference } from 'convex/server';
|
||||||
|
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
|
const client = useConvexClient();
|
||||||
|
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
|
||||||
|
|
||||||
|
let codigo = $state('');
|
||||||
|
let nome = $state('');
|
||||||
|
let titulo = $state('');
|
||||||
|
let corpo = $state('');
|
||||||
|
let categoria = $state<'email' | 'chat' | 'ambos'>('email');
|
||||||
|
let variaveisTexto = $state('');
|
||||||
|
let tagsTexto = $state('');
|
||||||
|
let criando = $state(false);
|
||||||
|
let mensagem = $state<{
|
||||||
|
tipo: 'success' | 'error' | 'info';
|
||||||
|
texto: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
function mostrarMensagem(tipo: 'success' | 'error' | 'info', texto: string) {
|
||||||
|
mensagem = { tipo, texto };
|
||||||
|
setTimeout(() => {
|
||||||
|
mensagem = null;
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLista(input: string): string[] {
|
||||||
|
return input
|
||||||
|
.split(/[;,\n]/)
|
||||||
|
.map((v) => v.trim())
|
||||||
|
.filter((v) => v.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function salvar() {
|
||||||
|
if (!currentUser.data) {
|
||||||
|
mostrarMensagem('error', 'Usuário não autenticado.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!codigo.trim() || !nome.trim() || !titulo.trim() || !corpo.trim()) {
|
||||||
|
mostrarMensagem('error', 'Preencha todos os campos obrigatórios.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const codigoNormalizado = codigo.trim().toUpperCase().replace(/\s+/g, '_');
|
||||||
|
const variaveis = parseLista(variaveisTexto);
|
||||||
|
const tags = parseLista(tagsTexto);
|
||||||
|
|
||||||
|
try {
|
||||||
|
criando = true;
|
||||||
|
const resultado = await client.mutation(api.templatesMensagens.criarTemplate, {
|
||||||
|
codigo: codigoNormalizado,
|
||||||
|
nome: nome.trim(),
|
||||||
|
titulo: titulo.trim(),
|
||||||
|
corpo: corpo.trim(),
|
||||||
|
variaveis,
|
||||||
|
categoria,
|
||||||
|
tags,
|
||||||
|
criadoPorId: currentUser.data._id as Id<'usuarios'>
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resultado.sucesso) {
|
||||||
|
mostrarMensagem('success', 'Template criado com sucesso!');
|
||||||
|
await goto(resolve('/ti/notificacoes/templates'));
|
||||||
|
} else {
|
||||||
|
mostrarMensagem('error', resultado.erro || 'Erro ao criar template.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const erro = error instanceof Error ? error.message : 'Erro desconhecido';
|
||||||
|
mostrarMensagem('error', `Erro ao criar template: ${erro}`);
|
||||||
|
} finally {
|
||||||
|
criando = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container mx-auto max-w-4xl px-4 py-6">
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div class="bg-info/10 rounded-xl p-3">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="text-info h-8 w-8"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-base-content text-3xl font-bold">Novo Template</h1>
|
||||||
|
<p class="text-base-content/60 mt-1">
|
||||||
|
Crie um template de email ou mensagem para reutilizar no sistema.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-ghost"> Voltar </a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if mensagem}
|
||||||
|
<div
|
||||||
|
class="alert mb-6 shadow-lg"
|
||||||
|
class:alert-success={mensagem.tipo === 'success'}
|
||||||
|
class:alert-error={mensagem.tipo === 'error'}
|
||||||
|
class:alert-info={mensagem.tipo === 'info'}
|
||||||
|
>
|
||||||
|
<span class="font-medium">{mensagem.texto}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-circle btn-ghost"
|
||||||
|
onclick={() => {
|
||||||
|
mensagem = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body space-y-4">
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="codigo">
|
||||||
|
<span class="label-text font-medium">Código *</span>
|
||||||
|
<span class="label-text-alt">Ex: AVISO_IMPORTANTE</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="codigo"
|
||||||
|
type="text"
|
||||||
|
bind:value={codigo}
|
||||||
|
class="input input-bordered"
|
||||||
|
maxlength="50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="nome">
|
||||||
|
<span class="label-text font-medium">Nome *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="nome"
|
||||||
|
type="text"
|
||||||
|
bind:value={nome}
|
||||||
|
class="input input-bordered"
|
||||||
|
maxlength="100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="titulo">
|
||||||
|
<span class="label-text font-medium">Título *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="titulo"
|
||||||
|
type="text"
|
||||||
|
bind:value={titulo}
|
||||||
|
class="input input-bordered"
|
||||||
|
maxlength="200"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="categoria">
|
||||||
|
<span class="label-text font-medium">Categoria</span>
|
||||||
|
</label>
|
||||||
|
<select id="categoria" bind:value={categoria} class="select select-bordered max-w-xs">
|
||||||
|
<option value="email">Email</option>
|
||||||
|
<option value="chat">Chat</option>
|
||||||
|
<option value="ambos">Ambos</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="corpo">
|
||||||
|
<span class="label-text font-medium">Corpo da Mensagem *</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="corpo"
|
||||||
|
bind:value={corpo}
|
||||||
|
class="textarea textarea-bordered h-40"
|
||||||
|
placeholder="Digite o conteúdo da mensagem. Você pode usar {{variavel}} para valores dinâmicos."
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="variaveis">
|
||||||
|
<span class="label-text font-medium">Variáveis (opcional)</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="variaveis"
|
||||||
|
type="text"
|
||||||
|
bind:value={variaveisTexto}
|
||||||
|
class="input input-bordered"
|
||||||
|
placeholder="nome, data, valor"
|
||||||
|
/>
|
||||||
|
<label class="label" for="variaveis">
|
||||||
|
<span class="label-text-alt">
|
||||||
|
Liste as variáveis que podem ser usadas no corpo (separadas por vírgula ou ponto e
|
||||||
|
vírgula).
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="tags">
|
||||||
|
<span class="label-text font-medium">Tags (opcional)</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="tags"
|
||||||
|
type="text"
|
||||||
|
bind:value={tagsTexto}
|
||||||
|
class="input input-bordered"
|
||||||
|
placeholder="avisos, chamados, rh"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex justify-end gap-3">
|
||||||
|
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-ghost"> Cancelar </a>
|
||||||
|
<button class="btn btn-primary" onclick={salvar} disabled={criando}>
|
||||||
|
{#if criando}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
Salvando...
|
||||||
|
{:else}
|
||||||
|
Salvar Template
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -364,9 +364,9 @@ export const criarSolicitacao = mutation({
|
|||||||
urlSistema = `http://${urlSistema}`;
|
urlSistema = `http://${urlSistema}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enviar email ao gestor usando template
|
// Enviar email ao gestor usando template (agendado via scheduler)
|
||||||
try {
|
try {
|
||||||
await ctx.runAction(api.email.enviarEmailComTemplate, {
|
await ctx.scheduler.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
destinatario: gestorUsuario.email,
|
destinatario: gestorUsuario.email,
|
||||||
destinatarioId: gestorId,
|
destinatarioId: gestorId,
|
||||||
templateCodigo: "ausencia_solicitada",
|
templateCodigo: "ausencia_solicitada",
|
||||||
@@ -381,8 +381,11 @@ export const criarSolicitacao = mutation({
|
|||||||
enviadoPor: funcionarioUsuario._id,
|
enviadoPor: funcionarioUsuario._id,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback para envio direto se template não existir
|
// Fallback para envio direto se houver erro ao agendar ou processar o template
|
||||||
console.warn("Template ausencia_solicitada não encontrado, usando envio direto:", error);
|
console.warn(
|
||||||
|
"Erro ao agendar envio de email com template ausencia_solicitada, usando envio direto:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||||
destinatario: gestorUsuario.email,
|
destinatario: gestorUsuario.email,
|
||||||
destinatarioId: gestorId,
|
destinatarioId: gestorId,
|
||||||
@@ -506,9 +509,9 @@ export const aprovar = mutation({
|
|||||||
urlSistema = `http://${urlSistema}`;
|
urlSistema = `http://${urlSistema}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enviar email ao funcionário usando template
|
// Enviar email ao funcionário usando template (agendado via scheduler)
|
||||||
try {
|
try {
|
||||||
await ctx.runAction(api.email.enviarEmailComTemplate, {
|
await ctx.scheduler.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
destinatario: funcionarioUsuario.email,
|
destinatario: funcionarioUsuario.email,
|
||||||
destinatarioId: funcionarioUsuario._id,
|
destinatarioId: funcionarioUsuario._id,
|
||||||
templateCodigo: "ausencia_aprovada",
|
templateCodigo: "ausencia_aprovada",
|
||||||
@@ -523,8 +526,11 @@ export const aprovar = mutation({
|
|||||||
enviadoPor: args.gestorId,
|
enviadoPor: args.gestorId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback para envio direto se template não existir
|
// Fallback para envio direto se houver erro ao agendar ou processar o template
|
||||||
console.warn("Template ausencia_aprovada não encontrado, usando envio direto:", error);
|
console.warn(
|
||||||
|
"Erro ao agendar envio de email com template ausencia_aprovada, usando envio direto:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||||
destinatario: funcionarioUsuario.email,
|
destinatario: funcionarioUsuario.email,
|
||||||
destinatarioId: funcionarioUsuario._id,
|
destinatarioId: funcionarioUsuario._id,
|
||||||
@@ -649,9 +655,9 @@ export const reprovar = mutation({
|
|||||||
urlSistema = `http://${urlSistema}`;
|
urlSistema = `http://${urlSistema}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enviar email ao funcionário usando template
|
// Enviar email ao funcionário usando template (agendado via scheduler)
|
||||||
try {
|
try {
|
||||||
await ctx.runAction(api.email.enviarEmailComTemplate, {
|
await ctx.scheduler.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
destinatario: funcionarioUsuario.email,
|
destinatario: funcionarioUsuario.email,
|
||||||
destinatarioId: funcionarioUsuario._id,
|
destinatarioId: funcionarioUsuario._id,
|
||||||
templateCodigo: "ausencia_reprovada",
|
templateCodigo: "ausencia_reprovada",
|
||||||
@@ -667,8 +673,11 @@ export const reprovar = mutation({
|
|||||||
enviadoPor: args.gestorId,
|
enviadoPor: args.gestorId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback para envio direto se template não existir
|
// Fallback para envio direto se houver erro ao agendar ou processar o template
|
||||||
console.warn("Template ausencia_reprovada não encontrado, usando envio direto:", error);
|
console.warn(
|
||||||
|
"Erro ao agendar envio de email com template ausencia_reprovada, usando envio direto:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||||
destinatario: funcionarioUsuario.email,
|
destinatario: funcionarioUsuario.email,
|
||||||
destinatarioId: funcionarioUsuario._id,
|
destinatarioId: funcionarioUsuario._id,
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ async function registrarNotificacoes(
|
|||||||
if (ticket.solicitanteEmail) {
|
if (ticket.solicitanteEmail) {
|
||||||
// Tentar usar template, senão usar envio direto
|
// Tentar usar template, senão usar envio direto
|
||||||
try {
|
try {
|
||||||
await ctx.runAction(api.email.enviarEmailComTemplate, {
|
await ctx.scheduler.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
destinatario: ticket.solicitanteEmail,
|
destinatario: ticket.solicitanteEmail,
|
||||||
destinatarioId: ticket.solicitanteId,
|
destinatarioId: ticket.solicitanteId,
|
||||||
templateCodigo: "chamado_atualizado",
|
templateCodigo: "chamado_atualizado",
|
||||||
@@ -145,6 +145,10 @@ async function registrarNotificacoes(
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback para envio direto
|
// Fallback para envio direto
|
||||||
|
console.warn(
|
||||||
|
"Erro ao agendar envio de email com template chamado_atualizado para solicitante, usando envio direto:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||||
destinatario: ticket.solicitanteEmail,
|
destinatario: ticket.solicitanteEmail,
|
||||||
destinatarioId: ticket.solicitanteId,
|
destinatarioId: ticket.solicitanteId,
|
||||||
@@ -172,7 +176,7 @@ async function registrarNotificacoes(
|
|||||||
if (responsavel?.email) {
|
if (responsavel?.email) {
|
||||||
// Tentar usar template, senão usar envio direto
|
// Tentar usar template, senão usar envio direto
|
||||||
try {
|
try {
|
||||||
await ctx.runAction(api.email.enviarEmailComTemplate, {
|
await ctx.scheduler.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
destinatario: responsavel.email,
|
destinatario: responsavel.email,
|
||||||
destinatarioId: ticket.responsavelId,
|
destinatarioId: ticket.responsavelId,
|
||||||
templateCodigo: "chamado_atualizado",
|
templateCodigo: "chamado_atualizado",
|
||||||
@@ -186,6 +190,10 @@ async function registrarNotificacoes(
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fallback para envio direto
|
// Fallback para envio direto
|
||||||
|
console.warn(
|
||||||
|
"Erro ao agendar envio de email com template chamado_atualizado para responsável, usando envio direto:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
await ctx.runMutation(api.email.enfileirarEmail, {
|
await ctx.runMutation(api.email.enfileirarEmail, {
|
||||||
destinatario: responsavel.email,
|
destinatario: responsavel.email,
|
||||||
destinatarioId: ticket.responsavelId,
|
destinatarioId: ticket.responsavelId,
|
||||||
|
|||||||
Reference in New Issue
Block a user