feat(campaigns): apply real TARGO brand + auto-route FR/EN by Customer.language
Brand audit against the official guide (Feb 2026 v1.0) caught several inconsistencies in the email template: - Wrong primary green: was #019547, should be #00C853 (Targo Green from brand palette). Globally replaced. - Wrong gradient: was #019547→#06a04d, should be 135deg #00C853→#005026 (the official Gradient Targo from the brand). Now using Outlook-safe background-image + bgcolor fallback for solid green on Outlook desktop. - Wrong contact info: facturation@targointernet.com / 514 242-1500 → support@targo.ca / 514 448-0773 / 1 855 888-2746 (per §11 of guide). - Wrong website: targointernet.com + gigafibre.ca → www.targo.ca. - Missing slogan + green dot: footer now ends with the trademark tagline "Services de confiance, tout-en-un, près de chez vous." with the obligatory green period (always FR — it's the trademark, not a marketing line, so stays untranslated in EN template too). - Missing brand fonts: added Space Grotesk (display) + Plus Jakarta Sans (body) via Google Fonts. Wrapped in MSO conditional comments so Outlook desktop skips the request and falls back to Helvetica via the explicit font-family stack on every element. - Wrong body bg / text colors: now #F5FAF7 (Muted) / #1B2E24 (Foreground) per brand semantic palette. - Wrong info-pill bg: was #f3f4f3 → #F5FAF7 (Muted). - Added official dark footer band #1C1E26 (Targo Dark) with white inverted wordmark, slogan, address, copyright. Multilang routing (FR/EN): - lib/campaigns.js matchCustomer now fetches Customer.language (14k FR / 1k EN distribution confirmed on prod). Default 'fr' for unmatched contacts. - New templateForLanguage(lang) helper picks gift-email-<lang>.html, falls back to FR. Resolves 'fr-CA' → 'fr' etc. - sendCampaignAsync pre-loads templates per recipient with an in-memory cache to avoid re-reading from disk on every send. - gift-email-en.html created — English translation of the full FR template, keeping the slogan in French (it's the trademark tagline). - year variable now injected (replaces hardcoded © year). UI (CampaignNewPage): - New "Langue" column in the Step 2 recipient table. Shows a clickable chip (FR primary green / EN blue-grey) that toggles language inline, so a campaign manager can override the ERPNext-resolved language per recipient. - Step 3 recap now shows "Répartition par langue: 145 × FR, 12 × EN" before confirming the send. Spell-check: - TemplateEditorPage HTML mode now has spellcheck="true" + dynamic lang attribute on the textarea, picked from the template name suffix (gift-email-fr → fr, gift-email-en → en). Browser's native dictionary flags typos in real time. AI-grade rewrites deferred to the future /campaigns/ai/rewrite endpoint discussed previously. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
8d9e190c21
commit
d6096fe1f8
|
|
@ -150,6 +150,21 @@
|
|||
</a>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-language="props">
|
||||
<q-td :props="props">
|
||||
<!-- Clickable chip — toggles FR ↔ EN inline. Default value comes
|
||||
from the matched Customer.language (or 'fr' for unmatched).
|
||||
The send-worker picks the template by this value at send
|
||||
time, so flipping here changes which template gets used. -->
|
||||
<q-chip dense clickable size="sm"
|
||||
:color="props.row.language === 'en' ? 'blue-grey-6' : 'primary'"
|
||||
text-color="white"
|
||||
:label="(props.row.language || 'fr').toUpperCase()"
|
||||
@click="props.row.language = props.row.language === 'en' ? 'fr' : 'en'">
|
||||
<q-tooltip>Cliquer pour basculer FR ↔ EN</q-tooltip>
|
||||
</q-chip>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-match="props">
|
||||
<q-td :props="props">
|
||||
<q-chip v-if="props.row.customer_id" dense color="positive" text-color="white" size="sm" :label="props.row.match_method" />
|
||||
|
|
@ -212,6 +227,7 @@
|
|||
<q-list dense>
|
||||
<q-item><q-item-section side>Nom</q-item-section><q-item-section>{{ params.name }}</q-item-section></q-item>
|
||||
<q-item><q-item-section side>Destinataires actifs</q-item-section><q-item-section>{{ sendableCount }} (sur {{ recipients.length }} paires ; {{ excludedCount }} exclus, {{ unpairedContacts.length }} sans lien)</q-item-section></q-item>
|
||||
<q-item><q-item-section side>Répartition par langue</q-item-section><q-item-section>{{ langBreakdown }}</q-item-section></q-item>
|
||||
<q-item><q-item-section side>Montant affiché</q-item-section><q-item-section>{{ params.amount }}</q-item-section></q-item>
|
||||
<q-item><q-item-section side>Engagement</q-item-section><q-item-section>{{ params.commitment_months }} mois</q-item-section></q-item>
|
||||
<q-item><q-item-section side>Sujet</q-item-section><q-item-section>{{ params.subject }}</q-item-section></q-item>
|
||||
|
|
@ -285,7 +301,8 @@ const recipientColumns = [
|
|||
{ name: 'email', label: 'Email', field: 'email', align: 'left' },
|
||||
{ name: 'civic_address', label: 'Adresse', field: 'civic_address', align: 'left' },
|
||||
{ name: 'gift_url', label: 'Lien-cadeau', field: 'gift_url', align: 'left' },
|
||||
{ name: 'match', label: 'Match client', field: 'match_method', align: 'left' },
|
||||
{ name: 'language', label: 'Langue', field: 'language', align: 'left' },
|
||||
{ name: 'match', label: 'Match', field: 'match_method', align: 'left' },
|
||||
{ name: 'customer_name', label: 'Client ERPNext', field: 'customer_name', align: 'left' },
|
||||
{ name: 'actions', label: '', field: '', align: 'right' },
|
||||
]
|
||||
|
|
@ -304,6 +321,19 @@ const unmatchedCount = computed(() => recipients.value.filter(r => !r.customer_i
|
|||
const excludedCount = computed(() => recipients.value.filter(r => r.excluded).length)
|
||||
// Net number of emails that will actually be fired off (paired AND not excluded)
|
||||
const sendableCount = computed(() => recipients.value.filter(r => !r.excluded && r.gift_url).length)
|
||||
|
||||
// FR / EN breakdown of the sendable recipients — useful preview before launch
|
||||
// so the user knows which template will actually be used and how many.
|
||||
const langBreakdown = computed(() => {
|
||||
const counts = {}
|
||||
for (const r of recipients.value) {
|
||||
if (r.excluded || !r.gift_url) continue
|
||||
const lang = (r.language || 'fr').toLowerCase()
|
||||
counts[lang] = (counts[lang] || 0) + 1
|
||||
}
|
||||
const parts = Object.entries(counts).map(([l, n]) => `${n} × ${l.toUpperCase()}`)
|
||||
return parts.length ? parts.join(', ') : '—'
|
||||
})
|
||||
const estimatedMinutes = computed(() => {
|
||||
const per = (params.value.throttle_ms || 600) / 1000
|
||||
return Math.max(1, Math.round((sendableCount.value * per) / 60))
|
||||
|
|
|
|||
|
|
@ -50,7 +50,13 @@
|
|||
Blocs conditionnels : <code>{{#expiry}}...{{/expiry}}</code>.
|
||||
</span>
|
||||
</q-banner>
|
||||
<!-- lang+spellcheck attributes turn on the browser's native spell-checker.
|
||||
Red squiggles under typos in real time. Language follows the current
|
||||
template suffix (-fr → fr, -en → en) so Chrome/Safari use the right
|
||||
dictionary. For AI-grade rewriting (grammar, tone) plug the future
|
||||
/campaigns/ai/rewrite endpoint into a separate button. -->
|
||||
<q-input v-model="html" type="textarea" outlined dense filled
|
||||
:input-attrs="{ spellcheck: 'true', lang: editorLang, autocorrect: 'on', autocapitalize: 'sentences' }"
|
||||
input-style="font-family:monospace; font-size:0.85rem; line-height:1.4; min-height:calc(100vh - 220px)"
|
||||
@update:model-value="dirty = true" />
|
||||
</div>
|
||||
|
|
@ -62,7 +68,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { listTemplates, getTemplate, saveTemplate, previewTemplate } from 'src/api/campaigns'
|
||||
|
|
@ -84,6 +90,14 @@ const saving = ref(false)
|
|||
const viewMode = ref('visual') // 'visual' | 'html' | 'preview'
|
||||
const blankCanvas = ref(false) // true after clicking "Vide" — hides the "use HTML" hint
|
||||
|
||||
// Derive the editor's spell-check language from the template name suffix.
|
||||
// gift-email-fr → fr, gift-email-en → en, etc. Browser's built-in dictionary
|
||||
// (Chrome/Safari/Firefox) uses this to flag typos in the correct language.
|
||||
const editorLang = computed(() => {
|
||||
const m = (currentName.value || '').match(/-([a-z]{2})$/)
|
||||
return m ? m[1] : 'fr'
|
||||
})
|
||||
|
||||
let editor = null
|
||||
let originalHtml = ''
|
||||
|
||||
|
|
|
|||
355
scripts/campaigns/templates/gift-email-en.html
Normal file
355
scripts/campaigns/templates/gift-email-en.html
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>An exclusive offer from TARGO</title>
|
||||
<!-- Brand fonts: Space Grotesk for display, Plus Jakarta Sans for body.
|
||||
Wrapped in MSO conditional comment so Outlook desktop skips the
|
||||
Google Fonts request (it can't render them anyway) and falls back
|
||||
to Helvetica via the font-family stack on each element. -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<!--<![endif]-->
|
||||
</head>
|
||||
<body style="margin:0; padding:0; background:#F5FAF7; font-family:'Plus Jakarta Sans','Helvetica Neue',Helvetica,Arial,sans-serif; color:#1B2E24; line-height:1.5;">
|
||||
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td align="center" style="padding:32px 16px;">
|
||||
|
||||
<!-- Main card -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:12px; overflow:hidden;">
|
||||
|
||||
<!-- Logo header (clean, no colored band) -->
|
||||
<tr>
|
||||
<td style="padding:28px 36px 22px; border-bottom:1px solid #eef0ee;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/eed4d18c-8065-4c5f-b47c-58af63171cd0/content"
|
||||
alt="TARGO" width="140"
|
||||
style="display:block; border:0; outline:none; text-decoration:none; max-width:140px; height:auto;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Greeting + hook -->
|
||||
<tr>
|
||||
<td style="padding:26px 36px 4px;">
|
||||
<p style="margin:0 0 14px; font-size:1rem; color:#374151;">Hi {{firstname}},</p>
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1B2E24; font-weight:500;">
|
||||
You went local — we want to say thanks.
|
||||
</p>
|
||||
<p style="margin:0; font-size:1rem; color:#374151;">
|
||||
Summer's here, and so is your
|
||||
<strong>limited-time exclusive offer</strong>:
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Info pill: gift card amount -->
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 18px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Digital gift card
|
||||
</div>
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1B2E24;">
|
||||
🎁 {{amount}} at hundreds of brands
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Two-column: DELIVERY + CONDITION -->
|
||||
<tr>
|
||||
<td style="padding:6px 36px 18px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td width="50%" style="padding-right:5px; vertical-align:top;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Delivery
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
⚡ Instant on activation
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td width="50%" style="padding-left:5px; vertical-align:top;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Condition
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
🤝 Stay with us {{commitment_months}}+ months
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Option 1 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 10px;">
|
||||
<span style="display:inline-block; background:#E6F9EE; color:#00C853; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
✅ Option 1
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Big green CTA card -->
|
||||
<tr>
|
||||
<td style="padding:0 36px 8px;">
|
||||
<a href="{{gift_url}}" style="text-decoration:none; color:#ffffff; display:block;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0"
|
||||
bgcolor="#00C853"
|
||||
style="background:#00C853; background-image:linear-gradient(135deg,#00C853 0%,#005026 100%); border-radius:12px;">
|
||||
<tr>
|
||||
<td style="padding:30px 24px; text-align:center;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:2.2rem; font-weight:700; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
🎁 {{amount}}
|
||||
</div>
|
||||
<div style="font-size:1.08rem; font-weight:700; color:#ffffff;">
|
||||
Redeem my gift card
|
||||
</div>
|
||||
<div style="font-size:0.85rem; opacity:0.9; margin-top:8px; color:#ffffff;">
|
||||
Pick your brand on Giftbit →
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Prorata refund disclaimer -->
|
||||
<tr>
|
||||
<td style="padding:10px 36px 22px;">
|
||||
<div style="font-size:0.85rem; color:#6b7280;">
|
||||
🪂 If you leave before {{commitment_months}} months, the prorated portion is refundable.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Option 2 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 8px;">
|
||||
<span style="display:inline-block; background:#F5FAF7; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
⏭️ Option 2
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 36px 22px;">
|
||||
<div style="font-size:0.97rem; color:#4b5563; line-height:1.55;">
|
||||
Do nothing. Your monthly subscription continues as usual —
|
||||
no commitment, no gift card.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{#expiry}}
|
||||
<!-- Optional expiry callout -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
<tr>
|
||||
<td style="padding:18px 36px 0;">
|
||||
<div style="font-size:0.85rem; color:#9ca3af;">
|
||||
⏰ This offer expires on <strong style="color:#374151;">{{expiry}}</strong>.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/expiry}}
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:18px 36px 0;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Signature -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 28px;">
|
||||
<div style="font-size:0.97rem; color:#1B2E24;">
|
||||
🤝 Thanks for helping our regional economy thrive!
|
||||
</div>
|
||||
<div style="font-size:0.9rem; color:#6b7280; margin-top:6px;">
|
||||
The TARGO team
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<!-- Merchant brands grid — 4 cols × 3 rows = 12 logos
|
||||
TO SWAP TO MAILJET-HOSTED LOGOS:
|
||||
Replace each placeholder src URL below with the Mailjet CDN URL
|
||||
you already have (same format as the TARGO logo:
|
||||
https://xqy3m.mjt.lu/img2/xqy3m/<UUID>/content). The alt= attribute
|
||||
stays as-is (used by screen readers + shown when images blocked).
|
||||
Brand list in order: Amazon, IGA, Tim Hortons, $1 Plus (Dollarama),
|
||||
Pizza Pizza, Home Depot, Best Buy, Walmart,
|
||||
Petro-Canada, Esso, Home Hardware, Sobeys.
|
||||
-->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:8px;">
|
||||
<tr>
|
||||
<td style="padding:24px 36px 12px; text-align:center;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#00C853;">
|
||||
Quelques exemples de choix pour votre carte cadeau :
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 28px 8px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<!-- Row 1 — real Mailjet-hosted logos -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" alt="Amazon" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" alt="IGA" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" alt="Tim Hortons" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/162b988c-beb7-49b3-b85e-ccc12fa2c155/content" alt="$1 Plus" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 2 -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e7771a?text=Pizza+Pizza" alt="Pizza Pizza" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/f96302?text=Home+Depot" alt="Home Depot" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/000000?text=Best+Buy" alt="Best Buy" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/0071ce?text=Walmart" alt="Walmart" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e1140a?text=Petro-Canada" alt="Petro-Canada" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/004b8d?text=Esso" alt="Esso" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e6332a?text=Home+Hardware" alt="Home Hardware" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/4caf50?text=Sobeys" alt="Sobeys" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- "Why this email" + official contacts (per brand guide §11) -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px;">
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px; text-align:center;">
|
||||
<div style="font-size:0.78rem; color:#64748B; line-height:1.55;">
|
||||
You're getting this email because you're a TARGO customer at
|
||||
<strong style="color:#1B2E24;">{{description}}</strong>.<br>
|
||||
Got a question? Write to
|
||||
<a href="mailto:support@targo.ca" style="color:#00C853; text-decoration:none;">support@targo.ca</a>
|
||||
or call
|
||||
<a href="tel:5144480773" style="color:#00C853; text-decoration:none;">514 448-0773</a>
|
||||
/ <a href="tel:18558882746" style="color:#00C853; text-decoration:none;">1 855 888-2746</a>.
|
||||
Support 7 days/week.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Dark footer band — official slogan + inverted wordmark + address.
|
||||
Slogan stays in French per brand identity (it's the trademark
|
||||
tagline of the company, not a translatable marketing line). -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:12px; background:#1C1E26; border-radius:12px; overflow:hidden;">
|
||||
<tr>
|
||||
<td style="padding:26px 36px 22px; text-align:center;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:1.4rem; font-weight:700; letter-spacing:0.18em; color:#ffffff; line-height:1;">
|
||||
TARGO<span style="color:#00C853;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.85rem; color:rgba(255,255,255,0.75); margin-top:10px;">
|
||||
Services de confiance, tout-en-un, près de chez vous<span style="color:#00C853; font-weight:700;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.7rem; color:rgba(255,255,255,0.45); margin-top:14px; line-height:1.5;">
|
||||
<a href="https://www.targo.ca" style="color:rgba(255,255,255,0.7); text-decoration:none;">www.targo.ca</a>
|
||||
· 1867 ch. de la rivière, Ste-Clotilde, QC<br>
|
||||
© {{year}} TARGO Communications · All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,8 +4,17 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Une offre exclusive de TARGO</title>
|
||||
<!-- Brand fonts: Space Grotesk for display, Plus Jakarta Sans for body.
|
||||
Wrapped in MSO conditional comment so Outlook desktop skips the
|
||||
Google Fonts request (it can't render them anyway) and falls back
|
||||
to Helvetica via the font-family stack on each element. -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<!--<![endif]-->
|
||||
</head>
|
||||
<body style="margin:0; padding:0; background:#f7f8f7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; color:#1f2937; line-height:1.5;">
|
||||
<body style="margin:0; padding:0; background:#F5FAF7; font-family:'Plus Jakarta Sans','Helvetica Neue',Helvetica,Arial,sans-serif; color:#1B2E24; line-height:1.5;">
|
||||
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
|
|
@ -13,7 +22,7 @@
|
|||
|
||||
<!-- Main card -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:14px; overflow:hidden;">
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:12px; overflow:hidden;">
|
||||
|
||||
<!-- Logo header (clean, no colored band) -->
|
||||
<tr>
|
||||
|
|
@ -28,7 +37,7 @@
|
|||
<tr>
|
||||
<td style="padding:26px 36px 4px;">
|
||||
<p style="margin:0 0 14px; font-size:1rem; color:#374151;">Bonjour {{firstname}},</p>
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1f2937; font-weight:500;">
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1B2E24; font-weight:500;">
|
||||
Tu choisis local, on veut te remercier.
|
||||
</p>
|
||||
<p style="margin:0; font-size:1rem; color:#374151;">
|
||||
|
|
@ -41,11 +50,11 @@
|
|||
<!-- Info pill: gift card amount -->
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 18px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 18px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Carte-cadeau numérique
|
||||
</div>
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1B2E24;">
|
||||
🎁 {{amount}} chez des centaines de marques
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -58,21 +67,21 @@
|
|||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td width="50%" style="padding-right:5px; vertical-align:top;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 16px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Envoi
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
⚡ Instantané à l'activation
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td width="50%" style="padding-left:5px; vertical-align:top;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 16px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Condition
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
🤝 Rester encore {{commitment_months}} mois ou +
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -88,7 +97,7 @@
|
|||
<!-- Option 1 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 10px;">
|
||||
<span style="display:inline-block; background:#dcf4e3; color:#019547; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
<span style="display:inline-block; background:#E6F9EE; color:#00C853; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
✅ Option 1
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -98,11 +107,16 @@
|
|||
<tr>
|
||||
<td style="padding:0 36px 8px;">
|
||||
<a href="{{gift_url}}" style="text-decoration:none; color:#ffffff; display:block;">
|
||||
<!-- CTA card — gradient Targo officiel (135deg, #00C853 → #005026).
|
||||
Outlook desktop will ignore the gradient and render the
|
||||
solid #00C853 fallback (the gradient is the bgcolor's
|
||||
fallback in nodemailer rendering). Acceptable degradation. -->
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0"
|
||||
style="background:#019547; border-radius:14px;">
|
||||
bgcolor="#00C853"
|
||||
style="background:#00C853; background-image:linear-gradient(135deg,#00C853 0%,#005026 100%); border-radius:12px;">
|
||||
<tr>
|
||||
<td style="padding:30px 24px; text-align:center;">
|
||||
<div style="font-size:2.1rem; font-weight:800; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:2.2rem; font-weight:700; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
🎁 {{amount}}
|
||||
</div>
|
||||
<div style="font-size:1.08rem; font-weight:700; color:#ffffff;">
|
||||
|
|
@ -133,7 +147,7 @@
|
|||
<!-- Option 2 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 8px;">
|
||||
<span style="display:inline-block; background:#f3f4f3; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
<span style="display:inline-block; background:#F5FAF7; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
⏭️ Option 2
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -165,7 +179,7 @@
|
|||
<!-- Signature -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 28px;">
|
||||
<div style="font-size:0.97rem; color:#1f2937;">
|
||||
<div style="font-size:0.97rem; color:#1B2E24;">
|
||||
🤝 Merci de faire rouler l'économie de notre région avec nous !
|
||||
</div>
|
||||
<div style="font-size:0.9rem; color:#6b7280; margin-top:6px;">
|
||||
|
|
@ -190,7 +204,7 @@
|
|||
style="max-width:600px; margin-top:8px;">
|
||||
<tr>
|
||||
<td style="padding:24px 36px 12px; text-align:center;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#019547;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#00C853;">
|
||||
Quelques exemples de choix pour votre carte cadeau :
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -296,28 +310,42 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Footer (outside the card, small print) -->
|
||||
<!-- "Pourquoi cet email" + coordonnées officielles (per brand guide §11) -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px;">
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px; text-align:center;">
|
||||
<div style="font-size:0.75rem; color:#9ca3af; line-height:1.55;">
|
||||
<div style="font-size:0.78rem; color:#64748B; line-height:1.55;">
|
||||
Tu reçois ce courriel parce que tu es client(e) TARGO à
|
||||
<strong style="color:#6b7280;">{{description}}</strong>.<br>
|
||||
<strong style="color:#1B2E24;">{{description}}</strong>.<br>
|
||||
Une question ? Écris-nous à
|
||||
<a href="mailto:facturation@targointernet.com" style="color:#019547;">facturation@targointernet.com</a>
|
||||
ou appelle au <a href="tel:5142421500" style="color:#019547;">514 242-1500</a>.
|
||||
<a href="mailto:support@targo.ca" style="color:#00C853; text-decoration:none;">support@targo.ca</a>
|
||||
ou appelle au
|
||||
<a href="tel:5144480773" style="color:#00C853; text-decoration:none;">514 448-0773</a>
|
||||
/ <a href="tel:18558882746" style="color:#00C853; text-decoration:none;">1 855 888-2746</a>.
|
||||
Support 7j/7.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Dark footer band — slogan officiel + logo inversé + adresse -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:12px; background:#1C1E26; border-radius:12px; overflow:hidden;">
|
||||
<tr>
|
||||
<td style="padding:0 36px 28px; text-align:center;">
|
||||
<div style="font-size:0.7rem; color:#9ca3af;">
|
||||
<strong style="color:#019547; letter-spacing:0.08em;">TARGO</strong>
|
||||
— Internet fibre optique au Québec — service <em>Gigafibre</em><br>
|
||||
<a href="https://www.targointernet.com" style="color:#9ca3af; text-decoration:underline;">targointernet.com</a>
|
||||
·
|
||||
<a href="https://www.gigafibre.ca" style="color:#9ca3af; text-decoration:underline;">gigafibre.ca</a>
|
||||
<td style="padding:26px 36px 22px; text-align:center;">
|
||||
<!-- TARGO wordmark in white (matches brand "fonds sombres" usage) -->
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:1.4rem; font-weight:700; letter-spacing:0.18em; color:#ffffff; line-height:1;">
|
||||
TARGO<span style="color:#00C853;">.</span>
|
||||
</div>
|
||||
<!-- Slogan officiel — toujours avec le point vert -->
|
||||
<div style="font-size:0.85rem; color:rgba(255,255,255,0.75); margin-top:10px;">
|
||||
Services de confiance, tout-en-un, près de chez vous<span style="color:#00C853; font-weight:700;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.7rem; color:rgba(255,255,255,0.45); margin-top:14px; line-height:1.5;">
|
||||
<a href="https://www.targo.ca" style="color:rgba(255,255,255,0.7); text-decoration:none;">www.targo.ca</a>
|
||||
· 1867 ch. de la rivière, Ste-Clotilde, QC<br>
|
||||
© {{year}} TARGO Communications · Tous droits réservés.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -266,12 +266,13 @@ async function matchCustomer (recipient) {
|
|||
if (recipient.email) {
|
||||
try {
|
||||
const r = await erp.list('Customer', {
|
||||
fields: ['name', 'customer_name', 'email_id', 'mobile_no'],
|
||||
fields: ['name', 'customer_name', 'email_id', 'mobile_no', 'language'],
|
||||
filters: [['email_id', '=', recipient.email]],
|
||||
limit: 1,
|
||||
})
|
||||
if (r && r.length) {
|
||||
return { customer_id: r[0].name, customer_name: r[0].customer_name,
|
||||
language: r[0].language || 'fr',
|
||||
match_method: 'email', match_confidence: 1.0 }
|
||||
}
|
||||
} catch (e) { log('match email error:', e.message) }
|
||||
|
|
@ -280,22 +281,21 @@ async function matchCustomer (recipient) {
|
|||
// 2) Phone match
|
||||
if (recipient.phone) {
|
||||
try {
|
||||
// Try both mobile_no (with various formattings) and the canonical 10-digit form
|
||||
const r = await erp.list('Customer', {
|
||||
fields: ['name', 'customer_name', 'mobile_no'],
|
||||
fields: ['name', 'customer_name', 'mobile_no', 'language'],
|
||||
filters: [['mobile_no', 'like', '%' + recipient.phone.slice(-7) + '%']],
|
||||
limit: 5,
|
||||
})
|
||||
// Filter to exact digit match
|
||||
const hit = (r || []).find(c => normalizePhone(c.mobile_no) === recipient.phone)
|
||||
if (hit) {
|
||||
return { customer_id: hit.name, customer_name: hit.customer_name,
|
||||
language: hit.language || 'fr',
|
||||
match_method: 'phone', match_confidence: 0.9 }
|
||||
}
|
||||
} catch (e) { log('match phone error:', e.message) }
|
||||
}
|
||||
|
||||
// 3) Civic + postal on Service Location → Customer
|
||||
// 3) Civic + postal on Service Location → Customer (+ fetch Customer.language separately)
|
||||
if (recipient.civic_address && recipient.postal_code) {
|
||||
try {
|
||||
const civic = normalizeCivic(recipient.civic_address)
|
||||
|
|
@ -315,14 +315,25 @@ async function matchCustomer (recipient) {
|
|||
slCivic.split('|')[1]?.startsWith(streetPrefix.slice(0, 5))
|
||||
})
|
||||
if (hit && hit.customer) {
|
||||
// Service Location doesn't carry language — fetch it from the linked Customer
|
||||
let language = 'fr'
|
||||
try {
|
||||
const c = await erp.list('Customer', {
|
||||
fields: ['language'], filters: [['name', '=', hit.customer]], limit: 1,
|
||||
})
|
||||
if (c?.[0]?.language) language = c[0].language
|
||||
} catch {}
|
||||
return { customer_id: hit.customer, customer_name: hit.customer_name || hit.customer,
|
||||
language,
|
||||
match_method: 'civic', match_confidence: 0.8 }
|
||||
}
|
||||
}
|
||||
} catch (e) { log('match civic error:', e.message) }
|
||||
}
|
||||
|
||||
return { customer_id: null, customer_name: null, match_method: null, match_confidence: 0 }
|
||||
// Unmatched: default to French (93% of customer base)
|
||||
return { customer_id: null, customer_name: null, language: 'fr',
|
||||
match_method: null, match_confidence: 0 }
|
||||
}
|
||||
|
||||
// ── Storage helpers ──────────────────────────────────────────────────────────
|
||||
|
|
@ -382,17 +393,26 @@ function renderTemplate (tpl, vars) {
|
|||
})
|
||||
}
|
||||
|
||||
// Default template path — bundled inside the hub at services/targo-hub/templates/.
|
||||
// Can be overridden per campaign via params.template_path (e.g. for A/B testing).
|
||||
// Keep the file in sync with scripts/campaigns/templates/gift-email-fr.html
|
||||
// (the CLI uses that copy for one-off batch sends outside the hub).
|
||||
// Templates are bundled inside the hub at services/targo-hub/templates/.
|
||||
// Keep them in sync with scripts/campaigns/templates/ for the CLI use case.
|
||||
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates')
|
||||
const DEFAULT_TEMPLATE = path.join(TEMPLATES_DIR, 'gift-email-fr.html')
|
||||
|
||||
// Allow-list templates that can be edited via the API. Anything outside this
|
||||
// list is rejected (path-traversal defence + intent: only campaign-related
|
||||
// templates are exposed; contract-residential.html etc. are NOT editable here).
|
||||
const EDITABLE_TEMPLATES = ['gift-email-fr']
|
||||
// Allow-list templates that can be edited via the API. Naming convention:
|
||||
// gift-email-<lang> — the recipient's language drives which one gets used
|
||||
// at send time. Adding a new lang = drop a new .html here + add to this list.
|
||||
const EDITABLE_TEMPLATES = ['gift-email-fr', 'gift-email-en']
|
||||
|
||||
// Resolve the template path for a given recipient language. Falls back to
|
||||
// the French version (most of the customer base) if the language-specific
|
||||
// file doesn't exist. Per-campaign override via params.template_path skips
|
||||
// language routing entirely.
|
||||
function templateForLanguage (lang) {
|
||||
const safe = (lang || 'fr').toLowerCase().split('-')[0] // 'fr-CA' → 'fr'
|
||||
const candidate = path.join(TEMPLATES_DIR, `gift-email-${safe}.html`)
|
||||
if (fs.existsSync(candidate)) return candidate
|
||||
return DEFAULT_TEMPLATE
|
||||
}
|
||||
|
||||
function templatePath (name) {
|
||||
if (!EDITABLE_TEMPLATES.includes(name)) {
|
||||
|
|
@ -447,8 +467,17 @@ async function sendCampaignAsync (id) {
|
|||
sse.broadcast(topic, 'campaign-status', { id, status: 'sending' })
|
||||
|
||||
const p = campaign.params || {}
|
||||
const tplText = readTemplate(p.template_path)
|
||||
const throttle = parseInt(p.throttle_ms || 600, 10)
|
||||
// Pre-load templates by language to avoid re-reading from disk on every
|
||||
// recipient. Cache map keyed by resolved template path.
|
||||
const tplCache = new Map()
|
||||
const getTpl = (lang) => {
|
||||
const tplPath = p.template_path || templateForLanguage(lang)
|
||||
if (!tplCache.has(tplPath)) {
|
||||
tplCache.set(tplPath, fs.readFileSync(tplPath, 'utf8'))
|
||||
}
|
||||
return tplCache.get(tplPath)
|
||||
}
|
||||
|
||||
for (let i = 0; i < campaign.recipients.length; i++) {
|
||||
const r = campaign.recipients[i]
|
||||
|
|
@ -459,8 +488,10 @@ async function sendCampaignAsync (id) {
|
|||
saveCampaign(campaign)
|
||||
sse.broadcast(topic, 'recipient-update', { i, recipient: r })
|
||||
|
||||
const lang = (r.language || 'fr').toLowerCase().split('-')[0]
|
||||
const tplText = getTpl(lang)
|
||||
const vars = {
|
||||
firstname: r.firstname || 'cher client',
|
||||
firstname: r.firstname || (lang === 'en' ? 'dear customer' : 'cher client'),
|
||||
lastname: r.lastname || '',
|
||||
email: r.email,
|
||||
description: r.civic_address || '',
|
||||
|
|
@ -468,6 +499,7 @@ async function sendCampaignAsync (id) {
|
|||
amount: p.amount || '50 $',
|
||||
expiry: p.expiry || '',
|
||||
commitment_months: p.commitment_months || '3',
|
||||
year: new Date().getFullYear(),
|
||||
}
|
||||
const html = renderTemplate(tplText, vars)
|
||||
const toName = `${r.firstname || ''} ${r.lastname || ''}`.trim()
|
||||
|
|
|
|||
355
services/targo-hub/templates/gift-email-en.html
Normal file
355
services/targo-hub/templates/gift-email-en.html
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>An exclusive offer from TARGO</title>
|
||||
<!-- Brand fonts: Space Grotesk for display, Plus Jakarta Sans for body.
|
||||
Wrapped in MSO conditional comment so Outlook desktop skips the
|
||||
Google Fonts request (it can't render them anyway) and falls back
|
||||
to Helvetica via the font-family stack on each element. -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<!--<![endif]-->
|
||||
</head>
|
||||
<body style="margin:0; padding:0; background:#F5FAF7; font-family:'Plus Jakarta Sans','Helvetica Neue',Helvetica,Arial,sans-serif; color:#1B2E24; line-height:1.5;">
|
||||
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td align="center" style="padding:32px 16px;">
|
||||
|
||||
<!-- Main card -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:12px; overflow:hidden;">
|
||||
|
||||
<!-- Logo header (clean, no colored band) -->
|
||||
<tr>
|
||||
<td style="padding:28px 36px 22px; border-bottom:1px solid #eef0ee;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/eed4d18c-8065-4c5f-b47c-58af63171cd0/content"
|
||||
alt="TARGO" width="140"
|
||||
style="display:block; border:0; outline:none; text-decoration:none; max-width:140px; height:auto;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Greeting + hook -->
|
||||
<tr>
|
||||
<td style="padding:26px 36px 4px;">
|
||||
<p style="margin:0 0 14px; font-size:1rem; color:#374151;">Hi {{firstname}},</p>
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1B2E24; font-weight:500;">
|
||||
You went local — we want to say thanks.
|
||||
</p>
|
||||
<p style="margin:0; font-size:1rem; color:#374151;">
|
||||
Summer's here, and so is your
|
||||
<strong>limited-time exclusive offer</strong>:
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Info pill: gift card amount -->
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 18px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Digital gift card
|
||||
</div>
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1B2E24;">
|
||||
🎁 {{amount}} at hundreds of brands
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Two-column: DELIVERY + CONDITION -->
|
||||
<tr>
|
||||
<td style="padding:6px 36px 18px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td width="50%" style="padding-right:5px; vertical-align:top;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Delivery
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
⚡ Instant on activation
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td width="50%" style="padding-left:5px; vertical-align:top;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Condition
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
🤝 Stay with us {{commitment_months}}+ months
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Option 1 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 10px;">
|
||||
<span style="display:inline-block; background:#E6F9EE; color:#00C853; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
✅ Option 1
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Big green CTA card -->
|
||||
<tr>
|
||||
<td style="padding:0 36px 8px;">
|
||||
<a href="{{gift_url}}" style="text-decoration:none; color:#ffffff; display:block;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0"
|
||||
bgcolor="#00C853"
|
||||
style="background:#00C853; background-image:linear-gradient(135deg,#00C853 0%,#005026 100%); border-radius:12px;">
|
||||
<tr>
|
||||
<td style="padding:30px 24px; text-align:center;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:2.2rem; font-weight:700; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
🎁 {{amount}}
|
||||
</div>
|
||||
<div style="font-size:1.08rem; font-weight:700; color:#ffffff;">
|
||||
Redeem my gift card
|
||||
</div>
|
||||
<div style="font-size:0.85rem; opacity:0.9; margin-top:8px; color:#ffffff;">
|
||||
Pick your brand on Giftbit →
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Prorata refund disclaimer -->
|
||||
<tr>
|
||||
<td style="padding:10px 36px 22px;">
|
||||
<div style="font-size:0.85rem; color:#6b7280;">
|
||||
🪂 If you leave before {{commitment_months}} months, the prorated portion is refundable.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Option 2 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 8px;">
|
||||
<span style="display:inline-block; background:#F5FAF7; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
⏭️ Option 2
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 36px 22px;">
|
||||
<div style="font-size:0.97rem; color:#4b5563; line-height:1.55;">
|
||||
Do nothing. Your monthly subscription continues as usual —
|
||||
no commitment, no gift card.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{#expiry}}
|
||||
<!-- Optional expiry callout -->
|
||||
<tr><td style="padding:0 36px;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
<tr>
|
||||
<td style="padding:18px 36px 0;">
|
||||
<div style="font-size:0.85rem; color:#9ca3af;">
|
||||
⏰ This offer expires on <strong style="color:#374151;">{{expiry}}</strong>.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/expiry}}
|
||||
|
||||
<!-- Divider -->
|
||||
<tr><td style="padding:18px 36px 0;"><div style="border-top:1px solid #eef0ee;"></div></td></tr>
|
||||
|
||||
<!-- Signature -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 28px;">
|
||||
<div style="font-size:0.97rem; color:#1B2E24;">
|
||||
🤝 Thanks for helping our regional economy thrive!
|
||||
</div>
|
||||
<div style="font-size:0.9rem; color:#6b7280; margin-top:6px;">
|
||||
The TARGO team
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<!-- Merchant brands grid — 4 cols × 3 rows = 12 logos
|
||||
TO SWAP TO MAILJET-HOSTED LOGOS:
|
||||
Replace each placeholder src URL below with the Mailjet CDN URL
|
||||
you already have (same format as the TARGO logo:
|
||||
https://xqy3m.mjt.lu/img2/xqy3m/<UUID>/content). The alt= attribute
|
||||
stays as-is (used by screen readers + shown when images blocked).
|
||||
Brand list in order: Amazon, IGA, Tim Hortons, $1 Plus (Dollarama),
|
||||
Pizza Pizza, Home Depot, Best Buy, Walmart,
|
||||
Petro-Canada, Esso, Home Hardware, Sobeys.
|
||||
-->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:8px;">
|
||||
<tr>
|
||||
<td style="padding:24px 36px 12px; text-align:center;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#00C853;">
|
||||
Quelques exemples de choix pour votre carte cadeau :
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 28px 8px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<!-- Row 1 — real Mailjet-hosted logos -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" alt="Amazon" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" alt="IGA" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" alt="Tim Hortons" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://xqy3m.mjt.lu/img2/xqy3m/162b988c-beb7-49b3-b85e-ccc12fa2c155/content" alt="$1 Plus" width="95" style="max-width:95px; height:auto; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 2 -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e7771a?text=Pizza+Pizza" alt="Pizza Pizza" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/f96302?text=Home+Depot" alt="Home Depot" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/000000?text=Best+Buy" alt="Best Buy" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/0071ce?text=Walmart" alt="Walmart" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Row 3 -->
|
||||
<tr>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e1140a?text=Petro-Canada" alt="Petro-Canada" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/004b8d?text=Esso" alt="Esso" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/e6332a?text=Home+Hardware" alt="Home Hardware" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<td width="25%" style="padding:4px;">
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#ffffff; border-radius:8px;">
|
||||
<tr><td align="center" valign="middle" height="92" style="height:92px;">
|
||||
<img src="https://placehold.co/160x90/ffffff/4caf50?text=Sobeys" alt="Sobeys" width="120" style="max-width:120px; max-height:72px; display:inline-block; border:0;">
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- "Why this email" + official contacts (per brand guide §11) -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px;">
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px; text-align:center;">
|
||||
<div style="font-size:0.78rem; color:#64748B; line-height:1.55;">
|
||||
You're getting this email because you're a TARGO customer at
|
||||
<strong style="color:#1B2E24;">{{description}}</strong>.<br>
|
||||
Got a question? Write to
|
||||
<a href="mailto:support@targo.ca" style="color:#00C853; text-decoration:none;">support@targo.ca</a>
|
||||
or call
|
||||
<a href="tel:5144480773" style="color:#00C853; text-decoration:none;">514 448-0773</a>
|
||||
/ <a href="tel:18558882746" style="color:#00C853; text-decoration:none;">1 855 888-2746</a>.
|
||||
Support 7 days/week.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Dark footer band — official slogan + inverted wordmark + address.
|
||||
Slogan stays in French per brand identity (it's the trademark
|
||||
tagline of the company, not a translatable marketing line). -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:12px; background:#1C1E26; border-radius:12px; overflow:hidden;">
|
||||
<tr>
|
||||
<td style="padding:26px 36px 22px; text-align:center;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:1.4rem; font-weight:700; letter-spacing:0.18em; color:#ffffff; line-height:1;">
|
||||
TARGO<span style="color:#00C853;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.85rem; color:rgba(255,255,255,0.75); margin-top:10px;">
|
||||
Services de confiance, tout-en-un, près de chez vous<span style="color:#00C853; font-weight:700;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.7rem; color:rgba(255,255,255,0.45); margin-top:14px; line-height:1.5;">
|
||||
<a href="https://www.targo.ca" style="color:rgba(255,255,255,0.7); text-decoration:none;">www.targo.ca</a>
|
||||
· 1867 ch. de la rivière, Ste-Clotilde, QC<br>
|
||||
© {{year}} TARGO Communications · All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,8 +4,17 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Une offre exclusive de TARGO</title>
|
||||
<!-- Brand fonts: Space Grotesk for display, Plus Jakarta Sans for body.
|
||||
Wrapped in MSO conditional comment so Outlook desktop skips the
|
||||
Google Fonts request (it can't render them anyway) and falls back
|
||||
to Helvetica via the font-family stack on each element. -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<!--<![endif]-->
|
||||
</head>
|
||||
<body style="margin:0; padding:0; background:#f7f8f7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; color:#1f2937; line-height:1.5;">
|
||||
<body style="margin:0; padding:0; background:#F5FAF7; font-family:'Plus Jakarta Sans','Helvetica Neue',Helvetica,Arial,sans-serif; color:#1B2E24; line-height:1.5;">
|
||||
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
|
|
@ -13,7 +22,7 @@
|
|||
|
||||
<!-- Main card -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:14px; overflow:hidden;">
|
||||
style="max-width:600px; background:#ffffff; border:1px solid #e5e7eb; border-radius:12px; overflow:hidden;">
|
||||
|
||||
<!-- Logo header (clean, no colored band) -->
|
||||
<tr>
|
||||
|
|
@ -28,7 +37,7 @@
|
|||
<tr>
|
||||
<td style="padding:26px 36px 4px;">
|
||||
<p style="margin:0 0 14px; font-size:1rem; color:#374151;">Bonjour {{firstname}},</p>
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1f2937; font-weight:500;">
|
||||
<p style="margin:0 0 10px; font-size:1.08rem; color:#1B2E24; font-weight:500;">
|
||||
Tu choisis local, on veut te remercier.
|
||||
</p>
|
||||
<p style="margin:0; font-size:1rem; color:#374151;">
|
||||
|
|
@ -41,11 +50,11 @@
|
|||
<!-- Info pill: gift card amount -->
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 18px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 18px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Carte-cadeau numérique
|
||||
</div>
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:1.05rem; font-weight:700; color:#1B2E24;">
|
||||
🎁 {{amount}} chez des centaines de marques
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -58,21 +67,21 @@
|
|||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td width="50%" style="padding-right:5px; vertical-align:top;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 16px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Envoi
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
⚡ Instantané à l'activation
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td width="50%" style="padding-left:5px; vertical-align:top;">
|
||||
<div style="background:#f3f4f3; border-radius:10px; padding:14px 16px;">
|
||||
<div style="background:#F5FAF7; border-radius:10px; padding:14px 16px;">
|
||||
<div style="font-size:0.7rem; font-weight:700; letter-spacing:0.12em; color:#9ca3af; text-transform:uppercase; margin-bottom:4px;">
|
||||
Condition
|
||||
</div>
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1f2937;">
|
||||
<div style="font-size:0.95rem; font-weight:700; color:#1B2E24;">
|
||||
🤝 Rester encore {{commitment_months}} mois ou +
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -88,7 +97,7 @@
|
|||
<!-- Option 1 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 10px;">
|
||||
<span style="display:inline-block; background:#dcf4e3; color:#019547; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
<span style="display:inline-block; background:#E6F9EE; color:#00C853; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
✅ Option 1
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -98,11 +107,16 @@
|
|||
<tr>
|
||||
<td style="padding:0 36px 8px;">
|
||||
<a href="{{gift_url}}" style="text-decoration:none; color:#ffffff; display:block;">
|
||||
<!-- CTA card — gradient Targo officiel (135deg, #00C853 → #005026).
|
||||
Outlook desktop will ignore the gradient and render the
|
||||
solid #00C853 fallback (the gradient is the bgcolor's
|
||||
fallback in nodemailer rendering). Acceptable degradation. -->
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0"
|
||||
style="background:#019547; border-radius:14px;">
|
||||
bgcolor="#00C853"
|
||||
style="background:#00C853; background-image:linear-gradient(135deg,#00C853 0%,#005026 100%); border-radius:12px;">
|
||||
<tr>
|
||||
<td style="padding:30px 24px; text-align:center;">
|
||||
<div style="font-size:2.1rem; font-weight:800; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:2.2rem; font-weight:700; line-height:1; margin-bottom:14px; color:#ffffff;">
|
||||
🎁 {{amount}}
|
||||
</div>
|
||||
<div style="font-size:1.08rem; font-weight:700; color:#ffffff;">
|
||||
|
|
@ -133,7 +147,7 @@
|
|||
<!-- Option 2 chip -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 8px;">
|
||||
<span style="display:inline-block; background:#f3f4f3; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
<span style="display:inline-block; background:#F5FAF7; color:#6b7280; font-size:0.82rem; font-weight:700; padding:5px 12px; border-radius:6px;">
|
||||
⏭️ Option 2
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -165,7 +179,7 @@
|
|||
<!-- Signature -->
|
||||
<tr>
|
||||
<td style="padding:22px 36px 28px;">
|
||||
<div style="font-size:0.97rem; color:#1f2937;">
|
||||
<div style="font-size:0.97rem; color:#1B2E24;">
|
||||
🤝 Merci de faire rouler l'économie de notre région avec nous !
|
||||
</div>
|
||||
<div style="font-size:0.9rem; color:#6b7280; margin-top:6px;">
|
||||
|
|
@ -190,7 +204,7 @@
|
|||
style="max-width:600px; margin-top:8px;">
|
||||
<tr>
|
||||
<td style="padding:24px 36px 12px; text-align:center;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#019547;">
|
||||
<div style="font-size:1.02rem; font-weight:700; color:#00C853;">
|
||||
Quelques exemples de choix pour votre carte cadeau :
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -296,28 +310,42 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Footer (outside the card, small print) -->
|
||||
<!-- "Pourquoi cet email" + coordonnées officielles (per brand guide §11) -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px;">
|
||||
<tr>
|
||||
<td style="padding:18px 36px 8px; text-align:center;">
|
||||
<div style="font-size:0.75rem; color:#9ca3af; line-height:1.55;">
|
||||
<div style="font-size:0.78rem; color:#64748B; line-height:1.55;">
|
||||
Tu reçois ce courriel parce que tu es client(e) TARGO à
|
||||
<strong style="color:#6b7280;">{{description}}</strong>.<br>
|
||||
<strong style="color:#1B2E24;">{{description}}</strong>.<br>
|
||||
Une question ? Écris-nous à
|
||||
<a href="mailto:facturation@targointernet.com" style="color:#019547;">facturation@targointernet.com</a>
|
||||
ou appelle au <a href="tel:5142421500" style="color:#019547;">514 242-1500</a>.
|
||||
<a href="mailto:support@targo.ca" style="color:#00C853; text-decoration:none;">support@targo.ca</a>
|
||||
ou appelle au
|
||||
<a href="tel:5144480773" style="color:#00C853; text-decoration:none;">514 448-0773</a>
|
||||
/ <a href="tel:18558882746" style="color:#00C853; text-decoration:none;">1 855 888-2746</a>.
|
||||
Support 7j/7.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Dark footer band — slogan officiel + logo inversé + adresse -->
|
||||
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0"
|
||||
style="max-width:600px; margin-top:12px; background:#1C1E26; border-radius:12px; overflow:hidden;">
|
||||
<tr>
|
||||
<td style="padding:0 36px 28px; text-align:center;">
|
||||
<div style="font-size:0.7rem; color:#9ca3af;">
|
||||
<strong style="color:#019547; letter-spacing:0.08em;">TARGO</strong>
|
||||
— Internet fibre optique au Québec — service <em>Gigafibre</em><br>
|
||||
<a href="https://www.targointernet.com" style="color:#9ca3af; text-decoration:underline;">targointernet.com</a>
|
||||
·
|
||||
<a href="https://www.gigafibre.ca" style="color:#9ca3af; text-decoration:underline;">gigafibre.ca</a>
|
||||
<td style="padding:26px 36px 22px; text-align:center;">
|
||||
<!-- TARGO wordmark in white (matches brand "fonds sombres" usage) -->
|
||||
<div style="font-family:'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif; font-size:1.4rem; font-weight:700; letter-spacing:0.18em; color:#ffffff; line-height:1;">
|
||||
TARGO<span style="color:#00C853;">.</span>
|
||||
</div>
|
||||
<!-- Slogan officiel — toujours avec le point vert -->
|
||||
<div style="font-size:0.85rem; color:rgba(255,255,255,0.75); margin-top:10px;">
|
||||
Services de confiance, tout-en-un, près de chez vous<span style="color:#00C853; font-weight:700;">.</span>
|
||||
</div>
|
||||
<div style="font-size:0.7rem; color:rgba(255,255,255,0.45); margin-top:14px; line-height:1.5;">
|
||||
<a href="https://www.targo.ca" style="color:rgba(255,255,255,0.7); text-decoration:none;">www.targo.ca</a>
|
||||
· 1867 ch. de la rivière, Ste-Clotilde, QC<br>
|
||||
© {{year}} TARGO Communications · Tous droits réservés.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user