Adds branded HTML layout matching the admin portal purple palette, updates 000066 seeds, and adds 000067 migration to refresh existing template rows. Co-authored-by: Cursor <cursoragent@cursor.com>
187 lines
12 KiB
SQL
187 lines
12 KiB
SQL
CREATE TABLE IF NOT EXISTS email_templates (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
slug VARCHAR(100) NOT NULL UNIQUE,
|
|
name VARCHAR(200) NOT NULL,
|
|
subject TEXT NOT NULL,
|
|
body_text TEXT NOT NULL,
|
|
body_html TEXT NOT NULL,
|
|
variables JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
is_system BOOLEAN NOT NULL DEFAULT FALSE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'INACTIVE')),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_email_templates_status ON email_templates(status);
|
|
CREATE INDEX IF NOT EXISTS idx_email_templates_slug ON email_templates(slug);
|
|
|
|
INSERT INTO email_templates (slug, name, subject, body_text, body_html, variables, is_system, status)
|
|
VALUES
|
|
(
|
|
'otp',
|
|
'One-Time Password',
|
|
'Yimaru Academy — Your verification code',
|
|
'Yimaru Academy{{if .FirstName}}, {{.FirstName}}{{end}}
|
|
|
|
Your verification code is {{.OTP}}.
|
|
It expires in {{.ExpiresMinutes}} minutes.
|
|
|
|
Please do not share this code with anyone.',
|
|
$otp_html$<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Yimaru Academy</title></head>
|
|
<body style="margin:0;padding:0;background-color:#f4f6fb;font-family:Inter,Roboto,Helvetica,Arial,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color:#f4f6fb;padding:32px 16px;">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(157,42,131,0.14);">
|
|
<tr><td style="background:linear-gradient(135deg,#7b1f6e 0%,#9d2a83 52%,#c43a9a 100%);padding:28px 32px;text-align:center;">
|
|
<p style="margin:0;color:#ffffff;font-size:22px;font-weight:700;letter-spacing:0.3px;">Yimaru Academy</p>
|
|
<p style="margin:8px 0 0;color:rgba(255,255,255,0.88);font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;">Online Learning Platform</p>
|
|
</td></tr>
|
|
<tr><td style="padding:32px;">
|
|
<h1 style="margin:0 0 8px;color:#333333;font-size:24px;font-weight:700;line-height:1.3;">Your verification code</h1>
|
|
<p style="margin:0 0 20px;color:#666666;font-size:15px;line-height:1.6;">{{if .FirstName}}Hi <strong style="color:#333333;">{{.FirstName}}</strong>, {{end}}use the code below to continue signing in to Yimaru Academy.</p>
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0"><tr><td style="background-color:#eef4ff;border-radius:8px;padding:20px;border:1px solid #e0e8f5;text-align:center;">
|
|
<p style="margin:0 0 6px;color:#9d2a83;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;">One-time password</p>
|
|
<p style="margin:0;color:#333333;font-size:34px;font-weight:700;letter-spacing:8px;font-family:Consolas,Monaco,monospace;">{{.OTP}}</p>
|
|
<p style="margin:12px 0 0;color:#666666;font-size:13px;">Expires in {{.ExpiresMinutes}} minutes</p>
|
|
</td></tr></table>
|
|
<p style="margin:24px 0 0;color:#666666;font-size:14px;line-height:1.6;">If you did not request this code, you can safely ignore this email.</p>
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background-color:#fafafa;border-top:1px solid #eeeeee;text-align:center;">
|
|
<p style="margin:0;color:#999999;font-size:12px;line-height:1.5;">© 2026 Yimaru Academy · All rights reserved</p>
|
|
</td></tr>
|
|
</table></td></tr></table></body></html>$otp_html$,
|
|
'["OTP", "FirstName", "ExpiresMinutes"]'::jsonb,
|
|
TRUE,
|
|
'ACTIVE'
|
|
),
|
|
(
|
|
'invitation',
|
|
'User Invitation',
|
|
'You are invited to Yimaru Academy',
|
|
'Hi{{if .FirstName}} {{.FirstName}}{{end}},
|
|
|
|
You have been invited{{if .InviterName}} by {{.InviterName}}{{end}} to join Yimaru Academy.
|
|
|
|
Accept your invitation: {{.InviteLink}}',
|
|
$invite_html$<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Yimaru Academy</title></head>
|
|
<body style="margin:0;padding:0;background-color:#f4f6fb;font-family:Inter,Roboto,Helvetica,Arial,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color:#f4f6fb;padding:32px 16px;">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(157,42,131,0.14);">
|
|
<tr><td style="background:linear-gradient(135deg,#7b1f6e 0%,#9d2a83 52%,#c43a9a 100%);padding:28px 32px;text-align:center;">
|
|
<p style="margin:0;color:#ffffff;font-size:22px;font-weight:700;">Yimaru Academy</p>
|
|
<p style="margin:8px 0 0;color:rgba(255,255,255,0.88);font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;">Online Learning Platform</p>
|
|
</td></tr>
|
|
<tr><td style="padding:32px;">
|
|
<h1 style="margin:0 0 8px;color:#333333;font-size:24px;font-weight:700;">You’re invited</h1>
|
|
<p style="margin:0 0 20px;color:#666666;font-size:15px;line-height:1.6;">{{if .FirstName}}Hi <strong style="color:#333333;">{{.FirstName}}</strong>, {{end}}you have been invited{{if .InviterName}} by <strong style="color:#9d2a83;">{{.InviterName}}</strong>{{end}} to join Yimaru Academy.</p>
|
|
<p style="margin:0 0 24px;text-align:center;"><a href="{{.InviteLink}}" style="display:inline-block;background-color:#9d2a83;color:#ffffff !important;text-decoration:none;padding:14px 28px;border-radius:8px;font-weight:600;font-size:15px;">Accept invitation</a></p>
|
|
<p style="margin:24px 0 0;color:#666666;font-size:14px;line-height:1.6;">Or copy this link: <a href="{{.InviteLink}}" style="color:#9d2a83;">{{.InviteLink}}</a></p>
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background-color:#fafafa;border-top:1px solid #eeeeee;text-align:center;">
|
|
<p style="margin:0;color:#999999;font-size:12px;">© 2026 Yimaru Academy · All rights reserved</p>
|
|
</td></tr>
|
|
</table></td></tr></table></body></html>$invite_html$,
|
|
'["FirstName", "InviterName", "InviteLink"]'::jsonb,
|
|
TRUE,
|
|
'ACTIVE'
|
|
),
|
|
(
|
|
'password_reset',
|
|
'Password Reset',
|
|
'Reset your Yimaru Academy password',
|
|
'Hi{{if .FirstName}} {{.FirstName}}{{end}},
|
|
|
|
Reset your password: {{.ResetLink}}
|
|
|
|
This link expires in {{.ExpiresMinutes}} minutes.',
|
|
$reset_html$<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Yimaru Academy</title></head>
|
|
<body style="margin:0;padding:0;background-color:#f4f6fb;font-family:Inter,Roboto,Helvetica,Arial,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color:#f4f6fb;padding:32px 16px;">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(157,42,131,0.14);">
|
|
<tr><td style="background:linear-gradient(135deg,#7b1f6e 0%,#9d2a83 52%,#c43a9a 100%);padding:28px 32px;text-align:center;">
|
|
<p style="margin:0;color:#ffffff;font-size:22px;font-weight:700;">Yimaru Academy</p>
|
|
<p style="margin:8px 0 0;color:rgba(255,255,255,0.88);font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;">Online Learning Platform</p>
|
|
</td></tr>
|
|
<tr><td style="padding:32px;">
|
|
<h1 style="margin:0 0 8px;color:#333333;font-size:24px;font-weight:700;">Reset your password</h1>
|
|
<p style="margin:0 0 20px;color:#666666;font-size:15px;line-height:1.6;">{{if .FirstName}}Hi <strong style="color:#333333;">{{.FirstName}}</strong>, {{end}}we received a request to reset your Yimaru Academy password. The link below expires in {{.ExpiresMinutes}} minutes.</p>
|
|
<p style="margin:0 0 24px;text-align:center;"><a href="{{.ResetLink}}" style="display:inline-block;background-color:#9d2a83;color:#ffffff !important;text-decoration:none;padding:14px 28px;border-radius:8px;font-weight:600;font-size:15px;">Reset password</a></p>
|
|
<p style="margin:24px 0 0;color:#666666;font-size:14px;line-height:1.6;">If you did not request a reset, ignore this email.</p>
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background-color:#fafafa;border-top:1px solid #eeeeee;text-align:center;">
|
|
<p style="margin:0;color:#999999;font-size:12px;">© 2026 Yimaru Academy · All rights reserved</p>
|
|
</td></tr>
|
|
</table></td></tr></table></body></html>$reset_html$,
|
|
'["FirstName", "ResetLink", "ExpiresMinutes"]'::jsonb,
|
|
TRUE,
|
|
'ACTIVE'
|
|
),
|
|
(
|
|
'welcome',
|
|
'Welcome Email',
|
|
'Welcome to Yimaru Academy',
|
|
'Hi{{if .FirstName}} {{.FirstName}}{{end}},
|
|
|
|
Welcome to Yimaru Academy! Sign in to get started: {{.LoginURL}}',
|
|
$welcome_html$<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Yimaru Academy</title></head>
|
|
<body style="margin:0;padding:0;background-color:#f4f6fb;font-family:Inter,Roboto,Helvetica,Arial,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color:#f4f6fb;padding:32px 16px;">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(157,42,131,0.14);">
|
|
<tr><td style="background:linear-gradient(135deg,#7b1f6e 0%,#9d2a83 52%,#c43a9a 100%);padding:28px 32px;text-align:center;">
|
|
<p style="margin:0;color:#ffffff;font-size:22px;font-weight:700;">Yimaru Academy</p>
|
|
<p style="margin:8px 0 0;color:rgba(255,255,255,0.88);font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;">Online Learning Platform</p>
|
|
</td></tr>
|
|
<tr><td style="padding:32px;">
|
|
<h1 style="margin:0 0 8px;color:#333333;font-size:24px;font-weight:700;">Welcome aboard</h1>
|
|
<p style="margin:0 0 20px;color:#666666;font-size:15px;line-height:1.6;">{{if .FirstName}}Hi <strong style="color:#333333;">{{.FirstName}}</strong>, {{end}}your Yimaru Academy account is ready. Start learning at your own pace.</p>
|
|
<p style="margin:0 0 24px;text-align:center;"><a href="{{.LoginURL}}" style="display:inline-block;background-color:#9d2a83;color:#ffffff !important;text-decoration:none;padding:14px 28px;border-radius:8px;font-weight:600;font-size:15px;">Sign in to Yimaru Academy</a></p>
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background-color:#fafafa;border-top:1px solid #eeeeee;text-align:center;">
|
|
<p style="margin:0;color:#999999;font-size:12px;">© 2026 Yimaru Academy · All rights reserved</p>
|
|
</td></tr>
|
|
</table></td></tr></table></body></html>$welcome_html$,
|
|
'["FirstName", "LoginURL"]'::jsonb,
|
|
TRUE,
|
|
'ACTIVE'
|
|
),
|
|
(
|
|
'custom_message',
|
|
'Custom Message',
|
|
'{{.Subject}}',
|
|
'{{.Message}}',
|
|
$custom_html$<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Yimaru Academy</title></head>
|
|
<body style="margin:0;padding:0;background-color:#f4f6fb;font-family:Inter,Roboto,Helvetica,Arial,sans-serif;">
|
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color:#f4f6fb;padding:32px 16px;">
|
|
<tr><td align="center">
|
|
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(157,42,131,0.14);">
|
|
<tr><td style="background:linear-gradient(135deg,#7b1f6e 0%,#9d2a83 52%,#c43a9a 100%);padding:28px 32px;text-align:center;">
|
|
<p style="margin:0;color:#ffffff;font-size:22px;font-weight:700;">Yimaru Academy</p>
|
|
<p style="margin:8px 0 0;color:rgba(255,255,255,0.88);font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;">Online Learning Platform</p>
|
|
</td></tr>
|
|
<tr><td style="padding:32px;">
|
|
<h1 style="margin:0 0 8px;color:#333333;font-size:24px;font-weight:700;">{{.Subject}}</h1>
|
|
<div style="margin:0;color:#666666;font-size:15px;line-height:1.6;">{{.Message}}</div>
|
|
</td></tr>
|
|
<tr><td style="padding:20px 32px;background-color:#fafafa;border-top:1px solid #eeeeee;text-align:center;">
|
|
<p style="margin:0;color:#999999;font-size:12px;">© 2026 Yimaru Academy · All rights reserved</p>
|
|
</td></tr>
|
|
</table></td></tr></table></body></html>$custom_html$,
|
|
'["Subject", "Message"]'::jsonb,
|
|
TRUE,
|
|
'ACTIVE'
|
|
)
|
|
ON CONFLICT (slug) DO NOTHING;
|