Merge pull request #33 from killer-cf/refinament-1

Refinament 1
This commit is contained in:
Kilder Costa
2025-11-20 14:17:11 -03:00
committed by GitHub
12 changed files with 82 additions and 232 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = tab
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true

View File

@@ -1,186 +0,0 @@
# Relatório de Testes - Sistema de Central de Chamados
**Data:** 16 de novembro de 2025
**Testador:** Sistema Automatizado
**Página Testada:** `/ti/central-chamados`
## Resumo Executivo
Foram realizados testes completos na página de Central de Chamados do sistema SGSE. A maioria das funcionalidades está funcionando corretamente, mas foram identificados alguns problemas que precisam ser corrigidos.
## Testes Realizados
### ✅ Testes Bem-Sucedidos
1. **Login no Sistema**
- Status: ✅ PASSOU
- Usuário logado: Deyvison (dfw@poli.br)
2. **Visualização de SLAs Configurados**
- Status: ✅ PASSOU
- Tabela de SLAs exibe 7 SLAs ativos corretamente
- Resumo mostra: 4 Baixa, 2 Média, 1 Alta/Crítica
- Detalhes completos (tempos, prioridades) são exibidos corretamente
3. **Cards de Prioridade**
- Status: ✅ PASSOU
- Cards mostram corretamente "Configurado" ou "Não configurado"
- Botão "Configurar" funciona corretamente
- Detalhes dos SLAs configurados são exibidos nos cards
4. **Criação de SLA**
- Status: ✅ PASSOU
- SLA criado com sucesso para prioridade "Alta"
- Formulário preenche corretamente quando clica em "Configurar"
- Tabela atualiza automaticamente após criação
- Card de prioridade atualiza para "Configurado"
5. **Edição de SLA**
- Status: ✅ PASSOU
- Botão "Editar" abre formulário com dados corretos
- Atualização funciona corretamente
6. **Lista de Chamados**
- Status: ✅ PASSOU
- 4 chamados sendo exibidos corretamente
- Filtros funcionando (status, responsável, setor)
- Detalhes do chamado são exibidos ao selecionar
7. **Atribuição de Responsável**
- Status: ✅ PASSOU
- Dropdown mostra 2 usuários TI: Deyvison e Suporte_TI
- Formulário está funcional
8. **Prorrogação de Prazo**
- Status: ✅ PASSOU
- Dropdown de tickets carrega corretamente (4 tickets)
- Formulário permite selecionar tipo de prazo e horas
- Botão habilita quando todos os campos estão preenchidos
### ⚠️ Problemas Identificados
#### 1. Templates de Email - Listagem Após Criação
- **Status:** ⚠️ PROBLEMA
- **Descrição:** Templates são criados com sucesso (mensagem "Templates padrão criados com sucesso" aparece), mas não são listados na interface após criação
- **Ação Realizada:** Botão "Criar templates padrão" foi clicado e retornou sucesso
- **Comportamento Esperado:** Templates deveriam aparecer em uma lista após criação
- **Comportamento Atual:** Seção continua mostrando "Nenhum template encontrado"
- **Severidade:** MÉDIA
- **Impacto:** Usuários não conseguem visualizar/editar templates de email após criação
- **Possível Causa:** Query de templates pode não estar sendo atualizada após criação, ou filtro pode estar excluindo templates de chamados
#### 2. Warning no Console - Token de Autenticação
- **Status:** ⚠️ AVISO (Não crítico)
- **Descrição:** `⚠️ [useConvexWithAuth] Token não disponível` aparece no console durante carregamento inicial
- **Severidade:** BAIXA
- **Impacto:** Não afeta funcionalidade (autenticação funciona corretamente após carregamento)
- **Observação:** Parece ser um problema de timing durante inicialização da página
#### 3. Warning no Console - Formato de Query
- **Status:** ⚠️ AVISO (Não crítico)
- **Descrição:** `🔍 [usuariosTI] Formato inesperado: object {data: undefined, isLoading: undefined, error: undefined, isStale: undefined}` aparece no console
- **Severidade:** BAIXA
- **Impacto:** Não afeta funcionalidade (usuários são carregados corretamente - 2 usuários TI encontrados)
- **Observação:** Indica possível inconsistência no formato de retorno da query durante carregamento inicial
## Detalhes dos Testes
### Teste de Criação de SLA
- **Prioridade Testada:** Alta
- **Valores Inseridos:**
- Nome: "SLA - Alta - Teste"
- Tempo de Resposta: 2h
- Tempo de Conclusão: 8h
- Auto-encerramento: 24h
- Alerta: 2h antes
- **Resultado:** ✅ SLA criado e exibido na tabela e no card
### Teste de Edição de SLA
- **SLA Editado:** Prioridade Baixa
- **Alterações:**
- Nome: "SLA Baixa - Editado em Teste"
- Tempo de Resposta: 6h
- **Resultado:** ✅ Atualização bem-sucedida
### Teste de Prorrogação
- **Ticket Selecionado:** SGSE-202511-3750
- **Prazo:** Conclusão
- **Horas Adicionais:** 24h
- **Motivo:** "Teste de prorrogação de prazo - necessário mais tempo para análise"
- **Resultado:** ✅ Formulário preenchido corretamente, botão habilitado
## Lista de Erros Encontrados
### Erros Críticos
- **Nenhum erro crítico encontrado**
### Erros de Funcionalidade
1. **Templates de Email não aparecem após criação**
- Localização: Seção "Templates de Email - Chamados"
- Ação necessária: Verificar query de templates e atualização reativa após criação
### Avisos (Warnings)
1. **Token de autenticação não disponível durante carregamento inicial**
- Localização: Console do navegador
- Ação necessária: Melhorar timing de inicialização de autenticação
2. **Formato inesperado de query durante carregamento**
- Localização: Console do navegador (usuariosTI)
- Ação necessária: Verificar formato de retorno de useQuery do convex-svelte
## Recomendações
### Prioridade ALTA
1. **Corrigir listagem de templates de email após criação**
- Verificar se a query `templatesChamados` está sendo atualizada após criação
- Verificar se o filtro de templates está correto (deve incluir templates de chamados)
- Adicionar refresh automático após criação de templates
### Prioridade MÉDIA
2. **Investigar e corrigir warnings no console**
- Melhorar timing de autenticação para evitar warning inicial
- Padronizar formato de retorno de queries do convex-svelte
### Prioridade BAIXA
3. **Melhorar logs de debug**
- Reduzir verbosidade de logs informativos
- Manter apenas logs de erro e warnings importantes
## Conclusão
O sistema está **funcionalmente operacional**, com a maioria das funcionalidades testadas funcionando corretamente:
**Funcionalidades Testadas e Funcionando:**
- Login e autenticação
- Visualização de SLAs (tabela e cards)
- Criação de SLAs
- Edição de SLAs
- Lista de chamados
- Atribuição de responsável
- Prorrogação de prazo (formulário funcional)
- Criação de templates (backend funciona, frontend não atualiza)
⚠️ **Problemas Identificados:**
- Templates não aparecem na lista após criação (problema de atualização reativa)
- Warnings no console (não afetam funcionalidade)
**Status Geral:****OPERACIONAL COM PEQUENOS AJUSTES NECESSÁRIOS**
**Próximos Passos:**
1. Corrigir atualização reativa de templates após criação
2. Investigar e resolver warnings do console (opcional, não crítico)

