Files
sgse-app/packages/backend/convex/utils/emailTemplateWrapper.ts

190 lines
6.7 KiB
TypeScript

/**
* Wrapper HTML para templates de email do SGSE
* Aplica estilo governamental profissional com logo e assinatura padronizada
*/
/**
* Obtém a URL base do sistema para uso em links de email
*/
function getBaseUrl(): string {
// Em produção, usar variável de ambiente
const url = process.env.FRONTEND_URL || 'http://localhost:5173';
// Garantir que tenha protocolo
if (!url.match(/^https?:\/\//i)) {
return `http://${url}`;
}
return url;
}
/**
* Gera o HTML do header com logo do Governo de PE
* Usa URL estática do SvelteKit para servir a logo
*/
function generateHeader(): string {
const baseUrl = getBaseUrl();
// URL da logo na pasta static do SvelteKit
const logoUrl = `${baseUrl}/logo_governo_PE.png`;
return `
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #0052A5; padding: 20px 0;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="text-align: center; padding: 20px 0;">
<img src="${logoUrl}" alt="Governo de Pernambuco" style="max-width: 200px; height: auto; display: block; margin: 0 auto;" />
</td>
</tr>
</table>
</td>
</tr>
</table>
`;
}
/**
* Gera o HTML do footer com assinatura SGSE
*/
function generateFooter(): string {
const baseUrl = getBaseUrl();
const currentYear = new Date().getFullYear();
return `
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f5; border-top: 3px solid #0052A5; margin-top: 30px;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" border="0" style="padding: 30px 20px;">
<tr>
<td style="text-align: center; font-family: Arial, sans-serif; color: #333333; font-size: 14px; line-height: 1.6;">
<p style="margin: 0 0 10px 0; font-weight: bold; color: #0052A5; font-size: 16px;">
SGSE - Sistema de Gerenciamento de Secretaria
</p>
<p style="margin: 0 0 10px 0; color: #666666;">
Secretaria de Esportes do Estado de Pernambuco
</p>
<p style="margin: 0 0 15px 0; color: #666666; font-size: 12px;">
Este é um email automático do sistema. Por favor, não responda diretamente a este email.
</p>
<hr style="border: none; border-top: 1px solid #dddddd; margin: 20px 0;" />
<p style="margin: 0; color: #999999; font-size: 11px;">
© ${currentYear} Secretaria de Esportes - Governo de Pernambuco. Todos os direitos reservados.
</p>
<p style="margin: 5px 0 0 0; color: #999999; font-size: 11px;">
<a href="${baseUrl}" style="color: #0052A5; text-decoration: none;">Acessar Sistema</a> |
<a href="${baseUrl}/ti/notificacoes" style="color: #0052A5; text-decoration: none;">Central de Notificações</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
`;
}
/**
* Envolve o conteúdo HTML do email com template profissional governamental
* @param conteudoHTML - Conteúdo HTML do corpo do email
* @param titulo - Título do email (usado no meta)
* @returns HTML completo do email pronto para envio
*/
export function wrapEmailHTML(conteudoHTML: string, titulo?: string): string {
// Se o conteúdo já estiver dentro de um wrapper completo, retornar como está
if (conteudoHTML.includes('<!DOCTYPE html>') || conteudoHTML.includes('<html')) {
return conteudoHTML;
}
// Garantir que o conteúdo tenha estrutura básica
let conteudoProcessado = conteudoHTML.trim();
// Se não tiver tags HTML básicas, envolver em parágrafo
if (!conteudoProcessado.match(/^<[a-z]/i)) {
conteudoProcessado = `<p style="margin: 0 0 15px 0;">${conteudoProcessado}</p>`;
}
const header = generateHeader();
const footer = generateFooter();
const emailTitle = titulo || 'Notificação do SGSE';
return `
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>${emailTitle}</title>
<!--[if mso]>
<style type="text/css">
body, table, td {font-family: Arial, sans-serif !important;}
</style>
<![endif]-->
</head>
<body style="margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;">
<!-- Wrapper principal -->
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f5; padding: 20px 0;">
<tr>
<td align="center">
<!-- Container do conteúdo -->
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color: #ffffff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden;">
<!-- Header -->
${header}
<!-- Corpo do email -->
<tr>
<td style="padding: 30px 20px;">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="font-family: Arial, sans-serif; color: #333333; font-size: 14px; line-height: 1.6;">
${conteudoProcessado}
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td>
${footer}
</td>
</tr>
</table>
<!-- Espaçamento inferior -->
<table width="600" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 20px 0; text-align: center; font-family: Arial, sans-serif; color: #999999; font-size: 11px;">
<p style="margin: 0;">Se você não solicitou este email, pode ignorá-lo com segurança.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`.trim();
}
/**
* Converte texto plano em HTML básico
* @param texto - Texto plano
* @returns HTML formatado
*/
export function textToHTML(texto: string): string {
return texto
.split('\n')
.map((linha) => {
const linhaTrim = linha.trim();
if (!linhaTrim) return '<br />';
// Detectar links
const linkRegex = /(https?:\/\/[^\s]+)/g;
const linhaComLinks = linhaTrim.replace(
linkRegex,
'<a href="$1" style="color: #0052A5; text-decoration: underline;">$1</a>'
);
return `<p style="margin: 0 0 15px 0;">${linhaComLinks}</p>`;
})
.join('');
}