Merge remote-tracking branch 'origin' into feat-pedidos

This commit is contained in:
2025-12-11 10:08:12 -03:00
194 changed files with 30374 additions and 10247 deletions

View File

@@ -0,0 +1,65 @@
/**
* Utilitários para manipulação de datas no backend
* Resolve problemas de timezone ao trabalhar com datas no formato YYYY-MM-DD
*/
/**
* Converte uma string de data no formato YYYY-MM-DD para um objeto Date local
* No ambiente Convex, as datas são tratadas como UTC, então precisamos garantir
* que a data seja interpretada corretamente.
*
* @param dateString - String no formato YYYY-MM-DD
* @returns Date objeto representando a data
*
* @example
* parseLocalDate('2024-01-15') // Retorna Date para 15/01/2024
*/
export function parseLocalDate(dateString: string): Date {
if (!dateString || typeof dateString !== 'string') {
throw new Error('dateString deve ser uma string válida');
}
// Validar formato YYYY-MM-DD
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(dateString)) {
throw new Error('dateString deve estar no formato YYYY-MM-DD');
}
// Extrair ano, mês e dia
const [year, month, day] = dateString.split('-').map(Number);
// No Convex, criar a data usando UTC para evitar problemas de timezone
// Usamos UTC para garantir consistência, mas mantemos a data correta
const date = new Date(Date.UTC(year, month - 1, day, 0, 0, 0, 0));
// Validar se a data é válida
if (isNaN(date.getTime())) {
throw new Error(`Data inválida: ${dateString}`);
}
return date;
}
/**
* Formata uma data para o formato brasileiro (DD/MM/YYYY)
*
* @param date - Date objeto ou string no formato YYYY-MM-DD
* @returns String formatada no formato DD/MM/YYYY
*/
export function formatarDataBR(date: Date | string): string {
let dateObj: Date;
if (typeof date === 'string') {
dateObj = parseLocalDate(date);
} else {
dateObj = date;
}
// Usar UTC para garantir consistência
const day = dateObj.getUTCDate().toString().padStart(2, '0');
const month = (dateObj.getUTCMonth() + 1).toString().padStart(2, '0');
const year = dateObj.getUTCFullYear();
return `${day}/${month}/${year}`;
}

View File

@@ -8,7 +8,7 @@
*/
function getBaseUrl(): string {
// Em produção, usar variável de ambiente
const url = process.env.FRONTEND_URL || 'http://localhost:5173';
const url = process.env.SITE_URL || 'http://localhost:5173';
// Garantir que tenha protocolo
if (!url.match(/^https?:\/\//i)) {
return `http://${url}`;
@@ -18,17 +18,20 @@ function getBaseUrl(): string {
/**
* 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: #1a3a52; padding: 20px 0;">
<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="${baseUrl}/logo_governo_PE.png" alt="Governo de Pernambuco" style="max-width: 200px; height: auto;" />
<img src="${logoUrl}" alt="Governo de Pernambuco" style="max-width: 200px; height: auto; display: block; margin: 0 auto;" />
</td>
</tr>
</table>
@@ -46,13 +49,13 @@ function generateFooter(): string {
const currentYear = new Date().getFullYear();
return `
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f5f5f5; border-top: 3px solid #1a3a52; margin-top: 30px;">
<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: #1a3a52; font-size: 16px;">
<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;">
@@ -66,8 +69,8 @@ function generateFooter(): string {
© ${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: #1a3a52; text-decoration: none;">Acessar Sistema</a> |
<a href="${baseUrl}/ti/notificacoes" style="color: #1a3a52; text-decoration: none;">Central de Notificações</a>
<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>
@@ -178,7 +181,7 @@ export function textToHTML(texto: string): string {
const linkRegex = /(https?:\/\/[^\s]+)/g;
const linhaComLinks = linhaTrim.replace(
linkRegex,
'<a href="$1" style="color: #1a3a52; text-decoration: underline;">$1</a>'
'<a href="$1" style="color: #0052A5; text-decoration: underline;">$1</a>'
);
return `<p style="margin: 0 0 15px 0;">${linhaComLinks}</p>`;
})