View File

@@ -1,12 +1,11 @@
<script lang="ts"> <script lang="ts">
import { useQuery, useConvexClient } from 'convex-svelte'; import { useQuery, useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import type { FunctionReference } from 'convex/server';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale'; import { ptBR } from 'date-fns/locale';
import type { Id, Doc } from '@sgse-app/backend/convex/_generated/dataModel'; import type { Id, Doc } from '@sgse-app/backend/convex/_generated/dataModel';
// Tipos para agendamentos
type TipoAgendamento = 'email' | 'chat';
type StatusAgendamento = 'agendado' | 'enviado' | 'cancelado'; type StatusAgendamento = 'agendado' | 'enviado' | 'cancelado';
interface AgendamentoEmail { interface AgendamentoEmail {
@@ -43,7 +42,7 @@
| { tipo: 'chat'; dados: AgendamentoChat }; | { tipo: 'chat'; dados: AgendamentoChat };
const client = useConvexClient(); const client = useConvexClient();
const currentUser = useQuery(api.auth.getCurrentUser, {}); const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
// Queries // Queries
const templatesQuery = useQuery(api.templatesMensagens.listarTemplates, {}); const templatesQuery = useQuery(api.templatesMensagens.listarTemplates, {});
@@ -57,10 +56,9 @@
Array.from(emailIdsRastreados).map((id) => id as Id<'notificacoesEmail'>) Array.from(emailIdsRastreados).map((id) => id as Id<'notificacoesEmail'>)
); );
// Usar função para evitar execução quando array está vazio // Usar função para evitar execução quando array está vazio
const emailsStatusQuery = $derived.by(() => { const emailsStatusQuery = useQuery(api.email.buscarEmailsPorIds, () =>
if (emailIdsArray.length === 0) return null; emailIdsArray.length === 0 ? 'skip' : { emailIds: emailIdsArray }
return useQuery(api.email.buscarEmailsPorIds, { emailIds: emailIdsArray }); );
});
// Queries para agendamentos // Queries para agendamentos
const agendamentosEmailQuery = useQuery(api.email.listarAgendamentosEmail, {}); const agendamentosEmailQuery = useQuery(api.email.listarAgendamentosEmail, {});
@@ -101,8 +99,8 @@
const carregandoTemplates = $derived(templatesQuery === undefined || templatesQuery === null); const carregandoTemplates = $derived(templatesQuery === undefined || templatesQuery === null);
const carregandoUsuarios = $derived(usuariosQuery === undefined || usuariosQuery === null); const carregandoUsuarios = $derived(usuariosQuery === undefined || usuariosQuery === null);
// Verificar erros de forma mais robusta // Verificar erros de forma mais robusta (código mantido mas variáveis não utilizadas removidas para lint)
const erroTemplates = $derived.by(() => { /* const erroTemplates = $derived.by(() => {
if (templatesQuery === undefined || templatesQuery === null) return false; if (templatesQuery === undefined || templatesQuery === null) return false;
// Verificar se é um objeto com propriedade error // Verificar se é um objeto com propriedade error
if (typeof templatesQuery === 'object' && 'error' in templatesQuery) { if (typeof templatesQuery === 'object' && 'error' in templatesQuery) {
@@ -117,7 +115,7 @@
return usuariosQuery.error !== undefined && usuariosQuery.error !== null; return usuariosQuery.error !== undefined && usuariosQuery.error !== null;
} }
return false; return false;
}); }); */
// Log para debug (remover depois se necessário) // Log para debug (remover depois se necessário)
$effect(() => { $effect(() => {
@@ -566,7 +564,7 @@
titulo: tituloTemplate.trim(), titulo: tituloTemplate.trim(),
corpo: corpoTemplate.trim(), corpo: corpoTemplate.trim(),
variaveis: variaveis.length > 0 ? variaveis : undefined, variaveis: variaveis.length > 0 ? variaveis : undefined,
criadoPorId: currentUser.data._id as Id<'usuarios'> criadoPorId: currentUser.data!._id as Id<'usuarios'>
}); });
if (resultado.sucesso) { if (resultado.sucesso) {
@@ -592,6 +590,11 @@
} }
async function enviarNotificacao() { async function enviarNotificacao() {
if (!currentUser?.data) {
mostrarMensagem('error', 'Você precisa estar autenticado para enviar notificações.');
return;
}
if (!enviarParaTodos && !destinatarioId) { if (!enviarParaTodos && !destinatarioId) {
mostrarMensagem('error', "Selecione um destinatário ou marque 'Enviar para todos'"); mostrarMensagem('error', "Selecione um destinatário ou marque 'Enviar para todos'");
return; return;
@@ -622,7 +625,7 @@
return; return;
} }
agendadaPara = dataHora.getTime(); agendadaPara = dataHora.getTime();
} catch (error) { } catch {
mostrarMensagem('error', 'Data ou hora inválida'); mostrarMensagem('error', 'Data ou hora inválida');
return; return;
} }
@@ -743,9 +746,9 @@
templateCodigo: template.codigo, templateCodigo: template.codigo,
variaveis: { variaveis: {
nome: destinatario.nome, nome: destinatario.nome,
matricula: destinatario.matricula matricula: destinatario.matricula || ''
}, },
enviadoPor: currentUser.data._id as Id<'usuarios'>, enviadoPor: currentUser.data!._id as Id<'usuarios'>,
agendadaPara: agendadaPara agendadaPara: agendadaPara
}); });
if (emailId) { if (emailId) {
@@ -783,7 +786,7 @@
destinatarioId: destinatario._id as Id<'usuarios'>, destinatarioId: destinatario._id as Id<'usuarios'>,
assunto: 'Notificação do Sistema', assunto: 'Notificação do Sistema',
corpo: mensagemPersonalizada, corpo: mensagemPersonalizada,
enviadoPor: currentUser.data._id as Id<'usuarios'>, enviadoPor: currentUser.data!._id as Id<'usuarios'>,
agendadaPara: agendadaPara agendadaPara: agendadaPara
}); });
if (emailId) { if (emailId) {
@@ -946,7 +949,7 @@
nome: destinatario.nome, nome: destinatario.nome,
matricula: destinatario.matricula || '' matricula: destinatario.matricula || ''
}, },
enviadoPor: currentUser.data._id as Id<'usuarios'>, enviadoPor: currentUser.data!._id as Id<'usuarios'>,
agendadaPara: agendadaPara agendadaPara: agendadaPara
}); });
if (emailId) { if (emailId) {
@@ -992,11 +995,10 @@
destinatarioId: destinatario._id as Id<'usuarios'>, destinatarioId: destinatario._id as Id<'usuarios'>,
assunto: 'Notificação do Sistema', assunto: 'Notificação do Sistema',
corpo: mensagemPersonalizada, corpo: mensagemPersonalizada,
enviadoPor: currentUser.data?._id as Id<'usuarios'>, enviadoPor: currentUser.data!._id as Id<'usuarios'>,
agendadaPara: agendadaPara agendadaPara: agendadaPara
}); });
if (emailId) { if (emailId) {
resultadoEmail = { sucesso: true, emailId };
if (agendadaPara) { if (agendadaPara) {
const dataFormatada = format( const dataFormatada = format(
new Date(agendadaPara), new Date(agendadaPara),
@@ -1203,7 +1205,7 @@
{#if carregandoUsuarios} {#if carregandoUsuarios}
<option disabled>Carregando usuários...</option> <option disabled>Carregando usuários...</option>
{:else if usuarios.length > 0} {:else if usuarios.length > 0}
{#each usuarios as usuario} {#each usuarios as usuario (usuario._id)}
<option value={usuario._id}> <option value={usuario._id}>
{usuario.nome} ({usuario.matricula}) {usuario.nome} ({usuario.matricula})
</option> </option>
@@ -1213,11 +1215,11 @@
{/if} {/if}
</select> </select>
{#if enviarParaTodos} {#if enviarParaTodos}
<label class="label"> <div class="label">
<span class="label-text-alt text-warning"> <span class="label-text-alt text-warning">
⚠️ A notificação será enviada para todos os {usuarios.length} usuários do sistema ⚠️ A notificação será enviada para todos os {usuarios.length} usuários do sistema
</span> </span>
</label> </div>
{/if} {/if}
</div> </div>
@@ -1280,7 +1282,7 @@
{#if carregandoTemplates} {#if carregandoTemplates}
<option disabled>Carregando templates...</option> <option disabled>Carregando templates...</option>
{:else if templates.length > 0} {:else if templates.length > 0}
{#each templates as template} {#each templates as template (template._id)}
<option value={template._id}> <option value={template._id}>
{template.nome} {template.nome}
</option> </option>
@@ -1440,9 +1442,9 @@
<!-- Terminal de Status --> <!-- Terminal de Status -->
<div class="mt-4"> <div class="mt-4">
<div class="mb-2 flex items-center justify-between"> <div class="mb-2 flex items-center justify-between">
<label class="label"> <div class="label pl-0">
<span class="label-text font-medium">Terminal de Status</span> <span class="label-text font-medium">Terminal de Status</span>
</label> </div>
{#if logsEnvio.length > 0} {#if logsEnvio.length > 0}
<button type="button" class="btn btn-sm btn-ghost" onclick={limparLogs}> <button type="button" class="btn btn-sm btn-ghost" onclick={limparLogs}>
<svg <svg
@@ -1471,7 +1473,7 @@
{#if logsEnvio.length === 0} {#if logsEnvio.length === 0}
<div class="text-neutral-content/60 italic">Aguardando envio de notificação...</div> <div class="text-neutral-content/60 italic">Aguardando envio de notificação...</div>
{:else} {:else}
{#each logsEnvio as log} {#each logsEnvio as log, i (i)}
<div class="mb-1"> <div class="mb-1">
<span class="text-neutral-content/60">[{formatarTimestamp(log.timestamp)}]</span> <span class="text-neutral-content/60">[{formatarTimestamp(log.timestamp)}]</span>
<span <span
@@ -1529,7 +1531,7 @@
{:else if templates.length > 0} {:else if templates.length > 0}
<!-- Mostrar templates se existirem --> <!-- Mostrar templates se existirem -->
<div class="max-h-[600px] space-y-3 overflow-y-auto"> <div class="max-h-[600px] space-y-3 overflow-y-auto">
{#each templates as template} {#each templates as template (template._id)}
<div class="card bg-base-200 compact"> <div class="card bg-base-200 compact">
<div class="card-body"> <div class="card-body">
<div class="flex items-start justify-between"> <div class="flex items-start justify-between">
@@ -1731,17 +1733,11 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each agendamentosFiltrados as agendamento} {#each agendamentosFiltrados as agendamento (agendamento.dados._id)}
{@const status = obterStatusAgendamento(agendamento)} {@const status = obterStatusAgendamento(agendamento)}
{@const nomeDestinatario = obterNomeDestinatario(agendamento)} {@const nomeDestinatario = obterNomeDestinatario(agendamento)}
{@const dataFormatada = formatarDataAgendamento(agendamento)} {@const dataFormatada = formatarDataAgendamento(agendamento)}
{@const podeCancelar = status === 'agendado'} {@const podeCancelar = status === 'agendado'}
{@const templateNome =
agendamento.tipo === 'email' && agendamento.dados.templateInfo
? agendamento.dados.templateInfo.nome
: agendamento.tipo === 'email' && agendamento.dados.templateId
? 'Template removido'
: '-'}
<tr> <tr>
<td> <td>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@@ -1898,18 +1894,19 @@
<div class="space-y-4"> <div class="space-y-4">
<!-- Código --> <!-- Código -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label" for="codigo-template">
<span class="label-text font-medium">Código *</span> <span class="label-text font-medium">Código *</span>
<span class="label-text-alt">Ex: AVISO_IMPORTANTE</span> <span class="label-text-alt">Ex: AVISO_IMPORTANTE</span>
</label> </label>
<input <input
id="codigo-template"
type="text" type="text"
bind:value={codigoTemplate} bind:value={codigoTemplate}
placeholder="CODIGO_TEMPLATE" placeholder="CODIGO_TEMPLATE"
class="input input-bordered" class="input input-bordered"
maxlength="50" maxlength="50"
/> />
<label class="label"> <label class="label" for="codigo-template">
<span class="label-text-alt" <span class="label-text-alt"
>Código único para identificar o template (será convertido para MAIÚSCULAS)</span >Código único para identificar o template (será convertido para MAIÚSCULAS)</span
> >
@@ -1918,34 +1915,36 @@
<!-- Nome --> <!-- Nome -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label" for="nome-template">
<span class="label-text font-medium">Nome *</span> <span class="label-text font-medium">Nome *</span>
</label> </label>
<input <input
id="nome-template"
type="text" type="text"
bind:value={nomeTemplate} bind:value={nomeTemplate}
placeholder="Nome do Template" placeholder="Nome do Template"
class="input input-bordered" class="input input-bordered"
maxlength="100" maxlength="100"
/> />
<label class="label"> <label class="label" for="nome-template">
<span class="label-text-alt">Nome exibido na lista de templates</span> <span class="label-text-alt">Nome exibido na lista de templates</span>
</label> </label>
</div> </div>
<!-- Título --> <!-- Título -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label" for="titulo-template">
<span class="label-text font-medium">Título *</span> <span class="label-text font-medium">Título *</span>
</label> </label>
<input <input
id="titulo-template"
type="text" type="text"
bind:value={tituloTemplate} bind:value={tituloTemplate}
placeholder="Título da Mensagem" placeholder="Título da Mensagem"
class="input input-bordered" class="input input-bordered"
maxlength="200" maxlength="200"
/> />
<label class="label"> <label class="label" for="titulo-template">
<span class="label-text-alt" <span class="label-text-alt"
>Título usado no assunto do email ou cabeçalho da mensagem</span >Título usado no assunto do email ou cabeçalho da mensagem</span
> >
@@ -1954,35 +1953,37 @@
<!-- Corpo --> <!-- Corpo -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label" for="corpo-template">
<span class="label-text font-medium">Corpo da Mensagem *</span> <span class="label-text font-medium">Corpo da Mensagem *</span>
</label> </label>
<textarea <textarea
id="corpo-template"
bind:value={corpoTemplate} bind:value={corpoTemplate}
placeholder="Digite o conteúdo da mensagem..." placeholder="Digite o conteúdo da mensagem..."
class="textarea textarea-bordered h-32" class="textarea textarea-bordered h-32"
maxlength="2000" maxlength="2000"
></textarea> ></textarea>
<label class="label"> <label class="label" for="corpo-template">
<span class="label-text-alt" <span class="label-text-alt"
>Use {'{{'}variavel{'}}'} para variáveis dinâmicas (ex: {'{{'}nome{'}}'}, >Use &#123;&#123;variavel&#125;&#125; para variáveis dinâmicas (ex: &#123;&#123;nome&#125;&#125;,
{'{{'}data{'}}'})</span &#123;&#123;data&#125;&#125;)</span
> >
</label> </label>
</div> </div>
<!-- Variáveis --> <!-- Variáveis -->
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label" for="variaveis-template">
<span class="label-text font-medium">Variáveis (opcional)</span> <span class="label-text font-medium">Variáveis (opcional)</span>
</label> </label>
<input <input
id="variaveis-template"
type="text" type="text"
bind:value={variaveisTemplate} bind:value={variaveisTemplate}
placeholder="nome, data, valor (separadas por vírgula)" placeholder="nome, data, valor (separadas por vírgula)"
class="input input-bordered" class="input input-bordered"
/> />
<label class="label"> <label class="label" for="variaveis-template">
<span class="label-text-alt" <span class="label-text-alt"
>Liste as variáveis que podem ser substituídas no template (separadas por vírgula ou >Liste as variáveis que podem ser substituídas no template (separadas por vírgula ou
espaço)</span espaço)</span
@@ -2019,6 +2020,6 @@
</button> </button>
</div> </div>
</div> </div>
<div class="modal-backdrop" onclick={fecharModalNovoTemplate}></div> <button class="modal-backdrop" onclick={fecharModalNovoTemplate}>fechar</button>
</div> </div>
{/if} {/if}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

View File

@@ -162,6 +162,29 @@ export const enfileirarEmail = mutation({
}, },
}); });
/**
* Cancelar agendamento de email
*/
export const cancelarAgendamentoEmail = mutation({
args: {
emailId: v.id("notificacoesEmail"),
},
handler: async (ctx, args) => {
const email = await ctx.db.get(args.emailId);
if (!email) {
return { sucesso: false, erro: "Email não encontrado" };
}
if (email.status !== "pendente") {
return { sucesso: false, erro: "Apenas emails pendentes podem ser cancelados" };
}
// Remove o email da fila
await ctx.db.delete(args.emailId);
return { sucesso: true };
},
});
/** /**
* Enviar email usando template * Enviar email usando template
*/ */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB