feat: enhance scheduling and management of email notifications
- Added functionality to cancel scheduled email notifications, improving user control over their email management. - Implemented a query to list all scheduled emails for the current user, providing better visibility into upcoming notifications. - Enhanced the email schema to support scheduling features, including a timestamp for scheduled delivery. - Improved error handling and user feedback for email scheduling actions, ensuring a smoother user experience.
This commit is contained in:
@@ -4,7 +4,44 @@
|
||||
import { format } from "date-fns";
|
||||
import { ptBR } from "date-fns/locale";
|
||||
import { authStore } from "$lib/stores/auth.svelte";
|
||||
import type { Id } 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";
|
||||
|
||||
interface AgendamentoEmail {
|
||||
_id: Id<"notificacoesEmail">;
|
||||
_creationTime: number;
|
||||
destinatario: string;
|
||||
destinatarioId: Id<"usuarios"> | undefined;
|
||||
assunto: string;
|
||||
corpo: string;
|
||||
templateId: Id<"templatesMensagens"> | undefined;
|
||||
status: "pendente" | "enviando" | "enviado" | "falha";
|
||||
agendadaPara: number | undefined;
|
||||
enviadoPor: Id<"usuarios">;
|
||||
criadoEm: number;
|
||||
enviadoEm: number | undefined;
|
||||
destinatarioInfo: Doc<"usuarios"> | null;
|
||||
templateInfo: Doc<"templatesMensagens"> | null;
|
||||
}
|
||||
|
||||
interface AgendamentoChat {
|
||||
_id: Id<"mensagens">;
|
||||
_creationTime: number;
|
||||
conversaId: Id<"conversas">;
|
||||
remetenteId: Id<"usuarios">;
|
||||
conteudo: string;
|
||||
agendadaPara: number | undefined;
|
||||
enviadaEm: number;
|
||||
conversaInfo: Doc<"conversas"> | null;
|
||||
destinatarioInfo: Doc<"usuarios"> | null;
|
||||
}
|
||||
|
||||
type Agendamento =
|
||||
| { tipo: "email"; dados: AgendamentoEmail }
|
||||
| { tipo: "chat"; dados: AgendamentoChat };
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -16,12 +53,20 @@
|
||||
let emailIdsRastreados = $state<Set<string>>(new Set());
|
||||
|
||||
// Query para buscar status dos emails
|
||||
const emailIdsArray = $derived(Array.from(emailIdsRastreados));
|
||||
const emailIdsArray = $derived(Array.from(emailIdsRastreados).map(id => id as Id<"notificacoesEmail">));
|
||||
const emailsStatusQuery = useQuery(
|
||||
api.email.buscarEmailsPorIds,
|
||||
emailIdsArray.length > 0 ? { emailIds: emailIdsArray as any[] } : undefined
|
||||
emailIdsArray.length > 0 ? { emailIds: emailIdsArray } : undefined
|
||||
);
|
||||
|
||||
// Queries para agendamentos
|
||||
const agendamentosEmailQuery = useQuery(api.email.listarAgendamentosEmail, {});
|
||||
const agendamentosChatQuery = useQuery(api.chat.listarAgendamentosChat, {});
|
||||
|
||||
// Filtro de agendamentos
|
||||
type FiltroAgendamento = "todos" | "agendados" | "enviados";
|
||||
let filtroAgendamento = $state<FiltroAgendamento>("todos");
|
||||
|
||||
// Extrair dados das queries de forma robusta
|
||||
const templates = $derived.by(() => {
|
||||
if (templatesQuery === undefined || templatesQuery === null) {
|
||||
@@ -241,6 +286,151 @@
|
||||
emailIdsRastreados = new Set();
|
||||
}
|
||||
|
||||
// Extrair e processar agendamentos
|
||||
const agendamentosEmail = $derived.by(() => {
|
||||
if (!agendamentosEmailQuery || agendamentosEmailQuery === undefined) return [];
|
||||
const dados = Array.isArray(agendamentosEmailQuery)
|
||||
? agendamentosEmailQuery
|
||||
: "data" in agendamentosEmailQuery && Array.isArray(agendamentosEmailQuery.data)
|
||||
? agendamentosEmailQuery.data
|
||||
: [];
|
||||
return dados as AgendamentoEmail[];
|
||||
});
|
||||
|
||||
const agendamentosChat = $derived.by(() => {
|
||||
if (!agendamentosChatQuery || agendamentosChatQuery === undefined) return [];
|
||||
const dados = Array.isArray(agendamentosChatQuery)
|
||||
? agendamentosChatQuery
|
||||
: "data" in agendamentosChatQuery && Array.isArray(agendamentosChatQuery.data)
|
||||
? agendamentosChatQuery.data
|
||||
: [];
|
||||
return dados as AgendamentoChat[];
|
||||
});
|
||||
|
||||
// Combinar e processar agendamentos
|
||||
const todosAgendamentos = $derived.by(() => {
|
||||
const agendamentos: Agendamento[] = [];
|
||||
|
||||
for (const email of agendamentosEmail) {
|
||||
if (email.agendadaPara) {
|
||||
agendamentos.push({
|
||||
tipo: "email",
|
||||
dados: email,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const chat of agendamentosChat) {
|
||||
if (chat.agendadaPara) {
|
||||
agendamentos.push({
|
||||
tipo: "chat",
|
||||
dados: chat,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Ordenar: futuros primeiro (mais próximos primeiro), depois passados (mais recentes primeiro)
|
||||
return agendamentos.sort((a, b) => {
|
||||
const timestampA = a.tipo === "email" ? (a.dados.agendadaPara ?? 0) : (a.dados.agendadaPara ?? 0);
|
||||
const timestampB = b.tipo === "email" ? (b.dados.agendadaPara ?? 0) : (b.dados.agendadaPara ?? 0);
|
||||
const agora = Date.now();
|
||||
|
||||
const aFuturo = timestampA > agora;
|
||||
const bFuturo = timestampB > agora;
|
||||
|
||||
// Futuros primeiro
|
||||
if (aFuturo && !bFuturo) return -1;
|
||||
if (!aFuturo && bFuturo) return 1;
|
||||
|
||||
// Dentro do mesmo grupo, ordenar por timestamp
|
||||
if (aFuturo) {
|
||||
// Futuros: mais próximos primeiro
|
||||
return timestampA - timestampB;
|
||||
} else {
|
||||
// Passados: mais recentes primeiro
|
||||
return timestampB - timestampA;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Filtrar agendamentos
|
||||
const agendamentosFiltrados = $derived.by(() => {
|
||||
if (filtroAgendamento === "todos") return todosAgendamentos;
|
||||
|
||||
return todosAgendamentos.filter(ag => {
|
||||
const status = obterStatusAgendamento(ag);
|
||||
if (filtroAgendamento === "agendados") return status === "agendado";
|
||||
if (filtroAgendamento === "enviados") return status === "enviado";
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
// Função para obter status do agendamento
|
||||
function obterStatusAgendamento(agendamento: Agendamento): StatusAgendamento {
|
||||
if (agendamento.tipo === "email") {
|
||||
const email = agendamento.dados;
|
||||
if (email.status === "enviado") return "enviado";
|
||||
if (email.agendadaPara && email.agendadaPara <= Date.now()) return "enviado";
|
||||
return "agendado";
|
||||
} else {
|
||||
const chat = agendamento.dados;
|
||||
if (chat.agendadaPara && chat.agendadaPara <= Date.now()) return "enviado";
|
||||
return "agendado";
|
||||
}
|
||||
}
|
||||
|
||||
// Função para cancelar agendamento
|
||||
async function cancelarAgendamento(agendamento: Agendamento) {
|
||||
if (!confirm("Tem certeza que deseja cancelar este agendamento?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (agendamento.tipo === "email") {
|
||||
const resultado = await client.mutation(api.email.cancelarAgendamentoEmail, {
|
||||
emailId: agendamento.dados._id,
|
||||
});
|
||||
if (resultado.sucesso) {
|
||||
mostrarMensagem("success", "Agendamento de email cancelado com sucesso!");
|
||||
} else {
|
||||
mostrarMensagem("error", resultado.erro || "Erro ao cancelar agendamento");
|
||||
}
|
||||
} else {
|
||||
const resultado = await client.mutation(api.chat.cancelarMensagemAgendada, {
|
||||
mensagemId: agendamento.dados._id,
|
||||
});
|
||||
if (resultado.sucesso) {
|
||||
mostrarMensagem("success", "Agendamento de chat cancelado com sucesso!");
|
||||
} else {
|
||||
mostrarMensagem("error", resultado.erro || "Erro ao cancelar agendamento");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
mostrarMensagem("error", `Erro ao cancelar agendamento: ${erro}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Função para obter nome do destinatário
|
||||
function obterNomeDestinatario(agendamento: Agendamento): string {
|
||||
if (agendamento.tipo === "email") {
|
||||
return agendamento.dados.destinatarioInfo?.nome || agendamento.dados.destinatario || "Usuário";
|
||||
} else {
|
||||
return agendamento.dados.destinatarioInfo?.nome || "Usuário";
|
||||
}
|
||||
}
|
||||
|
||||
// Função para formatar data/hora do agendamento
|
||||
function formatarDataAgendamento(agendamento: Agendamento): string {
|
||||
const timestamp = agendamento.tipo === "email"
|
||||
? agendamento.dados.agendadaPara
|
||||
: agendamento.dados.agendadaPara;
|
||||
|
||||
if (!timestamp) return "N/A";
|
||||
|
||||
return format(new Date(timestamp), "dd/MM/yyyy 'às' HH:mm", { locale: ptBR });
|
||||
}
|
||||
|
||||
// Função para formatar timestamp
|
||||
function formatarTimestamp(timestamp: number): string {
|
||||
return format(new Date(timestamp), "HH:mm:ss", { locale: ptBR });
|
||||
@@ -278,9 +468,10 @@
|
||||
} else {
|
||||
mostrarMensagem("error", "Erro ao criar templates padrão.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error("Erro ao criar templates:", error);
|
||||
mostrarMensagem("error", "Erro ao criar templates: " + (error.message || "Erro desconhecido"));
|
||||
mostrarMensagem("error", "Erro ao criar templates: " + erro);
|
||||
} finally {
|
||||
criandoTemplates = false;
|
||||
}
|
||||
@@ -351,9 +542,10 @@
|
||||
} else {
|
||||
mostrarMensagem("error", "Erro ao criar template: " + (resultado.erro || "Erro desconhecido"));
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error("Erro ao criar template:", error);
|
||||
mostrarMensagem("error", "Erro ao criar template: " + (error.message || "Erro desconhecido"));
|
||||
mostrarMensagem("error", "Erro ao criar template: " + erro);
|
||||
} finally {
|
||||
criandoNovoTemplate = false;
|
||||
}
|
||||
@@ -436,7 +628,7 @@
|
||||
adicionarLog("chat", destinatario.nome, "enviando", "Criando/buscando conversa...");
|
||||
const conversaResult = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{ outroUsuarioId: destinatario._id as any }
|
||||
{ outroUsuarioId: destinatario._id as Id<"usuarios"> }
|
||||
);
|
||||
|
||||
if (conversaResult.conversaId) {
|
||||
@@ -468,9 +660,10 @@
|
||||
} else {
|
||||
adicionarLog("chat", destinatario.nome, "erro", "Falha ao criar/buscar conversa");
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error("Erro ao enviar chat:", error);
|
||||
adicionarLog("chat", destinatario.nome, "erro", `Erro: ${error.message || "Erro desconhecido"}`);
|
||||
adicionarLog("chat", destinatario.nome, "erro", `Erro: ${erro}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +677,7 @@
|
||||
if (template) {
|
||||
resultadoEmail = await client.mutation(api.email.enviarEmailComTemplate, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
templateCodigo: template.codigo,
|
||||
variaveis: {
|
||||
nome: destinatario.nome,
|
||||
@@ -509,7 +702,7 @@
|
||||
} else {
|
||||
resultadoEmail = await client.mutation(api.email.enfileirarEmail, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
@@ -526,9 +719,10 @@
|
||||
adicionarLog("email", destinatario.nome, "erro", "Falha ao enfileirar email");
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error("Erro ao enviar email:", error);
|
||||
adicionarLog("email", destinatario.nome, "erro", `Erro: ${error.message || "Erro desconhecido"}`);
|
||||
adicionarLog("email", destinatario.nome, "erro", `Erro: ${erro}`);
|
||||
}
|
||||
} else {
|
||||
adicionarLog("email", destinatario.nome, "erro", "Destinatário não possui email cadastrado");
|
||||
@@ -577,7 +771,7 @@
|
||||
adicionarLog("chat", destinatario.nome, "enviando", "Processando...");
|
||||
const conversaResult = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{ outroUsuarioId: destinatario._id as any }
|
||||
{ outroUsuarioId: destinatario._id as Id<"usuarios"> }
|
||||
);
|
||||
|
||||
if (conversaResult.conversaId) {
|
||||
@@ -609,9 +803,10 @@
|
||||
adicionarLog("chat", destinatario.nome, "erro", "Falha ao criar/buscar conversa");
|
||||
falhasChat++;
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error(`Erro ao enviar chat para ${destinatario.nome}:`, error);
|
||||
adicionarLog("chat", destinatario.nome, "erro", `Erro: ${error.message || "Erro desconhecido"}`);
|
||||
adicionarLog("chat", destinatario.nome, "erro", `Erro: ${erro}`);
|
||||
falhasChat++;
|
||||
}
|
||||
}
|
||||
@@ -626,7 +821,7 @@
|
||||
if (template) {
|
||||
const resultadoEmail = await client.mutation(api.email.enviarEmailComTemplate, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
templateCodigo: template.codigo,
|
||||
variaveis: {
|
||||
nome: destinatario.nome,
|
||||
@@ -654,7 +849,7 @@
|
||||
} else {
|
||||
const resultadoEmail = await client.mutation(api.email.enfileirarEmail, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
destinatarioId: destinatario._id as Id<"usuarios">,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
@@ -673,9 +868,10 @@
|
||||
falhasEmail++;
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error(`Erro ao enviar email para ${destinatario.nome}:`, error);
|
||||
adicionarLog("email", destinatario.nome, "erro", `Erro: ${error.message || "Erro desconhecido"}`);
|
||||
adicionarLog("email", destinatario.nome, "erro", `Erro: ${erro}`);
|
||||
falhasEmail++;
|
||||
}
|
||||
} else {
|
||||
@@ -723,10 +919,11 @@
|
||||
agendarEnvio = false;
|
||||
dataAgendamento = "";
|
||||
horaAgendamento = "";
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
const erro = error instanceof Error ? error.message : "Erro desconhecido";
|
||||
console.error("Erro ao enviar notificação:", error);
|
||||
adicionarLog("email", "Sistema", "erro", `Erro geral: ${error.message || "Erro desconhecido"}`);
|
||||
mostrarMensagem("error", "Erro ao enviar notificação: " + (error.message || "Erro desconhecido"));
|
||||
adicionarLog("email", "Sistema", "erro", `Erro geral: ${erro}`);
|
||||
mostrarMensagem("error", "Erro ao enviar notificação: " + erro);
|
||||
} finally {
|
||||
processando = false;
|
||||
progressoEnvio = { total: 0, enviados: 0, falhas: 0 };
|
||||
@@ -1188,6 +1385,162 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Histórico de Agendamentos -->
|
||||
<div class="card bg-base-100 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="p-2 bg-secondary/10 rounded-lg">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-secondary" 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>
|
||||
</div>
|
||||
<h2 class="card-title">Histórico de Agendamentos</h2>
|
||||
</div>
|
||||
|
||||
<!-- Filtros -->
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm {filtroAgendamento === 'todos' ? 'btn-primary' : 'btn-ghost'}"
|
||||
onclick={() => filtroAgendamento = "todos"}
|
||||
>
|
||||
Todos
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm {filtroAgendamento === 'agendados' ? 'btn-primary' : 'btn-ghost'}"
|
||||
onclick={() => filtroAgendamento = "agendados"}
|
||||
>
|
||||
Agendados
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm {filtroAgendamento === 'enviados' ? 'btn-primary' : 'btn-ghost'}"
|
||||
onclick={() => filtroAgendamento = "enviados"}
|
||||
>
|
||||
Enviados
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if agendamentosFiltrados.length === 0}
|
||||
<div class="text-center py-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto mb-4 opacity-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>
|
||||
<p class="font-medium text-base-content mb-2">Nenhum agendamento encontrado</p>
|
||||
<p class="text-sm text-base-content/60">Os agendamentos aparecerão aqui quando você agendar envios.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Tabela de Agendamentos -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Destinatário</th>
|
||||
<th>Data/Hora</th>
|
||||
<th>Status</th>
|
||||
<th>Template</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each agendamentosFiltrados as agendamento}
|
||||
{@const status = obterStatusAgendamento(agendamento)}
|
||||
{@const nomeDestinatario = obterNomeDestinatario(agendamento)}
|
||||
{@const dataFormatada = formatarDataAgendamento(agendamento)}
|
||||
{@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>
|
||||
<td>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if agendamento.tipo === "email"}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-info" 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>
|
||||
<span class="badge badge-info badge-sm">Email</span>
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
<span class="badge badge-primary badge-sm">Chat</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">{nomeDestinatario}</div>
|
||||
{#if agendamento.tipo === "email"}
|
||||
<div class="text-xs text-base-content/60">{agendamento.dados.destinatario}</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">{dataFormatada}</div>
|
||||
{#if podeCancelar}
|
||||
{@const tempoRestante = agendamento.tipo === "email"
|
||||
? (agendamento.dados.agendadaPara ?? 0) - Date.now()
|
||||
: (agendamento.dados.agendadaPara ?? 0) - Date.now()}
|
||||
{@const horasRestantes = Math.floor(tempoRestante / (1000 * 60 * 60))}
|
||||
{@const minutosRestantes = Math.floor((tempoRestante % (1000 * 60 * 60)) / (1000 * 60))}
|
||||
{#if horasRestantes < 1 && minutosRestantes < 60}
|
||||
<div class="text-xs text-warning">Em {minutosRestantes} min</div>
|
||||
{:else if horasRestantes < 24}
|
||||
<div class="text-xs text-info">Em {horasRestantes}h {minutosRestantes}min</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if status === "agendado"}
|
||||
<span class="badge badge-warning badge-sm">Agendado</span>
|
||||
{:else if status === "enviado"}
|
||||
<span class="badge badge-success badge-sm">Enviado</span>
|
||||
{:else}
|
||||
<span class="badge badge-error badge-sm">Cancelado</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if agendamento.tipo === "email"}
|
||||
{#if agendamento.dados.templateInfo}
|
||||
<div class="text-sm">{agendamento.dados.templateInfo.nome}</div>
|
||||
{:else if agendamento.dados.templateId}
|
||||
<div class="text-sm text-base-content/60 italic">Template removido</div>
|
||||
{:else}
|
||||
<div class="text-sm text-base-content/60">-</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-sm text-base-content/60">-</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
{#if podeCancelar}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-error btn-outline"
|
||||
onclick={() => cancelarAgendamento(agendamento)}
|
||||
>
|
||||
<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="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
Cancelar
|
||||
</button>
|
||||
{:else}
|
||||
<span class="text-sm text-base-content/60">-</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="alert alert-warning mt-6">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
||||
|
||||
Reference in New Issue
Block a user