Refinament 1 #38

Merged
killer-cf merged 2 commits from refinament-1 into master 2025-11-22 13:26:25 +00:00
3 changed files with 532 additions and 534 deletions
Showing only changes of commit b8a2e67f3a - Show all commits

View File

@@ -0,0 +1,28 @@
---
trigger: glob
globs: **/*.svelte.ts,**/*.svelte
---
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -1,51 +1,45 @@
<script lang="ts">
import { useQuery, useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { useQuery, useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import type { FunctionReference } from 'convex/server';
const client = useConvexClient();
const currentUser = useQuery(api.auth.getCurrentUser, {});
const configAtual = useQuery(api.configuracaoEmail.obterConfigEmail, {});
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>);
const configAtual = useQuery(api.configuracaoEmail.obterConfigEmail);
let servidor = $state("");
let servidor = $state('');
let porta = $state(587);
let usuario = $state("");
let senha = $state("");
let emailRemetente = $state("");
let nomeRemetente = $state("");
let usuario = $state('');
let senha = $state('');
let emailRemetente = $state('');
let nomeRemetente = $state('');
let usarSSL = $state(false);
let usarTLS = $state(true);
let processando = $state(false);
let testando = $state(false);
let mensagem = $state<{ tipo: "success" | "error"; texto: string } | null>(
null,
);
let mensagem = $state<{ tipo: 'success' | 'error'; texto: string } | null>(null);
function mostrarMensagem(tipo: "success" | "error", texto: string) {
function mostrarMensagem(tipo: 'success' | 'error', texto: string) {
mensagem = { tipo, texto };
setTimeout(() => {
mensagem = null;
}, 5000);
}
// Carregar config existente
let dataLoaded = $state(false);
// Carregar config existente apenas uma vez
$effect(() => {
if (configAtual?.data) {
servidor = configAtual.data.servidor || "";
if (configAtual?.data && !dataLoaded) {
servidor = configAtual.data.servidor || '';
porta = configAtual.data.porta || 587;
usuario = configAtual.data.usuario || "";
emailRemetente = configAtual.data.emailRemetente || "";
nomeRemetente = configAtual.data.nomeRemetente || "";
usuario = configAtual.data.usuario || '';
emailRemetente = configAtual.data.emailRemetente || '';
nomeRemetente = configAtual.data.nomeRemetente || '';
usarSSL = configAtual.data.usarSSL || false;
usarTLS = configAtual.data.usarTLS || true;
}
});
// Tornar SSL e TLS mutuamente exclusivos
$effect(() => {
if (usarSSL && usarTLS) {
// Se ambos estão marcados, priorizar TLS por padrão
usarSSL = false;
dataLoaded = true;
}
});
@@ -72,62 +66,61 @@
!emailRemetente?.trim() ||
!nomeRemetente?.trim()
) {
mostrarMensagem("error", "Preencha todos os campos obrigatórios");
mostrarMensagem('error', 'Preencha todos os campos obrigatórios');
return;
}
// Validação de porta (1-65535)
const portaNum = Number(porta);
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
mostrarMensagem('error', 'Porta deve ser um número entre 1 e 65535');
return;
}
// Validação de formato de email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(emailRemetente.trim())) {
mostrarMensagem("error", "Email remetente inválido");
mostrarMensagem('error', 'Email remetente inválido');
return;
}
// Validação de senha: obrigatória apenas se não houver configuração existente
const temConfigExistente = configAtual?.data?.ativo;
if (!temConfigExistente && !senha) {
mostrarMensagem("error", "Senha é obrigatória para nova configuração");
mostrarMensagem('error', 'Senha é obrigatória para nova configuração');
return;
}
if (!currentUser?.data) {
mostrarMensagem("error", "Usuário não autenticado");
mostrarMensagem('error', 'Usuário não autenticado');
return;
}
processando = true;
try {
const resultado = await client.mutation(
api.configuracaoEmail.salvarConfigEmail,
{
const resultado = await client.mutation(api.configuracaoEmail.salvarConfigEmail, {
servidor: servidor.trim(),
porta: portaNum,
usuario: usuario.trim(),
senha: senha || "", // Senha vazia será tratada no backend
senha: senha || '', // Senha vazia será tratada no backend
emailRemetente: emailRemetente.trim(),
nomeRemetente: nomeRemetente.trim(),
usarSSL,
usarTLS,
configuradoPorId: currentUser.data._id as Id<"usuarios">,
},
);
configuradoPorId: currentUser.data._id as Id<'usuarios'>
});
if (resultado.sucesso) {
mostrarMensagem("success", "Configuração salva com sucesso!");
senha = ""; // Limpar senha
mostrarMensagem('success', 'Configuração salva com sucesso!');
senha = ''; // Limpar senha
} else {
mostrarMensagem("error", resultado.erro);
mostrarMensagem('error', resultado.erro);
}
} catch (error) {
if (error instanceof Error) {
console.error('Erro ao salvar configuração:', error);
mostrarMensagem('error', error.message || 'Erro ao salvar configuração');
}
} catch (error: any) {
console.error("Erro ao salvar configuração:", error);
mostrarMensagem("error", error.message || "Erro ao salvar configuração");
} finally {
processando = false;
}
@@ -135,66 +128,56 @@
async function testarConexao() {
if (!servidor?.trim() || !porta || !usuario?.trim() || !senha) {
mostrarMensagem("error", "Preencha os dados de conexão antes de testar");
mostrarMensagem('error', 'Preencha os dados de conexão antes de testar');
return;
}
// Validação de porta
const portaNum = Number(porta);
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
mostrarMensagem('error', 'Porta deve ser um número entre 1 e 65535');
return;
}
testando = true;
try {
const resultado = await client.action(
api.configuracaoEmail.testarConexaoSMTP,
{
const resultado = await client.action(api.configuracaoEmail.testarConexaoSMTP, {
servidor: servidor.trim(),
porta: portaNum,
usuario: usuario.trim(),
senha: senha,
usarSSL,
usarTLS,
},
);
usarTLS
});
if (resultado.sucesso) {
mostrarMensagem(
"success",
"Conexão testada com sucesso! Servidor SMTP está respondendo.",
);
mostrarMensagem('success', 'Conexão testada com sucesso! Servidor SMTP está respondendo.');
} else {
mostrarMensagem("error", `Erro ao testar conexão: ${resultado.erro}`);
mostrarMensagem('error', `Erro ao testar conexão: ${resultado.erro}`);
}
} catch (error) {
if (error instanceof Error) {
console.error('Erro ao testar conexão:', error);
mostrarMensagem('error', error.message || 'Erro ao conectar com o servidor SMTP');
}
} catch (error: any) {
console.error("Erro ao testar conexão:", error);
mostrarMensagem(
"error",
error.message || "Erro ao conectar com o servidor SMTP",
);
} finally {
testando = false;
}
}
const statusConfig = $derived(
configAtual?.data?.ativo ? "Configurado" : "Não configurado",
);
const statusConfig = $derived(configAtual?.data?.ativo ? 'Configurado' : 'Não configurado');
const isLoading = $derived(configAtual === undefined);
const hasError = $derived(configAtual === null && !isLoading);
</script>
<div class="container mx-auto px-4 py-6 max-w-4xl">
<div class="container mx-auto max-w-4xl px-4 py-6">
<!-- Header -->
<div class="flex items-center justify-between mb-6">
<div class="mb-6 flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="p-3 bg-secondary/10 rounded-xl">
<div class="bg-secondary/10 rounded-xl p-3">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 text-secondary"
class="text-secondary h-8 w-8"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -208,9 +191,7 @@
</svg>
</div>
<div>
<h1 class="text-3xl font-bold text-base-content">
Configurações de Email (SMTP)
</h1>
<h1 class="text-base-content text-3xl font-bold">Configurações de Email (SMTP)</h1>
<p class="text-base-content/60 mt-1">
Configurar servidor de email para envio de notificações
</p>
@@ -222,16 +203,16 @@
{#if mensagem}
<div
class="alert mb-6"
class:alert-success={mensagem.tipo === "success"}
class:alert-error={mensagem.tipo === "error"}
class:alert-success={mensagem.tipo === 'success'}
class:alert-error={mensagem.tipo === 'error'}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
class="h-6 w-6 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
{#if mensagem.tipo === "success"}
{#if mensagem.tipo === 'success'}
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -261,16 +242,12 @@
<!-- Status -->
{#if !isLoading}
<div
class="alert {configAtual?.data?.ativo
? 'alert-success'
: 'alert-warning'} mb-6"
>
<div class="alert {configAtual?.data?.ativo ? 'alert-success' : 'alert-warning'} mb-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"
class="h-6 w-6 shrink-0 stroke-current"
>
{#if configAtual?.data?.ativo}
<path
@@ -292,9 +269,7 @@
<strong>Status:</strong>
{statusConfig}
{#if configAtual?.data?.testadoEm}
- Última conexão testada em {new Date(
configAtual.data.testadoEm,
).toLocaleString("pt-BR")}
- Última conexão testada em {new Date(configAtual.data.testadoEm).toLocaleString('pt-BR')}
{/if}
</span>
</div>
@@ -306,7 +281,7 @@
<div class="card-body">
<h2 class="card-title mb-4">Dados do Servidor SMTP</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<!-- Servidor -->
<div class="form-control md:col-span-1">
<label class="label" for="smtp-servidor">
@@ -320,9 +295,7 @@
class="input input-bordered"
/>
<div class="label">
<span class="label-text-alt"
>Ex: smtp.gmail.com, smtp.office365.com</span
>
<span class="label-text-alt">Ex: smtp.gmail.com, smtp.office365.com</span>
</div>
</div>
@@ -339,8 +312,7 @@
class="input input-bordered"
/>
<div class="label">
<span class="label-text-alt">Comum: 587 (TLS), 465 (SSL), 25</span
>
<span class="label-text-alt">Comum: 587 (TLS), 465 (SSL), 25</span>
</div>
</div>
@@ -412,7 +384,7 @@
<!-- Opções de Segurança -->
<div class="divider"></div>
<h3 class="font-bold mb-2">Configurações de Segurança</h3>
<h3 class="mb-2 font-bold">Configurações de Segurança</h3>
<div class="flex flex-wrap gap-6">
<div class="form-control">
@@ -441,7 +413,7 @@
</div>
<!-- Ações -->
<div class="card-actions justify-end mt-6 gap-3">
<div class="card-actions mt-6 justify-end gap-3">
<button
class="btn btn-outline btn-info"
onclick={testarConexao}
@@ -499,12 +471,12 @@
{/if}
<!-- Exemplos Comuns -->
<div class="card bg-base-100 shadow-xl mt-6">
<div class="card bg-base-100 mt-6 shadow-xl">
<div class="card-body">
<h2 class="card-title mb-4">Exemplos de Configuração</h2>
<div class="overflow-x-auto">
<table class="table table-sm">
<table class="table-sm table">
<thead>
<tr>
<th>Provedor</th>
@@ -550,7 +522,7 @@
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current shrink-0 w-6 h-6"
class="h-6 w-6 shrink-0 stroke-current"
>
<path
stroke-linecap="round"
@@ -561,13 +533,11 @@
</svg>
<div>
<p>
<strong>Dica de Segurança:</strong> Para Gmail e outros provedores, você
pode precisar gerar uma "senha de app" específica em vez de usar sua senha
principal.
<strong>Dica de Segurança:</strong> Para Gmail e outros provedores, você pode precisar gerar
uma "senha de app" específica em vez de usar sua senha principal.
</p>
<p class="text-sm mt-1">
Gmail: Conta Google → Segurança → Verificação em duas etapas → Senhas de
app
<p class="mt-1 text-sm">
Gmail: Conta Google → Segurança → Verificação em duas etapas → Senhas de app
</p>
</div>
</div>