Fix email template rendering for Outlook-safe branded content
This commit is contained in:
@@ -1 +1 @@
|
||||
0303261702
|
||||
0303261719
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -139,17 +139,17 @@ TEMPLATE_PRESENTATION: Dict[str, Dict[str, str]] = {
|
||||
|
||||
def _build_email_stat_card(label: str, value: str, detail: str = "") -> str:
|
||||
detail_html = (
|
||||
f"<div style=\"margin-top:8px; font-size:13px; line-height:1.6; color:#9aa3b8; word-break:break-word;\">"
|
||||
f"<div style=\"margin-top:8px; font-size:13px; line-height:1.6; color:#5c687d; word-break:break-word;\">"
|
||||
f"{html.escape(detail)}</div>"
|
||||
if detail
|
||||
else ""
|
||||
)
|
||||
return (
|
||||
"<div style=\"padding:16px; background:#151c2d; border:1px solid rgba(255,255,255,0.08); "
|
||||
"border-radius:16px; color:#e9ecf5;\">"
|
||||
f"<div style=\"font-size:11px; letter-spacing:0.12em; text-transform:uppercase; color:#9aa3b8; "
|
||||
"<div style=\"padding:16px; background:#f8fafc; border:1px solid #d9e2ef; "
|
||||
"border-radius:16px; color:#132033;\">"
|
||||
f"<div style=\"font-size:11px; letter-spacing:0.12em; text-transform:uppercase; color:#6b778c; "
|
||||
f"margin-bottom:8px;\">{html.escape(label)}</div>"
|
||||
f"<div style=\"font-size:20px; font-weight:800; line-height:1.45; word-break:break-word;\">"
|
||||
f"<div style=\"font-size:20px; font-weight:800; line-height:1.45; word-break:break-word; color:#132033;\">"
|
||||
f"{html.escape(value)}</div>"
|
||||
f"{detail_html}"
|
||||
"</div>"
|
||||
@@ -184,7 +184,7 @@ def _build_email_list(items: list[str], *, ordered: bool = False) -> str:
|
||||
f"<li style=\"margin:0 0 8px;\">{html.escape(item)}</li>" for item in items if item
|
||||
)
|
||||
return (
|
||||
f"<{tag} style=\"margin:0; {marker} color:#dbe5ff; line-height:1.8; font-size:14px;\">"
|
||||
f"<{tag} style=\"margin:0; {marker} color:#132033; line-height:1.8; font-size:14px;\">"
|
||||
f"{rendered_items}"
|
||||
f"</{tag}>"
|
||||
)
|
||||
@@ -193,40 +193,40 @@ def _build_email_list(items: list[str], *, ordered: bool = False) -> str:
|
||||
def _build_email_panel(title: str, body_html: str, *, variant: str = "neutral") -> str:
|
||||
styles = {
|
||||
"neutral": {
|
||||
"background": "#101726",
|
||||
"border": "rgba(255,255,255,0.08)",
|
||||
"eyebrow": "#9aa3b8",
|
||||
"text": "#dbe5ff",
|
||||
"background": "#f8fafc",
|
||||
"border": "#d9e2ef",
|
||||
"eyebrow": "#6b778c",
|
||||
"text": "#132033",
|
||||
},
|
||||
"brand": {
|
||||
"background": "#101726",
|
||||
"border": "rgba(59,130,246,0.22)",
|
||||
"eyebrow": "#9dbfff",
|
||||
"text": "#dbe5ff",
|
||||
"background": "#eef4ff",
|
||||
"border": "#bfd2ff",
|
||||
"eyebrow": "#2754b6",
|
||||
"text": "#132033",
|
||||
},
|
||||
"success": {
|
||||
"background": "#122016",
|
||||
"border": "rgba(34,197,94,0.24)",
|
||||
"eyebrow": "#9de7b5",
|
||||
"text": "#d9f9e4",
|
||||
"background": "#edf9f0",
|
||||
"border": "#bfe4c6",
|
||||
"eyebrow": "#1f7a3f",
|
||||
"text": "#132033",
|
||||
},
|
||||
"warning": {
|
||||
"background": "#241814",
|
||||
"border": "rgba(251,146,60,0.34)",
|
||||
"eyebrow": "#fbbd7b",
|
||||
"text": "#ffe0ba",
|
||||
"background": "#fff5ea",
|
||||
"border": "#ffd5a8",
|
||||
"eyebrow": "#c46a10",
|
||||
"text": "#132033",
|
||||
},
|
||||
"danger": {
|
||||
"background": "#251418",
|
||||
"border": "rgba(239,68,68,0.32)",
|
||||
"eyebrow": "#ff9b9b",
|
||||
"text": "#ffd0d0",
|
||||
"background": "#fff0f0",
|
||||
"border": "#f3c1c1",
|
||||
"eyebrow": "#bb2d2d",
|
||||
"text": "#132033",
|
||||
},
|
||||
}.get(variant, {
|
||||
"background": "#101726",
|
||||
"border": "rgba(255,255,255,0.08)",
|
||||
"eyebrow": "#9aa3b8",
|
||||
"text": "#dbe5ff",
|
||||
"background": "#f8fafc",
|
||||
"border": "#d9e2ef",
|
||||
"eyebrow": "#6b778c",
|
||||
"text": "#132033",
|
||||
})
|
||||
return (
|
||||
f"<div style=\"margin:0 0 18px; padding:18px; background:{styles['background']}; "
|
||||
@@ -255,7 +255,7 @@ DEFAULT_TEMPLATES: Dict[str, Dict[str, str]] = {
|
||||
"Build: {{build_number}}\n"
|
||||
),
|
||||
"body_html": (
|
||||
"<div style=\"margin:0 0 20px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
"<div style=\"margin:0 0 20px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
"A new invitation has been prepared for <strong>{{recipient_email}}</strong>. Use the details below to sign up."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
@@ -305,7 +305,7 @@ DEFAULT_TEMPLATES: Dict[str, Dict[str, str]] = {
|
||||
"{{message}}\n"
|
||||
),
|
||||
"body_html": (
|
||||
"<div style=\"margin:0 0 18px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
"<div style=\"margin:0 0 18px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
"Your account is live and ready to use. Everything below mirrors the current site behavior."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
@@ -345,7 +345,7 @@ DEFAULT_TEMPLATES: Dict[str, Dict[str, str]] = {
|
||||
"If you need help, contact the admin.\n"
|
||||
),
|
||||
"body_html": (
|
||||
"<div style=\"margin:0 0 18px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
"<div style=\"margin:0 0 18px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
"Please review this account notice carefully. This message was sent by an administrator."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
@@ -388,7 +388,7 @@ DEFAULT_TEMPLATES: Dict[str, Dict[str, str]] = {
|
||||
"{{message}}\n"
|
||||
),
|
||||
"body_html": (
|
||||
"<div style=\"margin:0 0 18px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
"<div style=\"margin:0 0 18px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
"Your account access has changed. Review the details below."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
@@ -507,13 +507,14 @@ def _looks_like_full_html_document(value: str) -> bool:
|
||||
|
||||
|
||||
def _build_email_action_button(label: str, url: str, *, primary: bool) -> str:
|
||||
background = "linear-gradient(135deg, #ff6b2b 0%, #1c6bff 100%)" if primary else "#151c2d"
|
||||
border = "1px solid rgba(59, 130, 246, 0.32)" if primary else "1px solid rgba(255, 255, 255, 0.12)"
|
||||
color = "#ffffff"
|
||||
background = "linear-gradient(135deg, #ff6b2b 0%, #1c6bff 100%)" if primary else "#ffffff"
|
||||
fallback = "#1c6bff" if primary else "#ffffff"
|
||||
border = "1px solid rgba(28, 107, 255, 0.28)" if primary else "1px solid #d5deed"
|
||||
color = "#ffffff" if primary else "#132033"
|
||||
return (
|
||||
f"<a href=\"{html.escape(url)}\" "
|
||||
f"style=\"display:inline-block; padding:12px 20px; margin:0 12px 12px 0; border-radius:999px; "
|
||||
f"background:{background}; border:{border}; color:{color}; text-decoration:none; font-size:14px; "
|
||||
f"background-color:{fallback}; background:{background}; border:{border}; color:{color}; text-decoration:none; font-size:14px; "
|
||||
f"font-weight:800; letter-spacing:0.01em;\">{html.escape(label)}</a>"
|
||||
)
|
||||
|
||||
@@ -560,39 +561,39 @@ def _wrap_email_html(
|
||||
|
||||
return (
|
||||
"<!doctype html>"
|
||||
"<html><body style=\"margin:0; padding:0; background:#05070d;\">"
|
||||
"<html><body style=\"margin:0; padding:0; background:#eef2f7;\" bgcolor=\"#eef2f7\">"
|
||||
"<div style=\"display:none; max-height:0; overflow:hidden; opacity:0;\">"
|
||||
f"{html.escape(title)} - {html.escape(subtitle)}"
|
||||
"</div>"
|
||||
"<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" "
|
||||
"style=\"width:100%; border-collapse:collapse; background:radial-gradient(circle at top, rgba(17,33,74,0.9) 0%, rgba(8,12,22,1) 55%, #05070d 100%);\">"
|
||||
"<tr><td style=\"padding:32px 18px;\">"
|
||||
"style=\"width:100%; border-collapse:collapse; background:#eef2f7;\" bgcolor=\"#eef2f7\">"
|
||||
"<tr><td style=\"padding:32px 18px;\" bgcolor=\"#eef2f7\">"
|
||||
"<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" "
|
||||
"style=\"max-width:680px; margin:0 auto; border-collapse:collapse;\">"
|
||||
"<tr><td style=\"padding:0 0 18px;\">"
|
||||
f"<div style=\"padding:24px 28px; background:#0b0f18; border:1px solid rgba(255,255,255,0.08); border-radius:28px; box-shadow:0 24px 60px rgba(0,0,0,0.42);\">"
|
||||
f"<div style=\"padding:24px 28px; background:#ffffff; border:1px solid #d5deed; border-radius:28px; box-shadow:0 18px 48px rgba(15,23,42,0.08);\">"
|
||||
"<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;\">"
|
||||
"<tr>"
|
||||
f"<td style=\"vertical-align:middle; width:64px; padding:0 18px 0 0;\">{logo_block}</td>"
|
||||
"<td style=\"vertical-align:middle;\">"
|
||||
f"<div style=\"font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:#9aa3b8; margin-bottom:6px;\">{html.escape(app_name)}</div>"
|
||||
f"<div style=\"font-size:30px; line-height:1.1; font-weight:900; color:#e9ecf5; margin:0 0 6px;\">{html.escape(title)}</div>"
|
||||
f"<div style=\"font-size:15px; line-height:1.6; color:#9aa3b8;\">{html.escape(subtitle or EMAIL_TAGLINE)}</div>"
|
||||
f"<div style=\"font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:#6b778c; margin-bottom:6px;\">{html.escape(app_name)}</div>"
|
||||
f"<div style=\"font-size:30px; line-height:1.1; font-weight:900; color:#132033; margin:0 0 6px;\">{html.escape(title)}</div>"
|
||||
f"<div style=\"font-size:15px; line-height:1.6; color:#5c687d;\">{html.escape(subtitle or EMAIL_TAGLINE)}</div>"
|
||||
"</td>"
|
||||
"</tr>"
|
||||
"</table>"
|
||||
f"<div style=\"height:6px; margin:22px 0 22px; border-radius:999px; background:linear-gradient(90deg, {styles['accent_a']} 0%, {styles['accent_b']} 100%);\"></div>"
|
||||
f"<div style=\"height:6px; margin:22px 0 22px; border-radius:999px; background-color:{styles['accent_b']}; background:linear-gradient(90deg, {styles['accent_a']} 0%, {styles['accent_b']} 100%);\"></div>"
|
||||
f"<div style=\"display:inline-block; padding:7px 12px; margin:0 0 16px; background:{styles['chip_bg']}; "
|
||||
f"border:1px solid {styles['chip_border']}; border-radius:999px; color:{styles['chip_text']}; "
|
||||
"font-size:11px; font-weight:800; letter-spacing:0.14em; text-transform:uppercase;\">"
|
||||
f"{html.escape(EMAIL_TAGLINE)}</div>"
|
||||
f"<div style=\"color:#e9ecf5;\">{body_html}</div>"
|
||||
f"<div style=\"color:#132033;\">{body_html}</div>"
|
||||
f"<div style=\"margin:24px 0 0;\">{actions_html}</div>"
|
||||
"<div style=\"margin:28px 0 0; padding:18px 0 0; border-top:1px solid rgba(255,255,255,0.08);\">"
|
||||
"<div style=\"margin:28px 0 0; padding:18px 0 0; border-top:1px solid #e2e8f0;\">"
|
||||
"<table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;\">"
|
||||
"<tr>"
|
||||
f"<td style=\"font-size:12px; line-height:1.7; color:#9aa3b8;\">{html.escape(footer)}</td>"
|
||||
f"<td style=\"font-size:12px; line-height:1.7; color:#9aa3b8; text-align:right;\">Build {html.escape(build_number)}</td>"
|
||||
f"<td style=\"font-size:12px; line-height:1.7; color:#6b778c;\">{html.escape(footer)}</td>"
|
||||
f"<td style=\"font-size:12px; line-height:1.7; color:#6b778c; text-align:right;\">Build {html.escape(build_number)}</td>"
|
||||
"</tr>"
|
||||
"</table>"
|
||||
"</div>"
|
||||
@@ -1014,7 +1015,7 @@ async def send_test_email(recipient_email: Optional[str] = None) -> Dict[str, st
|
||||
subtitle="This confirms Magent can generate and hand off branded mail.",
|
||||
tone="brand",
|
||||
body_html=(
|
||||
"<div style=\"margin:0 0 18px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
"<div style=\"margin:0 0 18px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
"This is a live test email from Magent. If this renders correctly, the HTML template shell and SMTP handoff are both working."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
@@ -1110,7 +1111,7 @@ async def send_password_reset_email(
|
||||
subtitle=f"This will update the credentials used for {provider_label}.",
|
||||
tone="brand",
|
||||
body_html=(
|
||||
f"<div style=\"margin:0 0 18px; color:#e9ecf5; font-size:15px; line-height:1.7;\">"
|
||||
f"<div style=\"margin:0 0 18px; color:#132033; font-size:15px; line-height:1.7;\">"
|
||||
f"A password reset was requested for <strong>{html.escape(username)}</strong>."
|
||||
"</div>"
|
||||
+ _build_email_stat_grid(
|
||||
|
||||
@@ -2296,14 +2296,6 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="settings-action-button"
|
||||
onClick={() => void saveSettingGroup(sectionGroup)}
|
||||
disabled={sectionSaving[sectionGroup.key] || sectionTesting[sectionGroup.key]}
|
||||
>
|
||||
{sectionSaving[sectionGroup.key] ? 'Saving...' : 'Save section'}
|
||||
</button>
|
||||
{getSectionTestLabel(sectionGroup.key) ? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -2316,6 +2308,14 @@ export default function SettingsPage({ section }: SettingsPageProps) {
|
||||
: getSectionTestLabel(sectionGroup.key)}
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="settings-action-button"
|
||||
onClick={() => void saveSettingGroup(sectionGroup)}
|
||||
disabled={sectionSaving[sectionGroup.key] || sectionTesting[sectionGroup.key]}
|
||||
>
|
||||
{sectionSaving[sectionGroup.key] ? 'Saving...' : 'Save section'}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "magent-frontend",
|
||||
"version": "0303261702",
|
||||
"version": "0303261719",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "magent-frontend",
|
||||
"version": "0303261702",
|
||||
"version": "0303261719",
|
||||
"dependencies": {
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "magent-frontend",
|
||||
"private": true,
|
||||
"version": "0303261702",
|
||||
"version": "0303261719",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
||||
Reference in New Issue
Block a user