Fix usuarios page #6
@@ -1,25 +1,193 @@
|
||||
<script lang="ts">
|
||||
import { useQuery, useConvexClient } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
import { authStore } from "$lib/stores/auth.svelte";
|
||||
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
|
||||
|
||||
const client = useConvexClient();
|
||||
const templates = useQuery(api.templatesMensagens.listarTemplates, {});
|
||||
const usuarios = useQuery(api.usuarios.listar, {});
|
||||
|
||||
// Queries
|
||||
const templatesQuery = useQuery(api.templatesMensagens.listarTemplates, {});
|
||||
const usuariosQuery = useQuery(api.usuarios.listar, {});
|
||||
|
||||
// Extrair dados das queries de forma robusta
|
||||
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 usuarios = $derived.by(() => {
|
||||
if (usuariosQuery === undefined || usuariosQuery === null) {
|
||||
return [];
|
||||
}
|
||||
if ("data" in usuariosQuery && usuariosQuery.data !== undefined) {
|
||||
return Array.isArray(usuariosQuery.data) ? usuariosQuery.data : [];
|
||||
}
|
||||
if (Array.isArray(usuariosQuery)) {
|
||||
return usuariosQuery;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Estados de carregamento e erro
|
||||
const carregandoTemplates = $derived(
|
||||
templatesQuery === undefined || templatesQuery === null
|
||||
);
|
||||
const carregandoUsuarios = $derived(
|
||||
usuariosQuery === undefined || usuariosQuery === null
|
||||
);
|
||||
|
||||
// Verificar erros de forma mais robusta
|
||||
const erroTemplates = $derived.by(() => {
|
||||
if (templatesQuery === undefined || templatesQuery === null) return false;
|
||||
// Verificar se é um objeto com propriedade error
|
||||
if (typeof templatesQuery === 'object' && 'error' in templatesQuery) {
|
||||
return templatesQuery.error !== undefined && templatesQuery.error !== null;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const erroUsuarios = $derived.by(() => {
|
||||
if (usuariosQuery === undefined || usuariosQuery === null) return false;
|
||||
if (typeof usuariosQuery === 'object' && 'error' in usuariosQuery) {
|
||||
return usuariosQuery.error !== undefined && usuariosQuery.error !== null;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Log para debug (remover depois se necessário)
|
||||
$effect(() => {
|
||||
if (templatesQuery !== undefined && templatesQuery !== null) {
|
||||
console.log("Templates Query:", templatesQuery);
|
||||
console.log("Templates Extraídos:", templates);
|
||||
}
|
||||
});
|
||||
|
||||
let destinatarioId = $state("");
|
||||
let enviarParaTodos = $state(false);
|
||||
let canal = $state<"chat" | "email" | "ambos">("chat");
|
||||
let templateId = $state("");
|
||||
let mensagemPersonalizada = $state("");
|
||||
let usarTemplate = $state(true);
|
||||
let processando = $state(false);
|
||||
let criandoTemplates = $state(false);
|
||||
let progressoEnvio = $state({ total: 0, enviados: 0, falhas: 0 });
|
||||
|
||||
// Estados para modal de novo template
|
||||
let modalNovoTemplateAberto = $state(false);
|
||||
let codigoTemplate = $state("");
|
||||
let nomeTemplate = $state("");
|
||||
let tituloTemplate = $state("");
|
||||
let corpoTemplate = $state("");
|
||||
let variaveisTemplate = $state("");
|
||||
let criandoNovoTemplate = $state(false);
|
||||
|
||||
const templateSelecionado = $derived(
|
||||
templates?.data?.find(t => t._id === templateId)
|
||||
templates.find(t => t._id === templateId)
|
||||
);
|
||||
|
||||
async function criarTemplatesPadrao() {
|
||||
if (criandoTemplates) return;
|
||||
|
||||
criandoTemplates = true;
|
||||
try {
|
||||
const resultado = await client.mutation(api.templatesMensagens.criarTemplatesPadrao, {});
|
||||
if (resultado.sucesso) {
|
||||
alert("✅ Templates padrão criados com sucesso! A página será recarregada.");
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert("❌ Erro ao criar templates padrão.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Erro ao criar templates:", error);
|
||||
alert("❌ Erro ao criar templates: " + (error.message || "Erro desconhecido"));
|
||||
} finally {
|
||||
criandoTemplates = false;
|
||||
}
|
||||
}
|
||||
|
||||
function abrirModalNovoTemplate() {
|
||||
modalNovoTemplateAberto = true;
|
||||
// Limpar campos
|
||||
codigoTemplate = "";
|
||||
nomeTemplate = "";
|
||||
tituloTemplate = "";
|
||||
corpoTemplate = "";
|
||||
variaveisTemplate = "";
|
||||
}
|
||||
|
||||
function fecharModalNovoTemplate() {
|
||||
modalNovoTemplateAberto = false;
|
||||
}
|
||||
|
||||
async function salvarNovoTemplate() {
|
||||
if (!authStore.usuario) {
|
||||
alert("❌ Você precisa estar autenticado para criar templates.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validações
|
||||
if (!codigoTemplate.trim()) {
|
||||
alert("❌ O código do template é obrigatório.");
|
||||
return;
|
||||
}
|
||||
if (!nomeTemplate.trim()) {
|
||||
alert("❌ O nome do template é obrigatório.");
|
||||
return;
|
||||
}
|
||||
if (!tituloTemplate.trim()) {
|
||||
alert("❌ O título do template é obrigatório.");
|
||||
return;
|
||||
}
|
||||
if (!corpoTemplate.trim()) {
|
||||
alert("❌ O corpo do template é obrigatório.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Processar variáveis (separadas por vírgula ou espaço)
|
||||
const variaveis = variaveisTemplate
|
||||
.split(/[,;\s]+/)
|
||||
.map(v => v.trim())
|
||||
.filter(v => v.length > 0);
|
||||
|
||||
criandoNovoTemplate = true;
|
||||
try {
|
||||
const resultado = await client.mutation(api.templatesMensagens.criarTemplate, {
|
||||
codigo: codigoTemplate.trim().toUpperCase().replace(/\s+/g, "_"),
|
||||
nome: nomeTemplate.trim(),
|
||||
titulo: tituloTemplate.trim(),
|
||||
corpo: corpoTemplate.trim(),
|
||||
variaveis: variaveis.length > 0 ? variaveis : undefined,
|
||||
criadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
});
|
||||
|
||||
if (resultado.sucesso) {
|
||||
alert("✅ Template criado com sucesso!");
|
||||
fecharModalNovoTemplate();
|
||||
// Recarregar a página para atualizar a lista
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert("❌ Erro ao criar template: " + (resultado.erro || "Erro desconhecido"));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Erro ao criar template:", error);
|
||||
alert("❌ Erro ao criar template: " + (error.message || "Erro desconhecido"));
|
||||
} finally {
|
||||
criandoNovoTemplate = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function enviarNotificacao() {
|
||||
if (!destinatarioId) {
|
||||
alert("Selecione um destinatário");
|
||||
if (!enviarParaTodos && !destinatarioId) {
|
||||
alert("Selecione um destinatário ou marque 'Enviar para todos'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,93 +202,207 @@
|
||||
}
|
||||
|
||||
processando = true;
|
||||
try {
|
||||
const destinatario = usuarios?.data?.find(u => u._id === destinatarioId);
|
||||
progressoEnvio = { total: 0, enviados: 0, falhas: 0 };
|
||||
|
||||
if (!destinatario) {
|
||||
alert("Destinatário não encontrado");
|
||||
try {
|
||||
// Obter lista de destinatários
|
||||
const destinatarios: typeof usuarios = enviarParaTodos
|
||||
? usuarios
|
||||
: usuarios.filter(u => u._id === destinatarioId);
|
||||
|
||||
if (destinatarios.length === 0) {
|
||||
alert("Nenhum destinatário encontrado");
|
||||
return;
|
||||
}
|
||||
|
||||
let resultadoChat = null;
|
||||
let resultadoEmail = null;
|
||||
progressoEnvio.total = destinatarios.length;
|
||||
|
||||
// ENVIAR PARA CHAT
|
||||
if (canal === "chat" || canal === "ambos") {
|
||||
const conversaResult = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{ outroUsuarioId: destinatarioId as any }
|
||||
);
|
||||
// Se for envio para um único usuário
|
||||
if (destinatarios.length === 1) {
|
||||
const destinatario = destinatarios[0];
|
||||
let resultadoChat = null;
|
||||
let resultadoEmail = null;
|
||||
|
||||
if (conversaResult.conversaId) {
|
||||
const mensagem = usarTemplate
|
||||
? templateSelecionado?.corpo || ""
|
||||
: mensagemPersonalizada;
|
||||
// ENVIAR PARA CHAT
|
||||
if (canal === "chat" || canal === "ambos") {
|
||||
try {
|
||||
const conversaResult = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{ outroUsuarioId: destinatario._id as any }
|
||||
);
|
||||
|
||||
resultadoChat = await client.mutation(api.chat.enviarMensagem, {
|
||||
conversaId: conversaResult.conversaId,
|
||||
conteudo: mensagem,
|
||||
tipo: "texto", // Tipo de mensagem
|
||||
permitirNotificacaoParaSiMesmo: true, // ✅ Permite notificação para si mesmo via painel admin
|
||||
});
|
||||
}
|
||||
}
|
||||
if (conversaResult.conversaId) {
|
||||
const mensagem = usarTemplate
|
||||
? templateSelecionado?.corpo || ""
|
||||
: mensagemPersonalizada;
|
||||
|
||||
// ENVIAR PARA EMAIL
|
||||
if (canal === "email" || canal === "ambos") {
|
||||
if (!destinatario.email) {
|
||||
alert("Destinatário não possui email cadastrado");
|
||||
processando = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (usarTemplate && templateId) {
|
||||
// Usar template
|
||||
const template = templateSelecionado;
|
||||
if (template) {
|
||||
resultadoEmail = await client.mutation(api.email.enviarEmailComTemplate, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
templateCodigo: template.codigo,
|
||||
variaveis: {
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula,
|
||||
},
|
||||
enviadoPorId: destinatario._id as any, // TODO: Pegar usuário logado
|
||||
});
|
||||
resultadoChat = await client.mutation(api.chat.enviarMensagem, {
|
||||
conversaId: conversaResult.conversaId,
|
||||
conteudo: mensagem,
|
||||
tipo: "texto",
|
||||
permitirNotificacaoParaSiMesmo: true,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erro ao enviar chat:", error);
|
||||
}
|
||||
} else {
|
||||
// Mensagem personalizada
|
||||
resultadoEmail = await client.mutation(api.email.enfileirarEmail, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: destinatario._id as any, // TODO: Pegar usuário logado
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Feedback de sucesso
|
||||
let mensagem = "Notificação enviada com sucesso!";
|
||||
if (canal === "ambos") {
|
||||
if (resultadoChat && resultadoEmail) {
|
||||
mensagem = "✅ Notificação enviada para Chat e Email!";
|
||||
} else if (resultadoChat) {
|
||||
mensagem = "✅ Notificação enviada para Chat. Email falhou.";
|
||||
} else if (resultadoEmail) {
|
||||
mensagem = "✅ Notificação enviada para Email. Chat falhou.";
|
||||
// ENVIAR PARA EMAIL
|
||||
if (canal === "email" || canal === "ambos") {
|
||||
if (destinatario.email) {
|
||||
try {
|
||||
if (usarTemplate && templateId) {
|
||||
const template = templateSelecionado;
|
||||
if (template) {
|
||||
resultadoEmail = await client.mutation(api.email.enviarEmailComTemplate, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
templateCodigo: template.codigo,
|
||||
variaveis: {
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula,
|
||||
},
|
||||
enviadoPorId: destinatario._id as any,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resultadoEmail = await client.mutation(api.email.enfileirarEmail, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: destinatario._id as any,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erro ao enviar email:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (canal === "chat" && resultadoChat) {
|
||||
mensagem = "✅ Mensagem enviada no Chat!";
|
||||
} else if (canal === "email" && resultadoEmail) {
|
||||
mensagem = "✅ Email enfileirado para envio!";
|
||||
}
|
||||
|
||||
alert(mensagem);
|
||||
// Feedback de sucesso
|
||||
let mensagem = "Notificação enviada com sucesso!";
|
||||
if (canal === "ambos") {
|
||||
if (resultadoChat && resultadoEmail) {
|
||||
mensagem = "✅ Notificação enviada para Chat e Email!";
|
||||
} else if (resultadoChat) {
|
||||
mensagem = "✅ Notificação enviada para Chat. Email falhou.";
|
||||
} else if (resultadoEmail) {
|
||||
mensagem = "✅ Notificação enviada para Email. Chat falhou.";
|
||||
}
|
||||
} else if (canal === "chat" && resultadoChat) {
|
||||
mensagem = "✅ Mensagem enviada no Chat!";
|
||||
} else if (canal === "email" && resultadoEmail) {
|
||||
mensagem = "✅ Email enfileirado para envio!";
|
||||
}
|
||||
|
||||
alert(mensagem);
|
||||
progressoEnvio.enviados = 1;
|
||||
} else {
|
||||
// ENVIO EM MASSA
|
||||
let sucessosChat = 0;
|
||||
let sucessosEmail = 0;
|
||||
let falhasChat = 0;
|
||||
let falhasEmail = 0;
|
||||
|
||||
for (const destinatario of destinatarios) {
|
||||
try {
|
||||
// ENVIAR PARA CHAT
|
||||
if (canal === "chat" || canal === "ambos") {
|
||||
try {
|
||||
const conversaResult = await client.mutation(
|
||||
api.chat.criarOuBuscarConversaIndividual,
|
||||
{ outroUsuarioId: destinatario._id as any }
|
||||
);
|
||||
|
||||
if (conversaResult.conversaId) {
|
||||
// Para templates, usar corpo direto (o backend já faz substituição via email)
|
||||
// Para mensagem personalizada, usar diretamente
|
||||
const mensagem = usarTemplate
|
||||
? templateSelecionado?.corpo || ""
|
||||
: mensagemPersonalizada;
|
||||
|
||||
await client.mutation(api.chat.enviarMensagem, {
|
||||
conversaId: conversaResult.conversaId,
|
||||
conteudo: mensagem,
|
||||
tipo: "texto",
|
||||
permitirNotificacaoParaSiMesmo: true,
|
||||
});
|
||||
sucessosChat++;
|
||||
} else {
|
||||
falhasChat++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Erro ao enviar chat para ${destinatario.nome}:`, error);
|
||||
falhasChat++;
|
||||
}
|
||||
}
|
||||
|
||||
// ENVIAR PARA EMAIL
|
||||
if (canal === "email" || canal === "ambos") {
|
||||
if (destinatario.email) {
|
||||
try {
|
||||
if (usarTemplate && templateId) {
|
||||
const template = templateSelecionado;
|
||||
if (template) {
|
||||
await client.mutation(api.email.enviarEmailComTemplate, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
templateCodigo: template.codigo,
|
||||
variaveis: {
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula || "",
|
||||
},
|
||||
enviadoPorId: destinatario._id as any,
|
||||
});
|
||||
sucessosEmail++;
|
||||
} else {
|
||||
falhasEmail++;
|
||||
}
|
||||
} else {
|
||||
await client.mutation(api.email.enfileirarEmail, {
|
||||
destinatario: destinatario.email,
|
||||
destinatarioId: destinatario._id as any,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: destinatario._id as any,
|
||||
});
|
||||
sucessosEmail++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Erro ao enviar email para ${destinatario.nome}:`, error);
|
||||
falhasEmail++;
|
||||
}
|
||||
} else {
|
||||
falhasEmail++;
|
||||
}
|
||||
}
|
||||
|
||||
progressoEnvio.enviados++;
|
||||
} catch (error) {
|
||||
console.error(`Erro geral ao enviar para ${destinatario.nome}:`, error);
|
||||
progressoEnvio.falhas++;
|
||||
}
|
||||
}
|
||||
|
||||
// Feedback de envio em massa
|
||||
let mensagem = `✅ Envio em massa concluído!\n\n`;
|
||||
if (canal === "ambos") {
|
||||
mensagem += `Chat: ${sucessosChat} enviados, ${falhasChat} falhas\n`;
|
||||
mensagem += `Email: ${sucessosEmail} enviados, ${falhasEmail} falhas`;
|
||||
} else if (canal === "chat") {
|
||||
mensagem += `Chat: ${sucessosChat} enviados, ${falhasChat} falhas`;
|
||||
} else if (canal === "email") {
|
||||
mensagem += `Email: ${sucessosEmail} enviados, ${falhasEmail} falhas`;
|
||||
}
|
||||
|
||||
alert(mensagem);
|
||||
}
|
||||
|
||||
// Limpar form
|
||||
destinatarioId = "";
|
||||
enviarParaTodos = false;
|
||||
templateId = "";
|
||||
mensagemPersonalizada = "";
|
||||
} catch (error: any) {
|
||||
@@ -128,6 +410,7 @@
|
||||
alert("Erro ao enviar notificação: " + (error.message || "Erro desconhecido"));
|
||||
} finally {
|
||||
processando = false;
|
||||
progressoEnvio = { total: 0, enviados: 0, falhas: 0 };
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -156,19 +439,46 @@
|
||||
|
||||
<!-- Destinatário -->
|
||||
<div class="form-control mb-4">
|
||||
<label class="label" for="destinatario-select">
|
||||
<span class="label-text font-medium">Destinatário *</span>
|
||||
</label>
|
||||
<select id="destinatario-select" bind:value={destinatarioId} class="select select-bordered">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="label" for="destinatario-select">
|
||||
<span class="label-text font-medium">Destinatário *</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={enviarParaTodos}
|
||||
class="checkbox checkbox-primary checkbox-sm"
|
||||
disabled={processando}
|
||||
/>
|
||||
<span class="label-text text-sm">Enviar para todos ({usuarios.length} usuários)</span>
|
||||
</label>
|
||||
</div>
|
||||
<select
|
||||
id="destinatario-select"
|
||||
bind:value={destinatarioId}
|
||||
class="select select-bordered"
|
||||
disabled={enviarParaTodos || processando}
|
||||
>
|
||||
<option value="">Selecione um usuário</option>
|
||||
{#if usuarios?.data}
|
||||
{#each usuarios.data as usuario}
|
||||
{#if carregandoUsuarios}
|
||||
<option disabled>Carregando usuários...</option>
|
||||
{:else if usuarios.length > 0}
|
||||
{#each usuarios as usuario}
|
||||
<option value={usuario._id}>
|
||||
{usuario.nome} ({usuario.matricula})
|
||||
</option>
|
||||
{/each}
|
||||
{:else}
|
||||
<option disabled>Nenhum usuário disponível</option>
|
||||
{/if}
|
||||
</select>
|
||||
{#if enviarParaTodos}
|
||||
<label class="label">
|
||||
<span class="label-text-alt text-warning">
|
||||
⚠️ A notificação será enviada para todos os {usuarios.length} usuários do sistema
|
||||
</span>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Canal -->
|
||||
@@ -242,12 +552,16 @@
|
||||
</label>
|
||||
<select id="template-select" bind:value={templateId} class="select select-bordered">
|
||||
<option value="">Selecione um template</option>
|
||||
{#if templates?.data}
|
||||
{#each templates.data as template}
|
||||
{#if carregandoTemplates}
|
||||
<option disabled>Carregando templates...</option>
|
||||
{:else if templates.length > 0}
|
||||
{#each templates as template}
|
||||
<option value={template._id}>
|
||||
{template.nome}
|
||||
</option>
|
||||
{/each}
|
||||
{:else}
|
||||
<option disabled>Nenhum template disponível</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
@@ -282,17 +596,28 @@
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<button
|
||||
class="btn btn-primary btn-block"
|
||||
onclick={enviarNotificacao}
|
||||
onclick={() => enviarNotificacao()}
|
||||
disabled={processando}
|
||||
>
|
||||
{#if processando}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{#if progressoEnvio.total > 1}
|
||||
<span class="ml-2">
|
||||
Enviando... ({progressoEnvio.enviados}/{progressoEnvio.total})
|
||||
</span>
|
||||
{:else}
|
||||
<span class="ml-2">Enviando...</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<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="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
||||
</svg>
|
||||
{/if}
|
||||
Enviar Notificação
|
||||
{#if enviarParaTodos && !processando}
|
||||
Enviar para Todos ({usuarios.length})
|
||||
{:else if !processando}
|
||||
Enviar Notificação
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,7 +628,11 @@
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="card-title">Templates Disponíveis</h2>
|
||||
<button class="btn btn-sm btn-outline btn-primary">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline btn-primary"
|
||||
onclick={() => abrirModalNovoTemplate()}
|
||||
>
|
||||
<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 4v16m8-8H4" />
|
||||
</svg>
|
||||
@@ -311,17 +640,15 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if !templates?.data}
|
||||
<div class="flex justify-center py-10">
|
||||
{#if carregandoTemplates}
|
||||
<div class="flex flex-col items-center justify-center py-10 gap-4">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
<p class="text-sm text-base-content/60">Carregando templates...</p>
|
||||
</div>
|
||||
{:else if templates.data.length === 0}
|
||||
<div class="text-center py-10 text-base-content/60">
|
||||
Nenhum template disponível
|
||||
</div>
|
||||
{:else}
|
||||
{:else if templates.length > 0}
|
||||
<!-- Mostrar templates se existirem -->
|
||||
<div class="space-y-3 max-h-[600px] overflow-y-auto">
|
||||
{#each templates.data as template}
|
||||
{#each templates as template}
|
||||
<div class="card bg-base-200 compact">
|
||||
<div class="card-body">
|
||||
<div class="flex items-start justify-between">
|
||||
@@ -359,6 +686,30 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Nenhum template disponível - mostrar botão para criar -->
|
||||
<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="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>
|
||||
<p class="font-medium text-base-content mb-2">Nenhum template disponível</p>
|
||||
<p class="text-sm text-base-content/60 mb-4">Clique no botão abaixo para criar os templates padrão do sistema.</p>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
onclick={criarTemplatesPadrao}
|
||||
disabled={criandoTemplates}
|
||||
>
|
||||
{#if criandoTemplates}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
Criando templates...
|
||||
{:else}
|
||||
<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 4v16m8-8H4" />
|
||||
</svg>
|
||||
Criar Templates Padrão
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -373,3 +724,124 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Novo Template -->
|
||||
{#if modalNovoTemplateAberto}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box max-w-2xl">
|
||||
<h3 class="font-bold text-lg mb-4">Criar Novo Template</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Código -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">Código *</span>
|
||||
<span class="label-text-alt">Ex: AVISO_IMPORTANTE</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={codigoTemplate}
|
||||
placeholder="CODIGO_TEMPLATE"
|
||||
class="input input-bordered"
|
||||
maxlength="50"
|
||||
/>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Código único para identificar o template (será convertido para MAIÚSCULAS)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Nome -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">Nome *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={nomeTemplate}
|
||||
placeholder="Nome do Template"
|
||||
class="input input-bordered"
|
||||
maxlength="100"
|
||||
/>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Nome exibido na lista de templates</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Título -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">Título *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={tituloTemplate}
|
||||
placeholder="Título da Mensagem"
|
||||
class="input input-bordered"
|
||||
maxlength="200"
|
||||
/>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Título usado no assunto do email ou cabeçalho da mensagem</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Corpo -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">Corpo da Mensagem *</span>
|
||||
</label>
|
||||
<textarea
|
||||
bind:value={corpoTemplate}
|
||||
placeholder="Digite o conteúdo da mensagem..."
|
||||
class="textarea textarea-bordered h-32"
|
||||
maxlength="2000"
|
||||
></textarea>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Use {'{{'}variavel{'}}'} para variáveis dinâmicas (ex: {'{{'}nome{'}}'}, {'{{'}data{'}}'})</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Variáveis -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">Variáveis (opcional)</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={variaveisTemplate}
|
||||
placeholder="nome, data, valor (separadas por vírgula)"
|
||||
class="input input-bordered"
|
||||
/>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Liste as variáveis que podem ser substituídas no template (separadas por vírgula ou espaço)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button
|
||||
class="btn btn-ghost"
|
||||
onclick={fecharModalNovoTemplate}
|
||||
disabled={criandoNovoTemplate}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
onclick={salvarNovoTemplate}
|
||||
disabled={criandoNovoTemplate}
|
||||
>
|
||||
{#if criandoNovoTemplate}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
Criando...
|
||||
{:else}
|
||||
<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="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Criar Template
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop" onclick={fecharModalNovoTemplate}></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user