Skip to content

Commit e602b44

Browse files
Merge pull request #103 from goldlabelapps/staging
Add CTA support to email resend and template
2 parents 27b630d + 9f90744 commit e602b44

3 files changed

Lines changed: 205 additions & 36 deletions

File tree

app/api/notify/resend.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ def root() -> dict:
3939
"type": "string",
4040
"required": True,
4141
"description": "HTML content of the email."
42+
},
43+
"cta_label": {
44+
"type": "string",
45+
"required": False,
46+
"description": "Optional CTA button label. Defaults to 'Call To Action'."
47+
},
48+
"cta_url": {
49+
"type": "string",
50+
"required": False,
51+
"description": "Optional CTA URL. Defaults to the website base URL."
4252
}
4353
}
4454
}
@@ -53,6 +63,8 @@ class EmailRequest(BaseModel):
5363
to: EmailStr
5464
subject: str
5565
html: str
66+
cta_label: str | None = None
67+
cta_url: str | None = None
5668

5769
@router.post("/resend", status_code=status.HTTP_202_ACCEPTED)
5870
def send_email(request: EmailRequest):
@@ -63,7 +75,12 @@ def send_email(request: EmailRequest):
6375
result = send_email_resend(
6476
to=request.to,
6577
subject=request.subject,
66-
html=goldlabel_email(request.subject, request.html),
78+
html=goldlabel_email(
79+
request.subject,
80+
request.html,
81+
cta_label=request.cta_label or "Call To Action",
82+
cta_url=request.cta_url or "https://goldlabel.pro",
83+
),
6784
)
6885
if "error" in result:
6986
meta = make_meta("error", result["error"])
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""Goldlabel branded HTML email template."""
2+
3+
_LOGO_URL = "https://goldlabel.pro/goldlabelpro/png/favicon.png"
4+
5+
# Palette (dark theme used for the email chrome; body text stays readable on white clients)
6+
_DARK_BG = "#364450"
7+
_DARK_PAPER = "#364450"
8+
_DARK_PRIMARY = "#ffd849"
9+
_DARK_TEXT = "#ffffff"
10+
_LIGHT_BG = "#eaf0f5"
11+
_LIGHT_PAPER = "#EEF7FF"
12+
_LIGHT_TEXT = "#000000"
13+
_LIGHT_PRIMARY = "#364450"
14+
15+
16+
def goldlabel_email(subject: str, body_html: str) -> str:
17+
"""Return a complete HTML email string with Goldlabel branding.
18+
19+
Args:
20+
subject: Used as the visible heading inside the email.
21+
body_html: Inner HTML content placed in the message body area.
22+
23+
Returns:
24+
A self-contained HTML string ready to pass to send_email_resend().
25+
"""
26+
return f"""<!DOCTYPE html>
27+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
28+
<head>
29+
<meta charset="UTF-8" />
30+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
31+
<meta name="color-scheme" content="light dark" />
32+
<meta name="supported-color-schemes" content="light dark" />
33+
<title>{subject}</title>
34+
<style>
35+
/* ── Reset ─────────────────────────────────────── */
36+
body, table, td, a {{ -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }}
37+
table, td {{ mso-table-lspace: 0pt; mso-table-rspace: 0pt; }}
38+
img {{ -ms-interpolation-mode: bicubic; border: 0; outline: none; text-decoration: none; display: block; }}
39+
40+
/* ── Base ──────────────────────────────────────── */
41+
body {{
42+
margin: 0; padding: 0;
43+
background-color: {_LIGHT_BG};
44+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
45+
color: {_LIGHT_TEXT};
46+
}}
47+
48+
/* ── Dark-mode overrides ───────────────────────── */
49+
@media (prefers-color-scheme: dark) {{
50+
body, .email-wrapper {{ background-color: {_DARK_BG} !important; color: {_DARK_TEXT} !important; }}
51+
.email-card {{ background-color: {_DARK_PAPER} !important; color: {_DARK_TEXT} !important; }}
52+
.email-header {{ background-color: {_DARK_BG} !important; }}
53+
.email-footer {{ background-color: {_DARK_BG} !important; color: {_DARK_TEXT} !important; }}
54+
.email-subject {{ color: {_DARK_PRIMARY} !important; }}
55+
a {{ color: {_DARK_PRIMARY} !important; }}
56+
}}
57+
</style>
58+
</head>
59+
<body class="email-wrapper">
60+
<!-- Outer wrapper -->
61+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"
62+
style="background-color:{_LIGHT_BG}; padding: 32px 0;">
63+
<tr>
64+
<td align="center">
65+
66+
<!-- Card -->
67+
<table role="presentation" class="email-card" width="600" cellpadding="0" cellspacing="0"
68+
style="max-width:600px; width:100%; background-color:{_LIGHT_PAPER};
69+
border-radius:8px; overflow:hidden;
70+
box-shadow: 0 2px 8px rgba(0,0,0,0.10);">
71+
72+
<!-- Header -->
73+
<tr>
74+
<td class="email-header" align="center"
75+
style="background-color:{_DARK_BG}; padding: 28px 40px;">
76+
<img src="{_LOGO_URL}"
77+
width="48" height="48"
78+
alt="Goldlabel"
79+
style="width:48px; height:48px; border-radius:8px;" />
80+
</td>
81+
</tr>
82+
83+
<!-- Subject banner -->
84+
<tr>
85+
<td align="left"
86+
style="padding: 28px 40px 0 40px;">
87+
<h1 class="email-subject"
88+
style="margin:0; font-size:22px; font-weight:700;
89+
color:{_LIGHT_PRIMARY}; line-height:1.3;">
90+
{subject}
91+
</h1>
92+
</td>
93+
</tr>
94+
95+
<!-- Body -->
96+
<tr>
97+
<td align="left"
98+
style="padding: 20px 40px 32px 40px;
99+
font-size:15px; line-height:1.7; color:{_LIGHT_TEXT};">
100+
{body_html}
101+
</td>
102+
</tr>
103+
104+
<!-- Divider -->
105+
<tr>
106+
<td style="padding: 0 40px;">
107+
<hr style="border:none; border-top:1px solid {_DARK_PRIMARY}; margin:0;" />
108+
</td>
109+
</tr>
110+
111+
<!-- Footer -->
112+
<tr>
113+
<td class="email-footer" align="center"
114+
style="padding: 20px 40px;
115+
font-size:12px; color:#666666;">
116+
<a href="https://goldlabel.pro"
117+
style="color:{_LIGHT_PRIMARY}; text-decoration:none;">goldlabel.pro</a>
118+
&nbsp;&middot;&nbsp;
119+
You received this email because a request was made via the NX° API.
120+
</td>
121+
</tr>
122+
123+
</table>
124+
<!-- /Card -->
125+
126+
</td>
127+
</tr>
128+
</table>
129+
</body>
130+
</html>"""

app/utils/email_templates/goldlabel.py

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
"""Goldlabel branded HTML email template."""
22

33
_LOGO_URL = "https://goldlabel.pro/goldlabelpro/png/favicon.png"
4+
_BASE_URL = "https://goldlabel.pro"
45

56
# Palette (dark theme used for the email chrome; body text stays readable on white clients)
67
_DARK_BG = "#364450"
78
_DARK_PAPER = "#364450"
89
_DARK_PRIMARY = "#ffd849"
910
_DARK_TEXT = "#ffffff"
10-
_LIGHT_BG = "#eaf0f5"
11-
_LIGHT_PAPER = "#EEF7FF"
11+
_LIGHT_PAPER = "#ffffff"
1212
_LIGHT_TEXT = "#000000"
1313
_LIGHT_PRIMARY = "#364450"
1414

1515

16-
def goldlabel_email(subject: str, body_html: str) -> str:
16+
def goldlabel_email(
17+
subject: str,
18+
body_html: str,
19+
cta_label: str = "Call To Action",
20+
cta_url: str = _BASE_URL,
21+
) -> str:
1722
"""Return a complete HTML email string with Goldlabel branding.
1823
1924
Args:
2025
subject: Used as the visible heading inside the email.
2126
body_html: Inner HTML content placed in the message body area.
27+
cta_label: Label rendered in the full-width call-to-action button.
28+
cta_url: Destination URL for the call-to-action button.
2229
2330
Returns:
2431
A self-contained HTML string ready to pass to send_email_resend().
@@ -40,83 +47,98 @@ def goldlabel_email(subject: str, body_html: str) -> str:
4047
/* ── Base ──────────────────────────────────────── */
4148
body {{
4249
margin: 0; padding: 0;
43-
background-color: {_LIGHT_BG};
50+
background-color: #ffffff;
4451
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
4552
color: {_LIGHT_TEXT};
4653
}}
4754
4855
/* ── Dark-mode overrides ───────────────────────── */
4956
@media (prefers-color-scheme: dark) {{
50-
body, .email-wrapper {{ background-color: {_DARK_BG} !important; color: {_DARK_TEXT} !important; }}
57+
body, .email-wrapper {{ background-color: #ffffff !important; color: {_DARK_TEXT} !important; }}
5158
.email-card {{ background-color: {_DARK_PAPER} !important; color: {_DARK_TEXT} !important; }}
52-
.email-header {{ background-color: {_DARK_BG} !important; }}
59+
.email-header-inner {{ background-color: {_DARK_BG} !important; }}
5360
.email-footer {{ background-color: {_DARK_BG} !important; color: {_DARK_TEXT} !important; }}
5461
.email-subject {{ color: {_DARK_PRIMARY} !important; }}
62+
.email-cta {{ background-color: {_DARK_PRIMARY} !important; color: {_DARK_BG} !important; }}
5563
a {{ color: {_DARK_PRIMARY} !important; }}
5664
}}
5765
</style>
5866
</head>
5967
<body class="email-wrapper">
6068
<!-- Outer wrapper -->
6169
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"
62-
style="background-color:{_LIGHT_BG}; padding: 32px 0;">
70+
style="padding: 0;">
6371
<tr>
6472
<td align="center">
6573
6674
<!-- Card -->
6775
<table role="presentation" class="email-card" width="600" cellpadding="0" cellspacing="0"
6876
style="max-width:600px; width:100%; background-color:{_LIGHT_PAPER};
69-
border-radius:8px; overflow:hidden;
70-
box-shadow: 0 2px 8px rgba(0,0,0,0.10);">
77+
border-radius:6px; overflow:hidden;
78+
box-shadow:none;">
7179
7280
<!-- Header -->
7381
<tr>
74-
<td class="email-header" align="center"
75-
style="background-color:{_DARK_BG}; padding: 28px 40px;">
76-
<img src="{_LOGO_URL}"
77-
width="48" height="48"
78-
alt="Goldlabel"
79-
style="width:48px; height:48px; border-radius:8px;" />
80-
</td>
81-
</tr>
82-
83-
<!-- Subject banner -->
84-
<tr>
85-
<td align="left"
86-
style="padding: 28px 40px 0 40px;">
87-
<h1 class="email-subject"
88-
style="margin:0; font-size:22px; font-weight:700;
89-
color:{_LIGHT_PRIMARY}; line-height:1.3;">
90-
{subject}
91-
</h1>
82+
<td class="email-header" align="left"
83+
style="padding:24px 40px 0 40px;">
84+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" class="email-header-inner"
85+
style="background-color:{_LIGHT_PRIMARY}; border-radius:6px;">
86+
<tr>
87+
<td align="left" valign="middle" style="padding:20px 16px 20px 20px; width:1%; white-space:nowrap;">
88+
<a href="{_BASE_URL}" style="text-decoration:none; display:inline-block;">
89+
<img src="{_LOGO_URL}"
90+
width="48" height="48"
91+
alt="Goldlabel"
92+
style="width:48px; height:48px; border-radius:6px;" />
93+
</a>
94+
</td>
95+
<td align="left" valign="middle" style="padding:20px 20px 20px 0;">
96+
<h1 class="email-subject"
97+
style="margin:0; font-size:22px; font-weight:700;
98+
color:#ffffff; line-height:1.3;">
99+
{subject}
100+
</h1>
101+
</td>
102+
</tr>
103+
</table>
92104
</td>
93105
</tr>
94106
95107
<!-- Body -->
96108
<tr>
97109
<td align="left"
98-
style="padding: 20px 40px 32px 40px;
110+
style="padding: 30px 40px 24px 40px;
99111
font-size:15px; line-height:1.7; color:{_LIGHT_TEXT};">
100112
{body_html}
101113
</td>
102114
</tr>
103115
104-
<!-- Divider -->
116+
<!-- CTA -->
105117
<tr>
106-
<td style="padding: 0 40px;">
107-
<hr style="border:none; border-top:1px solid {_DARK_PRIMARY}; margin:0;" />
118+
<td align="left" style="padding: 0 40px 28px 40px;">
119+
<a class="email-cta"
120+
href="{cta_url}"
121+
style="display:block; width:100%; box-sizing:border-box;
122+
background-color:{_LIGHT_PRIMARY}; color:#ffffff;
123+
font-size:16px; font-weight:700; text-align:center;
124+
text-decoration:none; border-radius:6px;
125+
padding:14px 20px;">
126+
{cta_label}
127+
</a>
108128
</td>
109129
</tr>
110130
131+
111132
<!-- Footer -->
112133
<tr>
113134
<td class="email-footer" align="center"
114135
style="padding: 20px 40px;
115136
font-size:12px; color:#666666;">
116-
<a href="https://goldlabel.pro"
117-
style="color:{_LIGHT_PRIMARY}; text-decoration:none;">goldlabel.pro</a>
118-
&nbsp;&middot;&nbsp;
119-
You received this email because a request was made via the NX° API.
137+
<a href="https://github.com/goldlabelapps/python"
138+
style="color:{_LIGHT_PRIMARY}; text-decoration:none;">
139+
Sent with Python°
140+
</a>
141+
120142
</td>
121143
</tr>
122144

0 commit comments

Comments
 (0)