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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
201 lines
8.2 KiB
JavaScript
201 lines
8.2 KiB
JavaScript
/**
|
|
* Migration: Standardize email template styling
|
|
*
|
|
* Fixes inconsistent email template styling by updating all templates to use
|
|
* the brand purple-blue gradient: linear-gradient(135deg, #8A4DEA, #40CFF9)
|
|
*
|
|
* Templates fixed:
|
|
* - GREEN gradient → brand gradient (appointment-request-approved, ticket-closed, etc.)
|
|
* - RED gradient → brand gradient (appointment-request-declined, payment-overdue)
|
|
* - BLUE gradient → brand gradient (email-verification)
|
|
* - INDIGO gradient → brand gradient (SURVEY_TICKET_CLOSED, invoice-generated pl)
|
|
* - LEGACY purple → brand gradient (no-account-found, tenant-recovery)
|
|
* - Purple-only → brand gradient (password-reset, portal-invitation)
|
|
* - No gradient → full template with brand gradient
|
|
*/
|
|
|
|
const BRAND_GRADIENT = 'linear-gradient(135deg,#8A4DEA,#40CFF9)';
|
|
const BRAND_PRIMARY = '#8A4DEA';
|
|
|
|
// Gradient patterns to replace
|
|
const GRADIENT_REPLACEMENTS = [
|
|
// Green gradients
|
|
{ from: 'linear-gradient(135deg,#10b981,#059669)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', to: BRAND_GRADIENT },
|
|
// Red gradients
|
|
{ from: 'linear-gradient(135deg,#ef4444,#dc2626)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)', to: BRAND_GRADIENT },
|
|
// Amber/Orange gradients
|
|
{ from: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg,#f59e0b,#d97706)', to: BRAND_GRADIENT },
|
|
// Blue gradients
|
|
{ from: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg,#3b82f6,#2563eb)', to: BRAND_GRADIENT },
|
|
// Indigo/Violet gradients
|
|
{ from: 'linear-gradient(135deg,#6366f1,#8b5cf6)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', to: BRAND_GRADIENT },
|
|
// Legacy purple gradients
|
|
{ from: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg,#667eea,#764ba2)', to: BRAND_GRADIENT },
|
|
// Purple-only gradients (missing cyan)
|
|
{ from: 'linear-gradient(135deg, #8a4dea 0%, #7c3aed 100%)', to: BRAND_GRADIENT },
|
|
{ from: 'linear-gradient(135deg,#8a4dea,#7c3aed)', to: BRAND_GRADIENT },
|
|
];
|
|
|
|
// Button color replacements
|
|
const BUTTON_REPLACEMENTS = [
|
|
{ from: 'background:#10b981', to: `background:${BRAND_PRIMARY}` },
|
|
{ from: 'background: #10b981', to: `background: ${BRAND_PRIMARY}` },
|
|
{ from: 'background:#ef4444', to: `background:${BRAND_PRIMARY}` },
|
|
{ from: 'background: #ef4444', to: `background: ${BRAND_PRIMARY}` },
|
|
];
|
|
|
|
// Footer background replacements
|
|
const FOOTER_REPLACEMENTS = [
|
|
{ from: 'background:#f0fdf4', to: 'background:#f8f5ff' }, // Green footer bg
|
|
{ from: 'background: #f0fdf4', to: 'background: #f8f5ff' },
|
|
{ from: 'background:#fef2f2', to: 'background:#f8f5ff' }, // Red footer bg
|
|
{ from: 'background: #fef2f2', to: 'background: #f8f5ff' },
|
|
];
|
|
|
|
// Footer text color replacements
|
|
const FOOTER_TEXT_REPLACEMENTS = [
|
|
{ from: 'color:#047857', to: 'color:#5b38b0' }, // Green text
|
|
{ from: 'color: #047857', to: 'color: #5b38b0' },
|
|
{ from: 'color:#dc2626', to: 'color:#5b38b0' }, // Red text
|
|
{ from: 'color: #dc2626', to: 'color: #5b38b0' },
|
|
];
|
|
|
|
// Badge background replacements
|
|
const BADGE_REPLACEMENTS = [
|
|
{ from: 'rgba(16,185,129,0.12)', to: 'rgba(138,77,234,0.12)' }, // Green badge
|
|
{ from: 'rgba(239,68,68,0.12)', to: 'rgba(138,77,234,0.12)' }, // Red badge
|
|
];
|
|
|
|
// Info box replacements
|
|
const INFO_BOX_REPLACEMENTS = [
|
|
{ from: 'background:#f0fdf4;border:1px solid #bbf7d0', to: 'background:#f8f5ff;border:1px solid #e6deff' },
|
|
{ from: 'background: #f0fdf4; border: 1px solid #bbf7d0', to: 'background: #f8f5ff; border: 1px solid #e6deff' },
|
|
// Standalone border replacements (catch any remaining green borders)
|
|
{ from: 'border:1px solid #bbf7d0', to: 'border:1px solid #e6deff' },
|
|
{ from: 'border: 1px solid #bbf7d0', to: 'border: 1px solid #e6deff' },
|
|
// Standalone background replacements
|
|
{ from: 'background:#f0fdf4', to: 'background:#f8f5ff' },
|
|
{ from: 'background: #f0fdf4', to: 'background: #f8f5ff' },
|
|
];
|
|
|
|
// Templates that need gradient fixes (have gradients but wrong colors)
|
|
const TEMPLATES_WITH_GRADIENT_ISSUES = [
|
|
// All languages for these templates
|
|
'appointment-request-approved',
|
|
'appointment-request-declined',
|
|
'ticket-closed',
|
|
'email-verification',
|
|
'SURVEY_TICKET_CLOSED',
|
|
'no-account-found',
|
|
'tenant-recovery',
|
|
'password-reset',
|
|
'portal-invitation',
|
|
'project-task-assigned-primary',
|
|
];
|
|
|
|
function applyReplacements(content, replacements) {
|
|
let result = content;
|
|
for (const { from, to } of replacements) {
|
|
result = result.split(from).join(to);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
exports.up = async function(knex) {
|
|
console.log('Standardizing email template styling to brand colors...');
|
|
|
|
let updatedCount = 0;
|
|
|
|
// Fix templates with gradient issues
|
|
for (const templateName of TEMPLATES_WITH_GRADIENT_ISSUES) {
|
|
const templates = await knex('system_email_templates')
|
|
.where({ name: templateName });
|
|
|
|
for (const template of templates) {
|
|
let htmlContent = template.html_content || '';
|
|
|
|
// Apply all replacements
|
|
htmlContent = applyReplacements(htmlContent, GRADIENT_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, BUTTON_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, FOOTER_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, FOOTER_TEXT_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, BADGE_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, INFO_BOX_REPLACEMENTS);
|
|
|
|
if (htmlContent !== template.html_content) {
|
|
await knex('system_email_templates')
|
|
.where({ id: template.id })
|
|
.update({
|
|
html_content: htmlContent,
|
|
updated_at: new Date()
|
|
});
|
|
updatedCount++;
|
|
console.log(` Updated: ${templateName} (${template.language_code})`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fix payment-received and payment-overdue for Polish (they have gradients)
|
|
const plPaymentTemplates = await knex('system_email_templates')
|
|
.whereIn('name', ['payment-received', 'payment-overdue'])
|
|
.where({ language_code: 'pl' });
|
|
|
|
for (const template of plPaymentTemplates) {
|
|
let htmlContent = template.html_content || '';
|
|
htmlContent = applyReplacements(htmlContent, GRADIENT_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, BUTTON_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, FOOTER_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, FOOTER_TEXT_REPLACEMENTS);
|
|
htmlContent = applyReplacements(htmlContent, BADGE_REPLACEMENTS);
|
|
|
|
if (htmlContent !== template.html_content) {
|
|
await knex('system_email_templates')
|
|
.where({ id: template.id })
|
|
.update({
|
|
html_content: htmlContent,
|
|
updated_at: new Date()
|
|
});
|
|
updatedCount++;
|
|
console.log(` Updated: ${template.name} (pl)`);
|
|
}
|
|
}
|
|
|
|
// Fix invoice-generated for Polish (has gradient)
|
|
const plInvoiceGenerated = await knex('system_email_templates')
|
|
.where({ name: 'invoice-generated', language_code: 'pl' })
|
|
.first();
|
|
|
|
if (plInvoiceGenerated) {
|
|
let htmlContent = plInvoiceGenerated.html_content || '';
|
|
htmlContent = applyReplacements(htmlContent, GRADIENT_REPLACEMENTS);
|
|
|
|
if (htmlContent !== plInvoiceGenerated.html_content) {
|
|
await knex('system_email_templates')
|
|
.where({ id: plInvoiceGenerated.id })
|
|
.update({
|
|
html_content: htmlContent,
|
|
updated_at: new Date()
|
|
});
|
|
updatedCount++;
|
|
console.log(' Updated: invoice-generated (pl)');
|
|
}
|
|
}
|
|
|
|
console.log(`\nGradient fixes complete: ${updatedCount} templates updated`);
|
|
console.log('\nNote: Templates without gradients (invoice-generated, payment-*, project-*, time-entry-*, credit-expiring) need manual review for full template updates.');
|
|
};
|
|
|
|
exports.down = async function(knex) {
|
|
// This migration performs color replacements that are difficult to reverse
|
|
// without storing the original content. A full rollback would require
|
|
// restoring from backup or re-running the original template migrations.
|
|
console.log('Rollback: This migration cannot be automatically reversed.');
|
|
console.log('To restore original templates, re-run the original template migrations.');
|
|
};
|