feat(emails): subtask 4 — 4 complex templates (lead_forward, match_notify, digest, business_plan)
- Add lead_forward.html (brief table + contact table + optional CTA token link) - Add lead_match_notify.html (new matching lead alert with heat badge) - Add weekly_digest.html (leads table with Jinja2 for loop) - Add business_plan.html (PDF ready notification with download CTA) - Refactor 4 handlers in worker.py: send_lead_forward_email, notify_matching_suppliers, send_weekly_lead_digest, generate_business_plan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
11
web/src/padelnomics/templates/emails/business_plan.html
Normal file
11
web/src/padelnomics/templates/emails/business_plan.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends "emails/_base.html" %}
|
||||
{% from "emails/_macros.html" import email_button %}
|
||||
|
||||
{% block body %}
|
||||
<h2 style="margin:0 0 8px;color:#0F172A;font-size:22px;">{{ t.email_business_plan_heading }}</h2>
|
||||
<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">
|
||||
<p>{{ t.email_business_plan_body }}</p>
|
||||
<p style="font-size:14px;color:#334155;">{{ t.email_business_plan_includes }}</p>
|
||||
{{ email_button(download_url, t.email_business_plan_btn) }}
|
||||
<p style="font-size:13px;color:#64748B;text-align:center;margin:12px 0 0;">{{ t.email_business_plan_quote_cta | tformat(quote_url=quote_url) }}</p>
|
||||
{% endblock %}
|
||||
53
web/src/padelnomics/templates/emails/lead_forward.html
Normal file
53
web/src/padelnomics/templates/emails/lead_forward.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends "emails/_base.html" %}
|
||||
{% from "emails/_macros.html" import email_button, heat_badge, section_heading %}
|
||||
|
||||
{% block body %}
|
||||
{# Yellow urgency banner #}
|
||||
<p style="font-size:13px;color:#334155;margin:0 0 16px;padding:10px 14px;background-color:#FEF3C7;border-radius:6px;border-left:3px solid #F59E0B;">{{ t.email_lead_forward_urgency }}</p>
|
||||
|
||||
<h2 style="margin:0 0 4px;color:#0F172A;font-size:18px;">{{ t.email_lead_forward_heading }} {{ heat_badge(heat) }}</h2>
|
||||
<hr style="border:none;border-top:1px solid #E2E8F0;margin:8px 0 16px;">
|
||||
|
||||
{{ section_heading(t.email_lead_forward_section_brief) }}
|
||||
<table cellpadding="0" cellspacing="0" style="margin-bottom:20px;">
|
||||
{% for label, value in brief_rows %}
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;vertical-align:top;">{{ label }}</td>
|
||||
<td style="padding:4px 0;font-size:13px;color:#1E293B;">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{{ section_heading(t.email_lead_forward_section_contact) }}
|
||||
<table cellpadding="0" cellspacing="0" style="margin-bottom:20px;">
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">{{ t.email_lead_forward_lbl_name }}</td>
|
||||
<td style="padding:4px 0;font-size:14px;color:#0F172A;font-weight:600;">{{ contact_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">{{ t.email_lead_forward_lbl_email }}</td>
|
||||
<td style="padding:4px 0;font-size:14px;"><a href="mailto:{{ contact_email }}" style="color:#1D4ED8;font-weight:600;text-decoration:none;">{{ contact_email }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">{{ t.email_lead_forward_lbl_phone }}</td>
|
||||
<td style="padding:4px 0;font-size:13px;color:#1E293B;">{{ contact_phone }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">{{ t.email_lead_forward_lbl_company }}</td>
|
||||
<td style="padding:4px 0;font-size:13px;color:#1E293B;">{{ contact_company }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">{{ t.email_lead_forward_lbl_role }}</td>
|
||||
<td style="padding:4px 0;font-size:13px;color:#1E293B;">{{ stakeholder_type }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{ email_button(base_url ~ "/suppliers/leads", t.email_lead_forward_btn) }}
|
||||
<p style="font-size:13px;color:#64748B;text-align:center;margin:8px 0 0;">{{ t.email_lead_forward_reply_direct | tformat(contact_email=contact_email) }}</p>
|
||||
|
||||
{%- if cta_url %}
|
||||
<p style="font-size:12px;color:#94A3B8;text-align:center;margin:16px 0 0;">
|
||||
<a href="{{ cta_url }}" style="color:#94A3B8;">✓ Mark as contacted</a>
|
||||
</p>
|
||||
{%- endif %}
|
||||
{% endblock %}
|
||||
30
web/src/padelnomics/templates/emails/lead_match_notify.html
Normal file
30
web/src/padelnomics/templates/emails/lead_match_notify.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends "emails/_base.html" %}
|
||||
{% from "emails/_macros.html" import email_button, heat_badge %}
|
||||
|
||||
{% block body %}
|
||||
<h2 style="margin:0 0 8px;color:#0F172A;font-size:18px;">New [{{ heat }}] lead in {{ country }} {{ heat_badge(heat) }}</h2>
|
||||
<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">
|
||||
<p style="font-size:14px;color:#334155;">A new project brief has been submitted that matches your service area.</p>
|
||||
|
||||
<table cellpadding="0" cellspacing="0" style="margin-bottom:20px;">
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">Facility</td>
|
||||
<td style="font-size:13px;color:#1E293B;">{{ facility_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">Courts</td>
|
||||
<td style="font-size:13px;color:#1E293B;">{{ courts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">Country</td>
|
||||
<td style="font-size:13px;color:#1E293B;">{{ country }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;">Timeline</td>
|
||||
<td style="font-size:13px;color:#1E293B;">{{ timeline or "-" }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="font-size:13px;color:#64748B;">Contact details are available after unlocking. Credits required: {{ credit_cost }}.</p>
|
||||
{{ email_button(base_url ~ "/suppliers/leads", "View lead feed") }}
|
||||
{% endblock %}
|
||||
33
web/src/padelnomics/templates/emails/weekly_digest.html
Normal file
33
web/src/padelnomics/templates/emails/weekly_digest.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "emails/_base.html" %}
|
||||
{% from "emails/_macros.html" import email_button, heat_badge_sm %}
|
||||
|
||||
{% block body %}
|
||||
<h2 style="margin:0 0 8px;color:#0F172A;font-size:18px;">
|
||||
Your weekly lead digest — {{ leads | length }} new {{ "lead" if leads | length == 1 else "leads" }}
|
||||
</h2>
|
||||
<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">
|
||||
<p style="font-size:14px;color:#334155;">New matching leads in your service area this week:</p>
|
||||
|
||||
<table cellpadding="0" cellspacing="0" style="margin-bottom:20px;width:100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left;font-size:11px;color:#94A3B8;padding:0 12px 6px 0;text-transform:uppercase;">Project</th>
|
||||
<th style="text-align:left;font-size:11px;color:#94A3B8;padding:0 12px 6px 0;text-transform:uppercase;">Country</th>
|
||||
<th style="text-align:left;font-size:11px;color:#94A3B8;text-transform:uppercase;">Timeline</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for lead in leads %}
|
||||
<tr>
|
||||
<td style="padding:6px 12px 6px 0;font-size:13px;color:#1E293B;">
|
||||
{{ heat_badge_sm(lead.heat | upper) }} {{ lead.facility_type or "Padel" }}, {{ lead.court_count or "?" }} courts
|
||||
</td>
|
||||
<td style="padding:6px 12px 6px 0;font-size:13px;color:#64748B;">{{ lead.country or "-" }}</td>
|
||||
<td style="padding:6px 0;font-size:13px;color:#64748B;">{{ lead.timeline or "-" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ email_button(base_url ~ "/suppliers/leads", "Unlock leads →") }}
|
||||
{% endblock %}
|
||||
@@ -384,15 +384,6 @@ async def handle_send_lead_forward_email(payload: dict) -> None:
|
||||
|
||||
subject = f"[{heat}] New padel project in {country} \u2014 {courts} courts, \u20ac{budget}"
|
||||
|
||||
# Heat badge color
|
||||
heat_colors = {"HOT": "#DC2626", "WARM": "#EA580C", "COOL": "#2563EB"}
|
||||
heat_bg = heat_colors.get(heat, "#2563EB")
|
||||
heat_badge = (
|
||||
f'<span style="display:inline-block;padding:2px 8px;border-radius:4px;'
|
||||
f'background-color:{heat_bg};color:#FFFFFF;font-size:11px;font-weight:700;'
|
||||
f'letter-spacing:0.04em;vertical-align:middle;margin-left:8px;">{heat}</span>'
|
||||
)
|
||||
|
||||
tl = lambda key: _t(key, lang) # noqa: E731
|
||||
|
||||
brief_rows = [
|
||||
@@ -405,50 +396,11 @@ async def handle_send_lead_forward_email(payload: dict) -> None:
|
||||
(tl("email_lead_forward_lbl_additional"), lead["additional_info"] or "-"),
|
||||
]
|
||||
|
||||
brief_html = ""
|
||||
for label, value in brief_rows:
|
||||
brief_html += (
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px;vertical-align:top">{label}</td>'
|
||||
f'<td style="padding:4px 0;font-size:13px;color:#1E293B">{value}</td></tr>'
|
||||
)
|
||||
|
||||
contact_name = lead["contact_name"] or "-"
|
||||
contact_phone = lead["contact_phone"] or "-"
|
||||
|
||||
# Contact section with prominent email
|
||||
contact_html = (
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">{tl("email_lead_forward_lbl_name")}</td>'
|
||||
f'<td style="padding:4px 0;font-size:14px;color:#0F172A;font-weight:600">{contact_name}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">{tl("email_lead_forward_lbl_email")}</td>'
|
||||
f'<td style="padding:4px 0;font-size:14px;"><a href="mailto:{contact_email}" style="color:#1D4ED8;font-weight:600;text-decoration:none;">{contact_email}</a></td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">{tl("email_lead_forward_lbl_phone")}</td>'
|
||||
f'<td style="padding:4px 0;font-size:13px;color:#1E293B">{contact_phone}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">{tl("email_lead_forward_lbl_company")}</td>'
|
||||
f'<td style="padding:4px 0;font-size:13px;color:#1E293B">{lead["contact_company"] or "-"}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">{tl("email_lead_forward_lbl_role")}</td>'
|
||||
f'<td style="padding:4px 0;font-size:13px;color:#1E293B">{lead["stakeholder_type"] or "-"}</td></tr>'
|
||||
)
|
||||
|
||||
preheader_parts = [f"{facility_type} project"]
|
||||
if timeline:
|
||||
preheader_parts.append(f"{timeline} timeline")
|
||||
preheader_parts.append(_t("email_lead_forward_preheader_suffix", lang))
|
||||
|
||||
body = (
|
||||
f'<p style="font-size:13px;color:#334155;margin:0 0 16px;padding:10px 14px;'
|
||||
f'background-color:#FEF3C7;border-radius:6px;border-left:3px solid #F59E0B;">'
|
||||
f'{_t("email_lead_forward_urgency", lang)}</p>'
|
||||
f'<h2 style="margin:0 0 4px;color:#0F172A;font-size:18px;">{tl("email_lead_forward_heading")} {heat_badge}</h2>'
|
||||
f'<hr style="border:none;border-top:1px solid #E2E8F0;margin:8px 0 16px;">'
|
||||
f'<h3 style="font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:#94A3B8;margin:0 0 8px">{tl("email_lead_forward_section_brief")}</h3>'
|
||||
f'<table cellpadding="0" cellspacing="0" style="margin-bottom:20px">{brief_html}</table>'
|
||||
f'<h3 style="font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:#94A3B8;margin:0 0 8px">{tl("email_lead_forward_section_contact")}</h3>'
|
||||
f'<table cellpadding="0" cellspacing="0" style="margin-bottom:20px">{contact_html}</table>'
|
||||
f'{_email_button(f"{config.BASE_URL}/suppliers/leads", tl("email_lead_forward_btn"))}'
|
||||
f'<p style="font-size:13px;color:#64748B;text-align:center;margin:8px 0 0;">'
|
||||
f'{_t("email_lead_forward_reply_direct", lang, contact_email=contact_email)}</p>'
|
||||
)
|
||||
|
||||
# Send to supplier contact email or general contact
|
||||
to_email = supplier.get("contact_email") or supplier.get("contact") or ""
|
||||
if not to_email:
|
||||
@@ -458,16 +410,25 @@ async def handle_send_lead_forward_email(payload: dict) -> None:
|
||||
# Generate one-click "I've contacted this lead" CTA token
|
||||
cta_token = secrets.token_urlsafe(24)
|
||||
cta_url = f"{config.BASE_URL}/suppliers/leads/cta/{cta_token}"
|
||||
body += (
|
||||
f'<p style="font-size:12px;color:#94A3B8;text-align:center;margin:16px 0 0;">'
|
||||
f'<a href="{cta_url}" style="color:#94A3B8;">'
|
||||
f'✓ Mark as contacted</a></p>'
|
||||
|
||||
html = render_email_template(
|
||||
"emails/lead_forward.html",
|
||||
lang=lang,
|
||||
heat=heat,
|
||||
brief_rows=brief_rows,
|
||||
contact_name=lead["contact_name"] or "-",
|
||||
contact_email=contact_email,
|
||||
contact_phone=lead["contact_phone"] or "-",
|
||||
contact_company=lead["contact_company"] or "-",
|
||||
stakeholder_type=lead["stakeholder_type"] or "-",
|
||||
cta_url=cta_url,
|
||||
preheader=", ".join(preheader_parts),
|
||||
)
|
||||
|
||||
await send_email(
|
||||
to=to_email,
|
||||
subject=subject,
|
||||
html=_email_wrap(body, lang, preheader=", ".join(preheader_parts)),
|
||||
html=html,
|
||||
from_addr=EMAIL_ADDRESSES["leads"],
|
||||
email_type="lead_forward",
|
||||
)
|
||||
@@ -549,30 +510,22 @@ async def handle_notify_matching_suppliers(payload: dict) -> None:
|
||||
if not to_email:
|
||||
continue
|
||||
|
||||
body = (
|
||||
f'<h2 style="margin:0 0 8px;color:#0F172A;font-size:18px;">'
|
||||
f'New [{heat}] lead in {country}</h2>'
|
||||
f'<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">'
|
||||
f'<p style="font-size:14px;color:#334155;">A new project brief has been submitted that matches your service area.</p>'
|
||||
f'<table cellpadding="0" cellspacing="0" style="margin-bottom:20px">'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">Facility</td>'
|
||||
f'<td style="font-size:13px;color:#1E293B">{facility_type}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">Courts</td>'
|
||||
f'<td style="font-size:13px;color:#1E293B">{courts}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">Country</td>'
|
||||
f'<td style="font-size:13px;color:#1E293B">{country}</td></tr>'
|
||||
f'<tr><td style="padding:4px 12px 4px 0;color:#94A3B8;font-size:13px">Timeline</td>'
|
||||
f'<td style="font-size:13px;color:#1E293B">{timeline or "-"}</td></tr>'
|
||||
f'</table>'
|
||||
f'<p style="font-size:13px;color:#64748B;">'
|
||||
f'Contact details are available after unlocking. Credits required: {lead.get("credit_cost", "?")}.</p>'
|
||||
f'{_email_button(f"{config.BASE_URL}/suppliers/leads", "View lead feed")}'
|
||||
notify_html = render_email_template(
|
||||
"emails/lead_match_notify.html",
|
||||
lang=lang,
|
||||
heat=heat,
|
||||
country=country,
|
||||
facility_type=facility_type,
|
||||
courts=courts,
|
||||
timeline=timeline,
|
||||
credit_cost=lead.get("credit_cost", "?"),
|
||||
preheader=f"New matching lead in {country}",
|
||||
)
|
||||
|
||||
await send_email(
|
||||
to=to_email,
|
||||
subject=f"[{heat}] New {facility_type} project in {country} — {courts} courts",
|
||||
html=_email_wrap(body, lang, preheader=f"New matching lead in {country}"),
|
||||
html=notify_html,
|
||||
from_addr=EMAIL_ADDRESSES["leads"],
|
||||
email_type="lead_match_notify",
|
||||
)
|
||||
@@ -617,48 +570,27 @@ async def handle_send_weekly_lead_digest(payload: dict) -> None:
|
||||
if not new_leads:
|
||||
continue
|
||||
|
||||
lead_rows_html = ""
|
||||
for ld in new_leads:
|
||||
heat = (ld["heat_score"] or "cool").upper()
|
||||
heat_colors = {"HOT": "#DC2626", "WARM": "#EA580C", "COOL": "#2563EB"}
|
||||
hc = heat_colors.get(heat, "#2563EB")
|
||||
badge = (
|
||||
f'<span style="display:inline-block;padding:1px 6px;border-radius:4px;'
|
||||
f'background-color:{hc};color:#fff;font-size:10px;font-weight:700">{heat}</span>'
|
||||
)
|
||||
lead_rows_html += (
|
||||
f'<tr>'
|
||||
f'<td style="padding:6px 12px 6px 0;font-size:13px;color:#1E293B">'
|
||||
f'{badge} {ld["facility_type"] or "Padel"}, {ld["court_count"] or "?"} courts</td>'
|
||||
f'<td style="padding:6px 12px 6px 0;font-size:13px;color:#64748B">{ld["country"] or "-"}</td>'
|
||||
f'<td style="padding:6px 0;font-size:13px;color:#64748B">{ld["timeline"] or "-"}</td>'
|
||||
f'</tr>'
|
||||
)
|
||||
|
||||
body = (
|
||||
f'<h2 style="margin:0 0 8px;color:#0F172A;font-size:18px;">'
|
||||
f'Your weekly lead digest — {len(new_leads)} new {"lead" if len(new_leads) == 1 else "leads"}</h2>'
|
||||
f'<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">'
|
||||
f'<p style="font-size:14px;color:#334155;">New matching leads in your service area this week:</p>'
|
||||
f'<table cellpadding="0" cellspacing="0" style="margin-bottom:20px;width:100%">'
|
||||
f'<thead><tr>'
|
||||
f'<th style="text-align:left;font-size:11px;color:#94A3B8;padding:0 12px 6px 0;text-transform:uppercase">Project</th>'
|
||||
f'<th style="text-align:left;font-size:11px;color:#94A3B8;padding:0 12px 6px 0;text-transform:uppercase">Country</th>'
|
||||
f'<th style="text-align:left;font-size:11px;color:#94A3B8;text-transform:uppercase">Timeline</th>'
|
||||
f'</tr></thead>'
|
||||
f'<tbody>{lead_rows_html}</tbody>'
|
||||
f'</table>'
|
||||
f'{_email_button(f"{config.BASE_URL}/suppliers/leads", "Unlock leads →")}'
|
||||
)
|
||||
# Normalise lead dicts for template — heat_score → heat (uppercase)
|
||||
digest_leads = [
|
||||
{**ld, "heat": (ld["heat_score"] or "cool").upper()}
|
||||
for ld in new_leads
|
||||
]
|
||||
|
||||
area_summary = ", ".join(countries[:3])
|
||||
if len(countries) > 3:
|
||||
area_summary += f" +{len(countries) - 3}"
|
||||
|
||||
digest_html = render_email_template(
|
||||
"emails/weekly_digest.html",
|
||||
lang="en",
|
||||
leads=digest_leads,
|
||||
preheader=f"{len(new_leads)} new leads matching your service area",
|
||||
)
|
||||
|
||||
await send_email(
|
||||
to=to_email,
|
||||
subject=f"{len(new_leads)} new padel {'lead' if len(new_leads) == 1 else 'leads'} in {area_summary}",
|
||||
html=_email_wrap(body, "en", preheader=f"{len(new_leads)} new leads matching your service area"),
|
||||
html=digest_html,
|
||||
from_addr=EMAIL_ADDRESSES["leads"],
|
||||
email_type="weekly_digest",
|
||||
)
|
||||
@@ -776,19 +708,17 @@ async def handle_generate_business_plan(payload: dict) -> None:
|
||||
export_token = export_row["token"]
|
||||
user = await fetch_one("SELECT email FROM users WHERE id = ?", (user_id,))
|
||||
if user:
|
||||
body = (
|
||||
f'<h2 style="margin:0 0 8px;color:#0F172A;font-size:22px;">{_t("email_business_plan_heading", language)}</h2>'
|
||||
f'<hr style="border:none;border-top:1px solid #E2E8F0;margin:0 0 16px;">'
|
||||
f'<p>{_t("email_business_plan_body", language)}</p>'
|
||||
f'<p style="font-size:14px;color:#334155;">{_t("email_business_plan_includes", language)}</p>'
|
||||
f'{_email_button(f"{config.BASE_URL}/planner/export/{export_token}", _t("email_business_plan_btn", language))}'
|
||||
f'<p style="font-size:13px;color:#64748B;text-align:center;margin:12px 0 0;">'
|
||||
f'{_t("email_business_plan_quote_cta", language, quote_url=f"{config.BASE_URL}/{language}/leads/quote")}</p>'
|
||||
bp_html = render_email_template(
|
||||
"emails/business_plan.html",
|
||||
lang=language,
|
||||
download_url=f"{config.BASE_URL}/planner/export/{export_token}",
|
||||
quote_url=f"{config.BASE_URL}/{language}/leads/quote",
|
||||
preheader=_t("email_business_plan_preheader", language),
|
||||
)
|
||||
await send_email(
|
||||
to=user["email"],
|
||||
subject=_t("email_business_plan_subject", language),
|
||||
html=_email_wrap(body, language, preheader=_t("email_business_plan_preheader", language)),
|
||||
html=bp_html,
|
||||
from_addr=EMAIL_ADDRESSES["transactional"],
|
||||
email_type="business_plan",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user