feat: update ESLint and TypeScript configurations across frontend and backend; enhance component structure and improve data handling in various modules
This commit is contained in:
@@ -70,7 +70,7 @@
|
||||
processando = true;
|
||||
const resultado = await client.mutation(api.templatesMensagens.excluirTemplate, {
|
||||
templateId,
|
||||
excluidoPorId: currentUser.data._id,
|
||||
excluidoPorId: currentUser.data._id
|
||||
});
|
||||
|
||||
if (resultado.sucesso) {
|
||||
@@ -99,12 +99,12 @@
|
||||
|
||||
<div class="container mx-auto max-w-7xl px-4 py-8">
|
||||
<div
|
||||
class="rounded-2xl bg-base-100/80 shadow-xl border border-base-200/60 p-6 lg:p-8 space-y-6 backdrop-blur"
|
||||
class="bg-base-100/80 border-base-200/60 space-y-6 rounded-2xl border p-6 shadow-xl backdrop-blur lg:p-8"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="bg-gradient-to-br from-info/15 via-primary/10 to-secondary/10 rounded-2xl p-3">
|
||||
<div class="from-info/15 via-primary/10 to-secondary/10 rounded-2xl bg-gradient-to-br p-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-info h-9 w-9"
|
||||
@@ -123,8 +123,10 @@
|
||||
<div>
|
||||
<h1 class="text-base-content text-3xl font-bold">Gerenciar Templates</h1>
|
||||
<p class="text-base-content/60 mt-1 text-sm lg:text-base">
|
||||
Crie, edite e organize templates de <span class="font-semibold">chat</span> (texto puro) e
|
||||
<span class="font-semibold">email HTML padronizado</span> usados em todas as notificações do SGSE.
|
||||
Crie, edite e organize templates de <span class="font-semibold">chat</span> (texto puro)
|
||||
e
|
||||
<span class="font-semibold">email HTML padronizado</span> usados em todas as notificações
|
||||
do SGSE.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,7 +138,12 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
Voltar para Notificações
|
||||
</a>
|
||||
@@ -151,14 +158,18 @@
|
||||
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
|
||||
type="button"
|
||||
class="btn btn-sm btn-circle btn-ghost"
|
||||
onclick={() => (mensagem = null)}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Filtros e Busca -->
|
||||
<div class="card bg-base-100 shadow-sm border border-base-200">
|
||||
<div class="card bg-base-100 border-base-200 border shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="form-control">
|
||||
@@ -188,7 +199,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Lista de Templates -->
|
||||
<div class="card bg-base-100 shadow-sm border border-base-200">
|
||||
<div class="card bg-base-100 border-base-200 border shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h2 class="card-title">Templates ({templatesFiltrados.length})</h2>
|
||||
@@ -200,7 +211,12 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Template
|
||||
</a>
|
||||
@@ -231,7 +247,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">{template.nome}</div>
|
||||
<div class="text-sm text-base-content/60">{template.titulo}</div>
|
||||
<div class="text-base-content/60 text-sm">{template.titulo}</div>
|
||||
</td>
|
||||
<td>
|
||||
{#if template.tipo === 'sistema'}
|
||||
@@ -251,7 +267,7 @@
|
||||
{#if template.variaveis && template.variaveis.length > 0}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each template.variaveis.slice(0, 3) as variavel}
|
||||
<span class="badge badge-sm">{{variavel}}</span>
|
||||
<span class="badge badge-sm">{{ variavel }}</span>
|
||||
{/each}
|
||||
{#if template.variaveis.length > 3}
|
||||
<span class="badge badge-sm">+{template.variaveis.length - 3}</span>
|
||||
@@ -319,4 +335,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,21 +9,18 @@
|
||||
|
||||
const client = useConvexClient();
|
||||
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
|
||||
|
||||
|
||||
const templateIdParam = $derived($page.params.id ?? '');
|
||||
|
||||
|
||||
// Query específica para buscar o template por ID
|
||||
const templateQuery = useQuery(
|
||||
api.templatesMensagens.obterTemplatePorId,
|
||||
() => {
|
||||
if (!templateIdParam) return 'skip';
|
||||
// Validar se o ID tem o formato correto do Convex
|
||||
if (typeof templateIdParam === 'string' && templateIdParam.length > 0) {
|
||||
return { templateId: templateIdParam as Id<'templatesMensagens'> };
|
||||
}
|
||||
return 'skip';
|
||||
const templateQuery = useQuery(api.templatesMensagens.obterTemplatePorId, () => {
|
||||
if (!templateIdParam) return 'skip';
|
||||
// Validar se o ID tem o formato correto do Convex
|
||||
if (typeof templateIdParam === 'string' && templateIdParam.length > 0) {
|
||||
return { templateId: templateIdParam as Id<'templatesMensagens'> };
|
||||
}
|
||||
);
|
||||
return 'skip';
|
||||
});
|
||||
|
||||
// Extrair template da query
|
||||
const template = $derived.by(() => {
|
||||
@@ -31,7 +28,11 @@
|
||||
// useQuery retorna os dados diretamente
|
||||
if (templateQuery && typeof templateQuery === 'object') {
|
||||
// Se tem propriedade data, usar ela
|
||||
if ('data' in templateQuery && templateQuery.data !== undefined && templateQuery.data !== null) {
|
||||
if (
|
||||
'data' in templateQuery &&
|
||||
templateQuery.data !== undefined &&
|
||||
templateQuery.data !== null
|
||||
) {
|
||||
return templateQuery.data as Doc<'templatesMensagens'> | null;
|
||||
}
|
||||
// Caso contrário, assumir que é o próprio template
|
||||
@@ -134,11 +135,11 @@
|
||||
|
||||
<div class="container mx-auto max-w-4xl px-4 py-8">
|
||||
<div
|
||||
class="rounded-2xl bg-base-100/80 shadow-xl border border-base-200/60 p-6 lg:p-8 space-y-6 backdrop-blur"
|
||||
class="bg-base-100/80 border-base-200/60 space-y-6 rounded-2xl border p-6 shadow-xl backdrop-blur lg:p-8"
|
||||
>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="bg-gradient-to-br from-info/15 via-primary/10 to-secondary/10 rounded-2xl p-3">
|
||||
<div class="from-info/15 via-primary/10 to-secondary/10 rounded-2xl bg-gradient-to-br p-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-info h-9 w-9"
|
||||
@@ -158,7 +159,8 @@
|
||||
<h1 class="text-base-content text-3xl font-bold">Editar Template</h1>
|
||||
<p class="text-base-content/60 mt-1 text-sm lg:text-base">
|
||||
Ajuste o texto base usado em <span class="font-semibold">chat</span> e na versão HTML de
|
||||
<span class="font-semibold">email</span>. Templates de sistema podem ter restrições de edição.
|
||||
<span class="font-semibold">email</span>. Templates de sistema podem ter restrições de
|
||||
edição.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -172,7 +174,11 @@
|
||||
</div>
|
||||
{:else if erroTemplate}
|
||||
<div class="alert alert-error">
|
||||
<span>Erro ao carregar template: {typeof erroTemplate === 'string' ? erroTemplate : erroTemplate?.message || 'Erro desconhecido'}</span>
|
||||
<span
|
||||
>Erro ao carregar template: {typeof erroTemplate === 'string'
|
||||
? erroTemplate
|
||||
: erroTemplate?.message || 'Erro desconhecido'}</span
|
||||
>
|
||||
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-sm btn-outline">
|
||||
Voltar para Templates
|
||||
</a>
|
||||
@@ -180,7 +186,7 @@
|
||||
{:else if !template}
|
||||
<div class="alert alert-error">
|
||||
<span>Template não encontrado. Verifique se o ID está correto.</span>
|
||||
<div class="text-xs mt-2 opacity-70">ID: {templateIdParam}</div>
|
||||
<div class="mt-2 text-xs opacity-70">ID: {templateIdParam}</div>
|
||||
<a href={resolve('/ti/notificacoes/templates')} class="btn btn-sm btn-outline mt-2">
|
||||
Voltar para Templates
|
||||
</a>
|
||||
@@ -206,117 +212,117 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="card bg-base-100 shadow-sm border border-base-200">
|
||||
<div class="card bg-base-100 border-base-200 border shadow-sm">
|
||||
<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 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="titulo">
|
||||
<span class="label-text font-medium">Título *</span>
|
||||
<label class="label" for="categoria">
|
||||
<span class="label-text font-medium">Categoria</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>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">
|
||||
<span class="font-semibold">Chat:</span> usa o texto puro do corpo. <span
|
||||
class="font-semibold">Email:</span
|
||||
> usa uma versão HTML profissional gerada automaticamente com cabeçalho e assinatura SGSE.
|
||||
</span>
|
||||
</label>
|
||||
</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 em TEXTO. Você pode usar {{variavel}} para valores dinâmicos."
|
||||
></textarea>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">
|
||||
Este texto será usado diretamente nas mensagens de
|
||||
<span class="font-semibold">chat</span>. Para
|
||||
<span class="font-semibold">email</span>, o sistema gera automaticamente um layout HTML
|
||||
padronizado com logo e assinatura.
|
||||
</span>
|
||||
</label>
|
||||
</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">
|
||||
<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>
|
||||
<label class="label">
|
||||
<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 class="font-semibold">Chat:</span> usa o texto puro do corpo.
|
||||
<span class="font-semibold">Email:</span> usa uma versão HTML profissional gerada automaticamente
|
||||
com cabeçalho e assinatura SGSE.
|
||||
</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 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 em TEXTO. Você pode usar {{variavel}} para valores dinâmicos."
|
||||
></textarea>
|
||||
<label class="label">
|
||||
<span class="label-text-alt">
|
||||
Este texto será usado diretamente nas mensagens de
|
||||
<span class="font-semibold">chat</span>. Para
|
||||
<span class="font-semibold">email</span>, o sistema gera automaticamente um layout
|
||||
HTML padronizado com logo e assinatura.
|
||||
</span>
|
||||
</label>
|
||||
</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}
|
||||
|
||||
Reference in New Issue
Block a user