PSA/server/migrations/20251027091000_add_spanish_notification_templates.cjs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

1086 lines
49 KiB
JavaScript

/**
* Add Spanish translations for client-facing email templates
*
* Translates authentication, ticketing, and billing email templates to Spanish
* for client portal users.
*/
exports.up = async function(knex) {
console.log('Adding Spanish email templates...');
// Get notification subtypes
const subtypes = await knex('notification_subtypes')
.select('id', 'name')
.whereIn('name', [
'email-verification',
'password-reset',
'portal-invitation',
'tenant-recovery',
'no-account-found',
'Ticket Assigned',
'Ticket Created',
'Ticket Updated',
'Ticket Closed',
'Ticket Comment Added',
'Invoice Generated',
'Payment Received',
'Payment Overdue'
]);
const getSubtypeId = (name) => {
const subtype = subtypes.find(s => s.name === name);
if (!subtype) {
throw new Error(`Notification subtype '${name}' not found`);
}
return subtype.id;
};
// Insert Spanish templates
await knex('system_email_templates').insert([
// Authentication templates
// NOTE: email-verification template is managed in migration 20251029100000
{
name: 'password-reset',
language_code: 'es',
subject: 'Solicitud de Restablecimiento de Contraseña',
notification_subtype_id: getSubtypeId('password-reset'),
html_content: `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solicitud de Restablecimiento de Contraseña</title>
<style>
body {
font-family: Inter, system-ui, sans-serif;
line-height: 1.6;
color: #0f172a;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8fafc;
}
.header {
background: linear-gradient(135deg, #8a4dea 0%, #7c3aed 100%);
color: white;
padding: 32px 24px;
border-radius: 12px 12px 0 0;
text-align: center;
}
.header h1 {
font-family: Poppins, system-ui, sans-serif;
font-weight: 700;
font-size: 28px;
margin: 0 0 8px 0;
color: white;
}
.header p {
margin: 0;
opacity: 1;
font-size: 16px;
color: rgba(255, 255, 255, 0.95);
}
.content {
background: #ffffff;
padding: 32px;
border: 1px solid #e2e8f0;
border-top: none;
border-bottom: none;
}
.footer {
background: #1e293b;
color: #cbd5e1;
padding: 24px;
border-radius: 0 0 12px 12px;
text-align: center;
font-size: 14px;
line-height: 1.6;
}
.footer p {
margin: 6px 0;
color: #cbd5e1;
}
.footer p:last-child {
color: #94a3b8;
font-size: 13px;
margin-top: 16px;
}
.security-box {
background: #faf8ff;
padding: 24px;
border-radius: 8px;
border: 1px solid #e9e5f5;
border-left: 4px solid #8a4dea;
margin: 24px 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.security-box h3 {
color: #0f172a;
margin: 0 0 16px 0;
font-size: 18px;
font-weight: 600;
}
.security-box p {
margin: 8px 0;
color: #334155;
}
.action-button {
display: inline-block;
background: #8a4dea;
color: #ffffff !important;
padding: 14px 32px;
text-decoration: none;
border-radius: 8px;
font-weight: 600;
margin: 24px 0;
font-family: Poppins, system-ui, sans-serif;
font-size: 16px;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.action-button:hover {
background: #7c3aed;
color: #ffffff !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.warning {
background: #fffbeb;
border: 1px solid #f59e0b;
border-radius: 8px;
padding: 20px;
margin: 24px 0;
}
.warning h4 {
color: #92400e;
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
}
.warning ul {
margin: 0;
padding-left: 20px;
color: #92400e;
}
.warning li {
margin: 4px 0;
}
h2 {
color: #0f172a;
font-family: Poppins, system-ui, sans-serif;
font-size: 24px;
font-weight: 600;
margin: 0 0 16px 0;
}
p {
color: #334155;
margin: 0 0 16px 0;
}
a {
color: #8a4dea;
text-decoration: underline;
}
a:hover {
color: #7c3aed;
}
.code {
font-family: 'Courier New', monospace;
background: #e2e8f0;
padding: 4px 8px;
border-radius: 4px;
color: #0f172a;
font-size: 14px;
font-weight: 600;
}
.divider {
height: 1px;
background: #e2e8f0;
margin: 32px 0;
}
.link-text {
word-break: break-all;
font-size: 14px;
color: #64748b;
background: #f8fafc;
padding: 12px;
border-radius: 6px;
border: 1px solid #e2e8f0;
margin: 12px 0;
}
.help-section {
background: #f8fafc;
border-radius: 8px;
padding: 20px;
margin: 24px 0;
border: 1px solid #e2e8f0;
}
.help-section h4 {
color: #0f172a;
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
}
.help-section p {
margin: 4px 0;
color: #334155;
font-size: 14px;
}
</style>
</head>
<body>
<div class="header">
<h1>Solicitud de Restablecimiento de Contraseña</h1>
<p>Recuperación segura de contraseña para tu cuenta</p>
</div>
<div class="content">
<h2>Hola {{userName}},</h2>
<p>Recibimos una solicitud para restablecer la contraseña de tu cuenta asociada con <strong>{{email}}</strong>.</p>
<div class="security-box">
<h3>🔐 Verificación de Seguridad de la Cuenta</h3>
<p><strong>Solicitado:</strong> Hace un momento</p>
<p><strong>Correo de la cuenta:</strong> {{email}}</p>
<p><strong>Válido por:</strong> {{expirationTime}}</p>
</div>
<p>Para crear una nueva contraseña para tu cuenta, haz clic en el botón a continuación:</p>
<div style="text-align: center;">
<a href="{{resetLink}}" class="action-button">Restablecer Tu Contraseña</a>
</div>
<p style="text-align: center; color: #64748b; font-size: 14px;">
O copia y pega este enlace en tu navegador:
</p>
<div class="link-text">{{resetLink}}</div>
<div class="warning">
<h4>⚠️ Información de Seguridad Importante</h4>
<ul>
<li>Este enlace de restablecimiento expirará en <strong>{{expirationTime}}</strong></li>
<li>Por razones de seguridad, este enlace solo se puede usar <strong>una vez</strong></li>
<li>Si no solicitaste este restablecimiento, ignora este correo</li>
<li>Tu contraseña no cambiará hasta que crees una nueva</li>
</ul>
</div>
<h3>¿Qué Sigue?</h3>
<ol>
<li>Haz clic en el botón de restablecimiento arriba o usa el enlace proporcionado</li>
<li>Crea una contraseña fuerte y única para tu cuenta</li>
<li>Iniciarás sesión automáticamente después de restablecer</li>
<li>Todas las sesiones existentes se terminarán por seguridad</li>
<li>Considera habilitar la autenticación de dos factores para mayor protección</li>
</ol>
<div class="divider"></div>
<div class="help-section">
<h4>¿Necesitas Ayuda?</h4>
<p>Si tienes problemas para restablecer tu contraseña, nuestro equipo de soporte está aquí para ayudarte.</p>
<p style="margin-top: 12px;"><strong>Contactar Soporte:</strong> {{supportEmail}}</p>
</div>
</div>
<div class="footer">
<p>Este es un correo de seguridad automático enviado a {{email}}.</p>
<p>Por tu seguridad, nunca incluimos contraseñas en los correos.</p>
<p>© {{currentYear}} {{clientName}}. Todos los derechos reservados.</p>
</div>
</body>
</html>
`,
text_content: `Solicitud de Restablecimiento de Contraseña
Hola {{userName}},
Recibimos una solicitud para restablecer la contraseña de tu cuenta asociada con {{email}}.
VERIFICACIÓN DE SEGURIDAD DE LA CUENTA
- Solicitado: Hace un momento
- Correo de la cuenta: {{email}}
- Válido por: {{expirationTime}}
Para crear una nueva contraseña, visita el siguiente enlace:
{{resetLink}}
INFORMACIÓN DE SEGURIDAD IMPORTANTE:
- Este enlace expirará en {{expirationTime}}
- Solo se puede usar una vez
- Si no solicitaste esto, ignora este correo
- Tu contraseña no cambiará hasta que crees una nueva
QUÉ SIGUE:
1. Usa el enlace proporcionado arriba
2. Crea una contraseña fuerte y única
3. Iniciarás sesión automáticamente
4. Todas las sesiones existentes se terminarán
5. Considera habilitar autenticación de dos factores
¿Necesitas ayuda?
Contactar Soporte: {{supportEmail}}
---
Este es un correo de seguridad automático enviado a {{email}}.
© {{currentYear}} {{clientName}}. Todos los derechos reservados.`
},
// NOTE: portal-invitation template is managed in migration 20251029100000
{
name: 'tenant-recovery',
language_code: 'es',
subject: '{{platformName}} - Tus enlaces de inicio de sesión',
notification_subtype_id: getSubtypeId('tenant-recovery'),
html_content: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; margin: 0;">
{{platformName}}
</h2>
<div style="padding: 40px 30px;">
<p style="color: #111827; font-size: 16px; margin-bottom: 20px;">Hola,</p>
<p style="color: #111827; font-size: 16px; margin-bottom: 20px;">
Solicitaste acceso a tu portal{{#if isMultiple}}es{{/if}} de cliente{{#if isMultiple}}s{{/if}}.
{{#if isMultiple}}Encontramos {{tenantCount}} organizaciones asociadas con tu dirección de correo electrónico.{{else}}Aquí está tu enlace de inicio de sesión:{{/if}}
</p>
<table width="100%" cellpadding="0" cellspacing="0" style="border: 1px solid #e5e7eb; border-radius: 6px; overflow: hidden; margin: 25px 0;">
{{tenantLinksHtml}}
</table>
<div style="background-color: #f3f4f6; border-radius: 6px; padding: 20px; margin: 25px 0;">
<p style="color: #4b5563; font-size: 14px; margin: 0;">
<strong>Nota de seguridad:</strong> Si no solicitaste estos enlaces de inicio de sesión, puedes ignorar este correo de forma segura. Tu cuenta permanece segura.
</p>
</div>
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 30px;">
<p style="color: #6b7280; font-size: 14px; margin-bottom: 10px;">
Si tienes preguntas o necesitas asistencia, por favor contacta al equipo de soporte de tu organización.
</p>
</div>
</div>
<div style="background-color: #f9fafb; padding: 20px; text-align: center; border-top: 1px solid #e5e7eb;">
<p style="color: #9ca3af; font-size: 12px; margin: 5px 0;">
© {{currentYear}} {{platformName}}. Todos los derechos reservados.
</p>
<p style="color: #9ca3af; font-size: 11px; margin: 5px 0;">
Este es un mensaje automático. Por favor no respondas a este correo.
</p>
</div>
</div>
`,
text_content: `{{platformName}} - Tus enlaces de inicio de sesión
Hola,
Solicitaste acceso a tu portal{{#if isMultiple}}es{{/if}} de cliente{{#if isMultiple}}s{{/if}}.
{{#if isMultiple}}Encontramos {{tenantCount}} organizaciones asociadas con tu dirección de correo electrónico.{{else}}Aquí está tu enlace de inicio de sesión:{{/if}}
Tus enlaces de inicio de sesión:
{{tenantLinksText}}
Nota de seguridad: Si no solicitaste estos enlaces de inicio de sesión, puedes ignorar este correo de forma segura.
Si tienes preguntas o necesitas asistencia, por favor contacta al equipo de soporte de tu organización.
---
© {{currentYear}} {{platformName}}. Todos los derechos reservados.
Este es un mensaje automático. Por favor no respondas a este correo.`
},
{
name: 'no-account-found',
language_code: 'es',
subject: '{{platformName}} - Solicitud de acceso',
notification_subtype_id: getSubtypeId('no-account-found'),
html_content: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; margin: 0;">
{{platformName}}
</h2>
<div style="padding: 40px 30px;">
<p style="color: #111827; font-size: 16px; margin-bottom: 20px;">Hola,</p>
<p style="color: #111827; font-size: 16px; margin-bottom: 20px;">
Recibimos una solicitud para acceder al portal del cliente usando esta dirección de correo electrónico.
</p>
<p style="color: #111827; font-size: 16px; margin-bottom: 15px;">
Si tienes una cuenta con nosotros, deberías haber recibido un correo separado con tus enlaces de inicio de sesión.
</p>
<p style="color: #111827; font-size: 16px; margin-bottom: 10px;">
Si no recibiste un correo de inicio de sesión, puede significar:
</p>
<ul style="color: #111827; font-size: 16px; margin-bottom: 20px; padding-left: 20px;">
<li>Esta dirección de correo electrónico no está asociada con ninguna cuenta del portal del cliente</li>
<li>Tu cuenta puede estar inactiva</li>
<li>El correo puede haber sido filtrado a tu carpeta de spam</li>
</ul>
<div style="background-color: #eff6ff; border-left: 4px solid #3b82f6; padding: 15px; margin: 25px 0;">
<p style="color: #1e40af; font-size: 14px; margin: 0;">
<strong>¿Necesitas ayuda?</strong>
</p>
<p style="color: #1e40af; font-size: 14px; margin: 5px 0 0 0;">
Si crees que deberías tener acceso a un portal del cliente, por favor contacta al equipo de soporte de tu proveedor de servicios para obtener ayuda.
</p>
</div>
<div style="background-color: #f3f4f6; border-radius: 6px; padding: 20px; margin: 25px 0;">
<p style="color: #4b5563; font-size: 14px; margin: 0;">
<strong>Nota de seguridad:</strong> Si no solicitaste acceso, puedes ignorar este correo de forma segura.
</p>
</div>
</div>
<div style="background-color: #f9fafb; padding: 20px; text-align: center; border-top: 1px solid #e5e7eb;">
<p style="color: #9ca3af; font-size: 12px; margin: 5px 0;">
© {{currentYear}} {{platformName}}. Todos los derechos reservados.
</p>
<p style="color: #9ca3af; font-size: 11px; margin: 5px 0;">
Este es un mensaje automático. Por favor no respondas a este correo.
</p>
</div>
</div>
`,
text_content: `{{platformName}} - Solicitud de acceso
Hola,
Recibimos una solicitud para acceder al portal del cliente usando esta dirección de correo electrónico.
Si tienes una cuenta con nosotros, deberías haber recibido un correo separado con tus enlaces de inicio de sesión.
Si no recibiste un correo de inicio de sesión, puede significar:
- Esta dirección de correo electrónico no está asociada con ninguna cuenta del portal del cliente
- Tu cuenta puede estar inactiva
- El correo puede haber sido filtrado a tu carpeta de spam
¿Necesitas ayuda?
Si crees que deberías tener acceso a un portal del cliente, por favor contacta al equipo de soporte de tu proveedor de servicios para obtener ayuda.
Nota de seguridad: Si no solicitaste acceso, puedes ignorar este correo de forma segura.
---
© {{currentYear}} {{platformName}}. Todos los derechos reservados.
Este es un mensaje automático. Por favor no respondas a este correo.`
},
// Ticketing templates
{
name: 'ticket-assigned',
language_code: 'es',
subject: 'Ticket Asignado • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Assigned'),
html_content: `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f3ff;padding:32px 0;font-family:'Segoe UI',Arial,sans-serif;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:100%;max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e4ddff;box-shadow:0 12px 32px rgba(138,77,234,0.12);">
<tr>
<td style="padding:32px;background:linear-gradient(135deg,#8A4DEA,#40CFF9);color:#ffffff;">
<div style="text-transform:uppercase;letter-spacing:0.08em;font-size:12px;font-weight:600;opacity:0.85;">Ticket Asignado</div>
<div style="font-size:22px;font-weight:600;margin-top:8px;">{{ticket.title}}</div>
<div style="margin-top:12px;font-size:14px;opacity:0.85;">{{ticket.metaLine}}</div>
</td>
</tr>
<tr>
<td style="padding:28px 32px 20px 32px;">
<p style="margin:0 0 16px 0;font-size:15px;color:#1f2933;line-height:1.5;">Se te ha asignado un ticket para <strong>{{ticket.clientName}}</strong>. Revisa los detalles a continuación y toma acción.</p>
<div style="margin-bottom:24px;">
<div style="display:inline-block;padding:6px 12px;border-radius:999px;background:rgba(138,77,234,0.12);color:#5b38b0;font-size:12px;font-weight:600;letter-spacing:0.02em;">Ticket #{{ticket.id}}</div>
</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1f2933;">
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;width:160px;font-weight:600;color:#475467;">Prioridad</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<span style="display:inline-block;padding:6px 12px;border-radius:999px;background-color:{{ticket.priorityColor}};color:#ffffff;font-weight:600;">{{ticket.priority}}</span>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Estado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.status}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado por</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.assignedBy}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado a</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.assignedToName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.assignedToEmail}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Solicitante</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.requesterName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.requesterContact}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Tablero</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.board}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Categoría</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.categoryDetails}}</td>
</tr>
<tr>
<td style="padding:12px 0;font-weight:600;color:#475467;">Ubicación</td>
<td style="padding:12px 0;">{{ticket.locationSummary}}</td>
</tr>
</table>
<div style="margin:28px 0 16px 0;padding:18px 20px;border-radius:12px;background:#f8f5ff;border:1px solid #e6deff;">
<div style="font-weight:600;color:#5b38b0;margin-bottom:8px;">Descripción</div>
<div style="color:#475467;line-height:1.5;">{{ticket.description}}</div>
</div>
<a href="{{ticket.url}}" style="display:inline-block;background:#8A4DEA;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:10px;font-weight:600;">Ver Ticket</a>
</td>
</tr>
<tr>
<td style="padding:18px 32px;background:#f8f5ff;color:#5b38b0;font-size:12px;text-align:center;">Powered by Alga PSA • Manteniendo a los equipos alineados</td>
</tr>
</table>
</td>
</tr>
</table>
`,
text_content: `
Ticket Asignado a Ti
{{ticket.metaLine}}
Asignado por: {{ticket.assignedBy}}
Prioridad: {{ticket.priority}}
Estado: {{ticket.status}}
Asignado a: {{ticket.assignedDetails}}
Solicitante: {{ticket.requesterDetails}}
Tablero: {{ticket.board}}
Categoría: {{ticket.categoryDetails}}
Ubicación: {{ticket.locationSummary}}
Descripción:
{{ticket.description}}
Ver ticket: {{ticket.url}}
`
},
{
name: 'ticket-created',
language_code: 'es',
subject: 'Nuevo Ticket • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Created'),
html_content: `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f3ff;padding:32px 0;font-family:'Segoe UI',Arial,sans-serif;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:100%;max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e4ddff;box-shadow:0 12px 32px rgba(138,77,234,0.12);">
<tr>
<td style="padding:32px;background:linear-gradient(135deg,#8A4DEA,#40CFF9);color:#ffffff;">
<div style="text-transform:uppercase;letter-spacing:0.08em;font-size:12px;font-weight:600;opacity:0.85;">Nuevo Ticket Creado</div>
<div style="font-size:22px;font-weight:600;margin-top:8px;">{{ticket.title}}</div>
<div style="margin-top:12px;font-size:14px;opacity:0.85;">{{ticket.metaLine}}</div>
</td>
</tr>
<tr>
<td style="padding:28px 32px 20px 32px;">
<p style="margin:0 0 16px 0;font-size:15px;color:#1f2933;line-height:1.5;">Se ha registrado un nuevo ticket para <strong>{{ticket.clientName}}</strong>. Revisa el resumen a continuación y sigue el enlace para tomar acción.</p>
<div style="margin-bottom:24px;">
<div style="display:inline-block;padding:6px 12px;border-radius:999px;background:rgba(138,77,234,0.12);color:#5b38b0;font-size:12px;font-weight:600;letter-spacing:0.02em;">Ticket #{{ticket.id}}</div>
</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1f2933;">
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;width:160px;font-weight:600;color:#475467;">Prioridad</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<span style="display:inline-block;padding:6px 12px;border-radius:999px;background-color:{{ticket.priorityColor}};color:#ffffff;font-weight:600;">{{ticket.priority}}</span>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Estado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.status}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Creado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.createdAt}} · {{ticket.createdBy}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado a</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.assignedToName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.assignedToEmail}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Solicitante</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.requesterName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.requesterContact}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Tablero</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.board}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Categoría</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.categoryDetails}}</td>
</tr>
<tr>
<td style="padding:12px 0;font-weight:600;color:#475467;">Ubicación</td>
<td style="padding:12px 0;">{{ticket.locationSummary}}</td>
</tr>
</table>
<div style="margin:28px 0 16px 0;padding:18px 20px;border-radius:12px;background:#f8f5ff;border:1px solid #e6deff;">
<div style="font-weight:600;color:#5b38b0;margin-bottom:8px;">Descripción</div>
<div style="color:#475467;line-height:1.5;">{{ticket.description}}</div>
</div>
<a href="{{ticket.url}}" style="display:inline-block;background:#8A4DEA;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:10px;font-weight:600;">Ver Ticket</a>
</td>
</tr>
<tr>
<td style="padding:18px 32px;background:#f8f5ff;color:#5b38b0;font-size:12px;text-align:center;">Powered by Alga PSA • Manteniendo a los equipos alineados</td>
</tr>
</table>
</td>
</tr>
</table>
`,
text_content: `
Nuevo Ticket Creado para {{ticket.clientName}}
{{ticket.metaLine}}
Creado: {{ticket.createdAt}} · {{ticket.createdBy}}
Prioridad: {{ticket.priority}}
Estado: {{ticket.status}}
Asignado a: {{ticket.assignedDetails}}
Solicitante: {{ticket.requesterDetails}}
Tablero: {{ticket.board}}
Categoría: {{ticket.categoryDetails}}
Ubicación: {{ticket.locationSummary}}
Descripción:
{{ticket.description}}
Ver ticket: {{ticket.url}}
`
},
{
name: 'ticket-updated',
language_code: 'es',
subject: 'Ticket Actualizado • {{ticket.title}} ({{ticket.priority}})',
notification_subtype_id: getSubtypeId('Ticket Updated'),
html_content: `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f3ff;padding:32px 0;font-family:'Segoe UI',Arial,sans-serif;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:100%;max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e4ddff;box-shadow:0 12px 32px rgba(138,77,234,0.12);">
<tr>
<td style="padding:32px;background:linear-gradient(135deg,#8A4DEA,#40CFF9);color:#ffffff;">
<div style="text-transform:uppercase;letter-spacing:0.08em;font-size:12px;font-weight:600;opacity:0.85;">Ticket Actualizado</div>
<div style="font-size:22px;font-weight:600;margin-top:8px;">{{ticket.title}}</div>
<div style="margin-top:12px;font-size:14px;opacity:0.85;">{{ticket.metaLine}}</div>
</td>
</tr>
<tr>
<td style="padding:28px 32px 20px 32px;">
<p style="margin:0 0 16px 0;font-size:15px;color:#1f2933;line-height:1.5;">Se ha actualizado un ticket para <strong>{{ticket.clientName}}</strong>. Revisa los cambios a continuación.</p>
<div style="margin-bottom:24px;">
<div style="display:inline-block;padding:6px 12px;border-radius:999px;background:rgba(138,77,234,0.12);color:#5b38b0;font-size:12px;font-weight:600;letter-spacing:0.02em;">Ticket #{{ticket.id}}</div>
</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1f2933;">
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;width:160px;font-weight:600;color:#475467;">Prioridad</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<span style="display:inline-block;padding:6px 12px;border-radius:999px;background-color:{{ticket.priorityColor}};color:#ffffff;font-weight:600;">{{ticket.priority}}</span>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Estado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.status}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Actualizado por</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.updatedBy}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado a</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.assignedToName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.assignedToEmail}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Solicitante</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.requesterName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.requesterContact}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Tablero</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.board}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Categoría</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.categoryDetails}}</td>
</tr>
<tr>
<td style="padding:12px 0;font-weight:600;color:#475467;">Ubicación</td>
<td style="padding:12px 0;">{{ticket.locationSummary}}</td>
</tr>
</table>
<div style="margin:28px 0 16px 0;padding:18px 20px;border-radius:12px;background:#fff9e6;border:1px solid #ffe4a3;">
<div style="font-weight:600;color:#92400e;margin-bottom:8px;">Cambios Realizados</div>
<div style="color:#475467;line-height:1.5;">{{ticket.changes}}</div>
</div>
<a href="{{ticket.url}}" style="display:inline-block;background:#8A4DEA;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:10px;font-weight:600;">Ver Ticket</a>
</td>
</tr>
<tr>
<td style="padding:18px 32px;background:#f8f5ff;color:#5b38b0;font-size:12px;text-align:center;">Powered by Alga PSA • Manteniendo a los equipos alineados</td>
</tr>
</table>
</td>
</tr>
</table>
`,
text_content: `
Ticket Actualizado
{{ticket.metaLine}}
Actualizado por: {{ticket.updatedBy}}
Prioridad: {{ticket.priority}}
Estado: {{ticket.status}}
Asignado a: {{ticket.assignedDetails}}
Solicitante: {{ticket.requesterDetails}}
Tablero: {{ticket.board}}
Categoría: {{ticket.categoryDetails}}
Ubicación: {{ticket.locationSummary}}
Cambios realizados:
{{ticket.changes}}
Ver ticket: {{ticket.url}}
`
},
{
name: 'ticket-closed',
language_code: 'es',
subject: 'Ticket Cerrado • {{ticket.title}}',
notification_subtype_id: getSubtypeId('Ticket Closed'),
html_content: `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f3ff;padding:32px 0;font-family:'Segoe UI',Arial,sans-serif;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:100%;max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e4ddff;box-shadow:0 12px 32px rgba(138,77,234,0.12);">
<tr>
<td style="padding:32px;background:linear-gradient(135deg,#10b981,#059669);color:#ffffff;">
<div style="text-transform:uppercase;letter-spacing:0.08em;font-size:12px;font-weight:600;opacity:0.85;">Ticket Cerrado</div>
<div style="font-size:22px;font-weight:600;margin-top:8px;">{{ticket.title}}</div>
<div style="margin-top:12px;font-size:14px;opacity:0.85;">{{ticket.metaLine}}</div>
</td>
</tr>
<tr>
<td style="padding:28px 32px 20px 32px;">
<p style="margin:0 0 16px 0;font-size:15px;color:#1f2933;line-height:1.5;">Se ha resuelto y cerrado un ticket para <strong>{{ticket.clientName}}</strong>. Revisa los detalles de la resolución a continuación.</p>
<div style="margin-bottom:24px;">
<div style="display:inline-block;padding:6px 12px;border-radius:999px;background:rgba(16,185,129,0.12);color:#047857;font-size:12px;font-weight:600;letter-spacing:0.02em;">Ticket #{{ticket.id}}</div>
</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1f2933;">
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;width:160px;font-weight:600;color:#475467;">Estado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<span style="display:inline-block;padding:6px 12px;border-radius:999px;background-color:#10b981;color:#ffffff;font-weight:600;">Cerrado</span>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Cerrado por</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.closedBy}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado a</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.assignedToName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.assignedToEmail}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Solicitante</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.requesterName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.requesterContact}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Tablero</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.board}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Categoría</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.categoryDetails}}</td>
</tr>
<tr>
<td style="padding:12px 0;font-weight:600;color:#475467;">Ubicación</td>
<td style="padding:12px 0;">{{ticket.locationSummary}}</td>
</tr>
</table>
<div style="margin:28px 0 16px 0;padding:18px 20px;border-radius:12px;background:#f0fdf4;border:1px solid #bbf7d0;">
<div style="font-weight:600;color:#047857;margin-bottom:8px;">Resolución</div>
<div style="color:#475467;line-height:1.5;">{{ticket.resolution}}</div>
</div>
<a href="{{ticket.url}}" style="display:inline-block;background:#10b981;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:10px;font-weight:600;">Ver Ticket</a>
</td>
</tr>
<tr>
<td style="padding:18px 32px;background:#f0fdf4;color:#047857;font-size:12px;text-align:center;">Powered by Alga PSA • Manteniendo a los equipos alineados</td>
</tr>
</table>
</td>
</tr>
</table>
`,
text_content: `
Ticket Cerrado
{{ticket.metaLine}}
Cerrado por: {{ticket.closedBy}}
Estado: Cerrado
Asignado a: {{ticket.assignedDetails}}
Solicitante: {{ticket.requesterDetails}}
Tablero: {{ticket.board}}
Categoría: {{ticket.categoryDetails}}
Ubicación: {{ticket.locationSummary}}
Resolución:
{{ticket.resolution}}
Ver ticket: {{ticket.url}}
`
},
{
name: 'ticket-comment-added',
language_code: 'es',
subject: 'Nuevo Comentario • {{ticket.title}}',
notification_subtype_id: getSubtypeId('Ticket Comment Added'),
html_content: `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f3ff;padding:32px 0;font-family:'Segoe UI',Arial,sans-serif;">
<tr>
<td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:100%;max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e4ddff;box-shadow:0 12px 32px rgba(138,77,234,0.12);">
<tr>
<td style="padding:32px;background:linear-gradient(135deg,#8A4DEA,#40CFF9);color:#ffffff;">
<div style="text-transform:uppercase;letter-spacing:0.08em;font-size:12px;font-weight:600;opacity:0.85;">Nuevo Comentario Agregado</div>
<div style="font-size:22px;font-weight:600;margin-top:8px;">{{ticket.title}}</div>
<div style="margin-top:12px;font-size:14px;opacity:0.85;">{{ticket.metaLine}}</div>
</td>
</tr>
<tr>
<td style="padding:28px 32px 20px 32px;">
<p style="margin:0 0 16px 0;font-size:15px;color:#1f2933;line-height:1.5;">Se ha agregado un nuevo comentario a un ticket para <strong>{{ticket.clientName}}</strong>.</p>
<div style="margin-bottom:24px;">
<div style="display:inline-block;padding:6px 12px;border-radius:999px;background:rgba(138,77,234,0.12);color:#5b38b0;font-size:12px;font-weight:600;letter-spacing:0.02em;">Ticket #{{ticket.id}}</div>
</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;font-size:14px;color:#1f2933;">
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;width:160px;font-weight:600;color:#475467;">Prioridad</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<span style="display:inline-block;padding:6px 12px;border-radius:999px;background-color:{{ticket.priorityColor}};color:#ffffff;font-weight:600;">{{ticket.priority}}</span>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Estado</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.status}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Comentario de</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{comment.author}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Asignado a</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.assignedToName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.assignedToEmail}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Solicitante</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">
<div style="font-weight:600;">{{ticket.requesterName}}</div>
<div style="color:#667085;font-size:13px;">{{ticket.requesterContact}}</div>
</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Tablero</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.board}}</td>
</tr>
<tr>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;font-weight:600;color:#475467;">Categoría</td>
<td style="padding:12px 0;border-bottom:1px solid #eef2ff;">{{ticket.categoryDetails}}</td>
</tr>
<tr>
<td style="padding:12px 0;font-weight:600;color:#475467;">Ubicación</td>
<td style="padding:12px 0;">{{ticket.locationSummary}}</td>
</tr>
</table>
<div style="margin:28px 0 16px 0;padding:18px 20px;border-radius:12px;background:#eff6ff;border:1px solid #bfdbfe;">
<div style="font-weight:600;color:#1e40af;margin-bottom:8px;">💬 Comentario</div>
<div style="color:#475467;line-height:1.5;">{{comment.content}}</div>
</div>
<a href="{{ticket.url}}" style="display:inline-block;background:#8A4DEA;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:10px;font-weight:600;">Ver Ticket</a>
</td>
</tr>
<tr>
<td style="padding:18px 32px;background:#f8f5ff;color:#5b38b0;font-size:12px;text-align:center;">Powered by Alga PSA • Manteniendo a los equipos alineados</td>
</tr>
</table>
</td>
</tr>
</table>
`,
text_content: `
Nuevo Comentario Agregado
{{ticket.metaLine}}
Comentario de: {{comment.author}}
Prioridad: {{ticket.priority}}
Estado: {{ticket.status}}
Asignado a: {{ticket.assignedDetails}}
Solicitante: {{ticket.requesterDetails}}
Tablero: {{ticket.board}}
Categoría: {{ticket.categoryDetails}}
Ubicación: {{ticket.locationSummary}}
Comentario:
{{comment.content}}
Ver ticket: {{ticket.url}}
`
},
// Billing templates
{
name: 'invoice-generated',
language_code: 'es',
subject: 'Nueva factura #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Invoice Generated'),
html_content: `
<h2>Factura {{invoice.number}}</h2>
<p>Se ha generado una nueva factura para tu revisión:</p>
<div class="details">
<p><strong>Número de factura:</strong> {{invoice.number}}</p>
<p><strong>Monto:</strong> {{invoice.amount}}</p>
<p><strong>Fecha de vencimiento:</strong> {{invoice.dueDate}}</p>
<p><strong>Cliente:</strong> {{invoice.clientName}}</p>
</div>
<a href="{{invoice.url}}" class="button">Ver la factura</a>
`,
text_content: `
Factura {{invoice.number}}
Se ha generado una nueva factura para tu revisión:
Número de factura: {{invoice.number}}
Monto: {{invoice.amount}}
Fecha de vencimiento: {{invoice.dueDate}}
Cliente: {{invoice.clientName}}
Ver la factura: {{invoice.url}}
`
},
{
name: 'payment-received',
language_code: 'es',
subject: 'Pago recibido: Factura #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Payment Received'),
html_content: `
<h2>Pago recibido</h2>
<p>Se ha recibido el pago de la factura #{{invoice.number}}:</p>
<div class="details">
<p><strong>Número de factura:</strong> {{invoice.number}}</p>
<p><strong>Monto pagado:</strong> {{invoice.amountPaid}}</p>
<p><strong>Fecha de pago:</strong> {{invoice.paymentDate}}</p>
<p><strong>Método de pago:</strong> {{invoice.paymentMethod}}</p>
</div>
<a href="{{invoice.url}}" class="button">Ver la factura</a>
`,
text_content: `
Pago recibido
Se ha recibido el pago de la factura #{{invoice.number}}:
Número de factura: {{invoice.number}}
Monto pagado: {{invoice.amountPaid}}
Fecha de pago: {{invoice.paymentDate}}
Método de pago: {{invoice.paymentMethod}}
Ver la factura: {{invoice.url}}
`
},
{
name: 'payment-overdue',
language_code: 'es',
subject: 'Pago vencido: Factura #{{invoice.number}}',
notification_subtype_id: getSubtypeId('Payment Overdue'),
html_content: `
<h2>Pago vencido</h2>
<p>El pago de la factura #{{invoice.number}} está vencido:</p>
<div class="details">
<p><strong>Número de factura:</strong> {{invoice.number}}</p>
<p><strong>Monto adeudado:</strong> {{invoice.amountDue}}</p>
<p><strong>Fecha de vencimiento:</strong> {{invoice.dueDate}}</p>
<p><strong>Días de retraso:</strong> {{invoice.daysOverdue}}</p>
</div>
<a href="{{invoice.url}}" class="button">Ver la factura</a>
`,
text_content: `
Pago vencido
El pago de la factura #{{invoice.number}} está vencido:
Número de factura: {{invoice.number}}
Monto adeudado: {{invoice.amountDue}}
Fecha de vencimiento: {{invoice.dueDate}}
Días de retraso: {{invoice.daysOverdue}}
Ver la factura: {{invoice.url}}
`
}
]).onConflict(['name', 'language_code']).merge({
subject: knex.raw('excluded.subject'),
html_content: knex.raw('excluded.html_content'),
text_content: knex.raw('excluded.text_content'),
notification_subtype_id: knex.raw('excluded.notification_subtype_id')
});
console.log('✓ Spanish email templates added (auth + notifications)');
};
exports.down = async function(knex) {
// Remove Spanish email templates
// NOTE: email-verification and portal-invitation are NOT removed as they're managed by migration 20251029100000
await knex('system_email_templates')
.where({ language_code: 'es' })
.whereIn('name', [
'password-reset',
'tenant-recovery',
'no-account-found',
'ticket-assigned',
'ticket-created',
'ticket-updated',
'ticket-closed',
'ticket-comment-added',
'invoice-generated',
'payment-received',
'payment-overdue'
])
.del();
console.log('Spanish email templates removed');
};