feat(campaigns): CSV report, manual recipients, template polish

Ops UI
- CampaignDetailPage: "CSV" button — downloads per-recipient report
  (shortlinks, status, opened/clicked timestamps, mailjet UUID)
- CampaignNewPage: "Saisie manuelle (sans CSV)" on Step 1 and
  "Ajouter manuellement" on Step 2 — both open the same dialog with
  firstname / email / gift_url / city / postal_code / language /
  amount override. Indigo "manuel" chip in the recipients table.
- New "Ville" column shows city OR postal_code as fallback.

Hub
- GET /campaigns/:id/report.csv — RFC 4180 CSV with UTF-8 BOM so
  Excel auto-detects encoding. 20 columns including new "city".
- Worker honours per-recipient amount override:
  r.amount > derive from r.gift_value_cents > params.amount > "50 $".
  Fixes manual-add showing campaign default instead of typed value.
- Default subject "Un cadeau pour toi" (tutoyer).

Templates
- Order: Intro →  Option 1 → 🎁 marques → CTA → prorata → ⏭️ Option 2.
- New EN intro (manifesto): "Thank you for choosing local. Your
  support helps keep our community connected. / Because great
  connections aren't just about fiber — they're about people too."
- Amazon logo removed (incongruent with "achat local" framing).
- Body paragraphs: text-align justify (greeting/labels stay left).
- Support line: "N'hésite pas à nous écrire / Feel free to email us"
  + dash format 514-448-0773, drop "Support 7j/7" overpromise.
- Logo style fix: inline width:32px to beat Unlayer canvas CSS that
  was rendering brand pills full-width.

Ignore template converter .bak-*.json backups.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-05-22 09:09:07 -04:00
parent 40a2e4e8f2
commit 2bc9715485
13 changed files with 714 additions and 276 deletions

4
.gitignore vendored
View File

@ -43,3 +43,7 @@ scripts/migration/ref_invoice.pdf
# Playwright snapshots
.playwright-mcp/
# Auto-generated backups from scripts/convert-html-to-unlayer.js
services/targo-hub/templates/*.bak-*.json
services/targo-hub/templates/*.bak-*.html

View File

@ -69,6 +69,13 @@ export function sendCampaign (id) {
})
}
// Build the URL the browser hits to download the per-recipient CSV report
// (giftbit shortlink ↔ email ↔ name ↔ status). The hub serves it with the
// proper Content-Disposition so an <a download> click triggers a save.
export function campaignReportCsvUrl (id) {
return `${HUB_URL}/campaigns/${encodeURIComponent(id)}/report.csv`
}
// ── Image assets (self-hosted on the hub, for GrapesJS asset manager) ───────
export function listAssets () {

View File

@ -5,6 +5,13 @@
<div class="text-h5">{{ campaign?.name || id }}</div>
<q-chip dense class="q-ml-md" :color="statusColor(campaign?.status)" text-color="white" :label="statusLabel(campaign?.status)" />
<q-space />
<q-btn
v-if="campaign?.recipients?.length"
flat dense icon="file_download" label="CSV" class="q-mr-sm"
:href="reportCsvUrl" download
>
<q-tooltip>Télécharger le rapport (shortlinks Giftbit, emails, statuts d'envoi)</q-tooltip>
</q-btn>
<q-btn v-if="campaign?.status === 'draft'" unelevated color="primary" icon="send" label="Lancer l'envoi"
:loading="resending" @click="relaunch" />
</div>
@ -92,7 +99,7 @@
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import { useQuasar } from 'quasar'
import { getCampaign, sendCampaign, campaignSseUrl } from 'src/api/campaigns'
import { getCampaign, sendCampaign, campaignSseUrl, campaignReportCsvUrl } from 'src/api/campaigns'
const route = useRoute()
const $q = useQuasar()
@ -131,6 +138,8 @@ function statusLabel (s) {
}
function shortLink (u) { return (u || '').replace(/^https?:\/\//, '').slice(0, 28) + ((u || '').length > 35 ? '…' : '') }
const reportCsvUrl = computed(() => campaignReportCsvUrl(id))
const sentRatio = computed(() => {
const total = campaign.value?.counters?.total || 1
const done = counterFor('sent') + counterFor('opened') + counterFor('clicked')

View File

@ -73,6 +73,9 @@
</q-card>
<q-stepper-navigation>
<q-btn flat color="primary" icon="person_add" label="Saisie manuelle (sans CSV)" @click="goManual" class="q-mr-sm">
<q-tooltip>Sauter l'import CSV et ajouter les destinataires un par un à l'étape suivante</q-tooltip>
</q-btn>
<q-btn unelevated color="primary" label="Suivant — Aperçu" icon-right="arrow_forward"
:disable="!mapPreview || !giftPreview || parsing"
:loading="parsing" @click="goPreview" />
@ -157,9 +160,15 @@
</q-banner>
<!-- Paired recipients (will be sent) -->
<div class="text-subtitle1 q-mb-xs">
<q-icon name="link" /> Association contact lien-cadeau
<span class="text-caption text-grey-7"> vérifier avant d'approuver</span>
<div class="row items-center q-mb-xs">
<div class="text-subtitle1">
<q-icon name="link" /> Association contact lien-cadeau
<span class="text-caption text-grey-7"> vérifier avant d'approuver</span>
</div>
<q-space />
<q-btn unelevated dense color="primary" icon="person_add" label="Ajouter manuellement" @click="openManualDialog">
<q-tooltip>Ajouter un destinataire en saisissant les champs (sans passer par CSV)</q-tooltip>
</q-btn>
</div>
<q-table
:rows="recipients" :columns="recipientColumns" row-key="row_index"
@ -232,7 +241,8 @@
</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" />
<q-chip v-if="props.row.manual" dense color="indigo-5" text-color="white" size="sm" icon="edit" label="manuel" />
<q-chip v-else-if="props.row.customer_id" dense color="positive" text-color="white" size="sm" :label="props.row.match_method" />
<q-chip v-else dense color="warning" text-color="white" size="sm" label="non lié" />
</q-td>
</template>
@ -335,6 +345,71 @@
</q-stepper>
<!-- Manual-add dialog push a recipient into recipients[] without going
through CSV parsing. Useful for ad-hoc gifts, retroactive top-ups,
or test sends to internal stakeholders. The matchCustomer() lookup
that the CSV path does is skipped here customer_id stays empty
unless the user manually pastes it. -->
<q-dialog v-model="manualOpen" persistent>
<q-card style="min-width: 480px; max-width: 640px;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6"><q-icon name="person_add" class="q-mr-sm" />Ajouter un destinataire</div>
<q-space />
<q-btn flat dense round icon="close" v-close-popup />
</q-card-section>
<q-card-section>
<q-form @submit="submitManualRow" class="q-gutter-sm">
<div class="row q-col-gutter-sm">
<q-input v-model="manualRow.firstname" label="Prénom *" outlined dense
class="col-12 col-sm-6" :rules="[v => !!v || 'requis']" autofocus />
<q-input v-model="manualRow.lastname" label="Nom" outlined dense
class="col-12 col-sm-6" />
</div>
<q-input v-model="manualRow.email" label="Email *" outlined dense type="email"
:rules="[v => !!v || 'requis', v => /.+@.+\..+/.test(v) || 'email invalide']" />
<q-input v-model="manualRow.gift_url" label="Lien-cadeau Giftbit *" outlined dense
placeholder="https://gft.link/..."
:rules="[v => !!v || 'requis', v => /^https?:\/\//.test(v) || 'doit commencer par http:// ou https://']" />
<q-input v-model="manualRow.civic_address" label="Adresse civique (pour {{description}})" outlined dense
placeholder="25 Rue des Hirondelles" />
<div class="row q-col-gutter-sm">
<q-input v-model="manualRow.city" label="Ville" outlined dense
placeholder="Ste-Clotilde" class="col-12 col-sm-7" />
<q-input v-model="manualRow.postal_code" label="Code postal" outlined dense
placeholder="J0L 1W0" class="col-12 col-sm-5" />
</div>
<div class="row q-col-gutter-sm">
<q-select v-model="manualRow.language" :options="languageOptions" emit-value map-options
label="Langue du template" outlined dense class="col-12 col-sm-6" />
<q-input v-model="manualRow.phone" label="Téléphone (optionnel)" outlined dense class="col-12 col-sm-6" />
</div>
<div class="row q-col-gutter-sm">
<q-input v-model="manualRow.amount" label="Montant affiché dans le courriel" outlined dense
:placeholder="`défaut: ${params.amount}`" class="col-12 col-sm-6">
<q-tooltip><span v-pre>Texte qui apparaîtra à la place de la variable {{amount}}. Laisse vide pour utiliser le montant de la campagne.</span></q-tooltip>
</q-input>
<q-input v-model.number="manualRow.gift_value_cents" type="number"
label="Valeur en cents (rapport)" outlined dense placeholder="5000"
class="col-12 col-sm-6" /></div>
<div class="text-caption text-grey-7 q-mt-xs">
<q-icon name="info" size="14px" /> Ville et code postal ne s'affichent pas dans le courriel
ils servent à éviter une confusion entre deux clients du même nom dans le tableau ci-dessous
et dans le rapport CSV.
</div>
<div class="text-caption text-grey-7 q-mt-xs">
<q-icon name="info" size="14px" /> Aucun match ERPNext n'est tenté.
<span v-pre>Seule l'adresse civique apparaît dans le courriel (variable <code>{{description}}</code>).</span>
</div>
<div class="row q-mt-md">
<q-space />
<q-btn flat label="Annuler" v-close-popup class="q-mr-sm" />
<q-btn unelevated color="primary" type="submit" icon="add" label="Ajouter" />
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
<!-- Preview dialog renders the actual template with the first sendable
recipient's data + the campaign params. Lets the user verify the
visual + content WITHOUT firing any emails. Toggleable FR/EN since
@ -386,7 +461,7 @@ const params = ref({
name: `Campagne ${new Date().toISOString().slice(0,10)}`,
amount: '60 $',
commitment_months: 3,
subject: '🎁 Un cadeau pour vous, de la part de TARGO',
subject: '🎁 Un cadeau pour toi, de la part de TARGO',
from: 'TARGO <support@targointernet.com>',
expiry: '',
throttle_ms: 600,
@ -415,6 +490,7 @@ const recipientColumns = [
{ name: 'lastname', label: 'Nom', field: 'lastname', align: 'left' },
{ name: 'email', label: 'Email', field: 'email', align: 'left' },
{ name: 'civic_address', label: 'Adresse', field: 'civic_address', align: 'left' },
{ name: 'city', label: 'Ville', field: r => r.city || r.postal_code || '', align: 'left' },
{ name: 'gift_url', label: 'Lien-cadeau', field: 'gift_url', align: 'left' },
{ name: 'language', label: 'Langue', field: 'language', align: 'left' },
{ name: 'match', label: 'Match', field: 'match_method', align: 'left' },
@ -609,6 +685,74 @@ async function goPreview () {
}
}
// Skip CSV parsing entirely empty recipients list, user adds rows manually
// via the "Ajouter manuellement" button on Step 2. The Suivant button is
// disabled if no rows, so the user can't accidentally proceed with nothing.
function goManual () {
recipients.value = []
unpairedContacts.value = []
unusedGifts.value = []
step.value = 2
// Open the dialog immediately the most likely next action is adding a row
openManualDialog()
}
// Manual recipient entry
// Lets the user add a recipient by typing the fields directly instead of
// importing from CSV. Useful for: small ad-hoc gifts, replacement sends after
// a bounce, internal QA test sends, or topping up an existing CSV import
// with a missed contact. No ERPNext matching is attempted on these rows.
const manualOpen = ref(false)
const languageOptions = [
{ label: '🇫🇷 Français', value: 'fr' },
{ label: '🇺🇸 English', value: 'en' },
]
function emptyManualRow () {
return {
firstname: '', lastname: '', email: '', phone: '',
civic_address: '', city: '', postal_code: '', language: 'fr',
gift_url: '', gift_value_cents: null, amount: '',
}
}
const manualRow = ref(emptyManualRow())
function openManualDialog () {
manualRow.value = emptyManualRow()
manualOpen.value = true
}
function submitManualRow () {
// Determine next row_index. CSV-imported rows start at 1; manuals continue
// the sequence so #N reads naturally regardless of source.
const maxIdx = recipients.value.reduce((m, r) => Math.max(m, r.row_index || 0), 0)
recipients.value.push({
row_index: maxIdx + 1,
firstname: (manualRow.value.firstname || '').trim(),
lastname: (manualRow.value.lastname || '').trim(),
email: (manualRow.value.email || '').trim().toLowerCase(),
phone: (manualRow.value.phone || '').trim(),
civic_address: (manualRow.value.civic_address || '').trim(),
city: (manualRow.value.city || '').trim(),
postal_code: (manualRow.value.postal_code || '').trim().toUpperCase(),
language: manualRow.value.language || 'fr',
gift_url: (manualRow.value.gift_url || '').trim(),
gift_value_cents: manualRow.value.gift_value_cents || null,
// Per-recipient amount override. Empty string falls back to campaign
// params.amount in the worker. Useful for manuals on a mixed-amount campaign.
amount: (manualRow.value.amount || '').trim() || null,
giftbit_uuid: null,
// Flag for downstream code + table display so a "manuel" chip can be shown
// and the match-method column doesn't read "non lié" misleadingly.
manual: true,
match_method: 'manuel',
customer_id: null,
status: 'pending',
excluded: false,
})
manualOpen.value = false
$q.notify({ type: 'positive', message: 'Destinataire ajouté' })
}
async function launchSend () {
sending.value = true
try {

View File

@ -76,7 +76,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<tbody>
<tr>
<td style="display:none !important;visibility:hidden;mso-hide:all;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
Like you, we believe in steady connections — both the fiber kind and the human kind.
Because great connections aren't just about fiber — they're about people too.
</td>
</tr>
@ -102,7 +102,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px;font-family:'Plus Jakarta Sans', sans-serif;" align="left">
<div>
<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">Like you, we believe in steady connections — both the fiber kind and the human kind.</div>
<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">Because great connections aren't just about fiber — they're about people too.</div>
<div
aria-label="Your exclusive offer from TARGO" aria-roledescription="email" role="article" lang="und" dir="auto" style="word-spacing:normal;background-color:#F5FAF7;"
@ -221,12 +221,12 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:14px;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;"
>Summer's here, and we've got something special just for you — but only for a little while.</div>
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;"
>Summer is here, and so is something special — for a limited time.</div>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:left;color:#1B2E24;"
>Thanks for keeping it local — it means more than you think.<br />
Like you, we believe in steady connections — both the fiber kind and the human kind.</div>
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:justify;color:#1B2E24;"
>Thank you for choosing local. Your support helps keep our community connected.<br />
Because great connections aren't just about fiber — they're about people too.</div>
</td>
</tr>
@ -237,7 +237,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;">Because our customers trust us, we're now able to offer the <strong>fastest plans around</strong>, with speeds up to <strong>3.5&nbsp;Gbit/s</strong>.<br />
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;">Because our customers trust us, we're now able to offer the <strong>fastest plans around</strong>, with speeds up to <strong>3.5&nbsp;Gbit/s</strong>.<br />
Whether you're looking for more speed, want to beat another offer, or just need to optimize your gear, don't be shy! We're right next door — and we genuinely love lending a hand.</div>
</td>
@ -257,6 +257,60 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ COMPACT INFO CARD (was 3 pills) ════════ -->
@ -299,7 +353,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:700;line-height:1.5;text-align:left;color:#1B2E24;"
>🎁 {{amount}} to spend at hundreds of your favorite stores<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" width="32" alt="Amazon" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">and more</span></span></div>
>🎁 {{amount}} to spend at hundreds of your favorite stores<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">and more</span></span></div>
</td>
</tr>
@ -347,60 +401,6 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ BIG GREEN CTA BUTTON ════════ -->
@ -744,11 +744,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:12px;line-height:1.55;text-align:center;color:#64748B;"
>You're getting this email because you're a TARGO customer at <strong style="color:#1B2E24;">{{description}}</strong>.<br />
Got a question? Shoot us an email at
Got a question? Feel free to email us at
<a href="mailto:support@targo.ca" style="color:#00C853;text-decoration:none;">support@targo.ca</a>
or give us a call at
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514&nbsp;448-0773</a>.
We're here 7&nbsp;days/week to help!</div>
or call us at
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514-448-0773</a>.</div>
</td>
</tr>

File diff suppressed because one or more lines are too long

View File

@ -221,10 +221,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:14px;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;"
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;"
>Avec l'arrivée de l'été, voici un <strong>cadeau pour toi, disponible pour un temps limité</strong>.</div>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:left;color:#1B2E24;"
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:justify;color:#1B2E24;"
>On veut te remercier pour ta loyauté envers l'achat local.<br />
Comme toi, on aime les connexions stables et les relations durables.</div>
@ -237,7 +237,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;">Grâce à la confiance de nos clients, on offre maintenant les forfaits à <strong>la plus haute vitesse dans le secteur</strong>, jusqu'à <strong>3.5&nbsp;Gbit/s</strong>.<br />
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;">Grâce à la confiance de nos clients, on offre maintenant les forfaits à <strong>la plus haute vitesse dans le secteur</strong>, jusqu'à <strong>3.5&nbsp;Gbit/s</strong>.<br />
Que tu souhaites plus de vitesse, battre une autre offre ou faire optimiser des équipements, n'hésite pas. On est juste à côté et on aime aider.</div>
</td>
@ -257,6 +257,60 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ COMPACT INFO CARD (was 3 pills) ════════ -->
@ -299,7 +353,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:700;line-height:1.5;text-align:left;color:#1B2E24;"
>🎁 {{amount}} chez des centaines de marques<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" width="32" alt="Amazon" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">et plus</span></span></div>
>🎁 {{amount}} chez des centaines de marques<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">et plus</span></span></div>
</td>
</tr>
@ -347,60 +401,6 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ BIG GREEN CTA BUTTON ════════ -->
@ -744,11 +744,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:12px;line-height:1.55;text-align:center;color:#64748B;"
>Tu reçois ce courriel parce que tu es client(e) TARGO à <strong style="color:#1B2E24;">{{description}}</strong>.<br />
Une question ? Écris à
Une question ? N'hésite pas à nous écrire à
<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&nbsp;448-0773</a>.
Support&nbsp;7j/7.</div>
ou nous appeler au
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514-448-0773</a>.</div>
</td>
</tr>

File diff suppressed because one or more lines are too long

View File

@ -805,13 +805,30 @@ async function sendCampaignAsync (id) {
const lang = (r.language || 'fr').toLowerCase().split('-')[0]
const tplText = getTpl(lang)
// Per-recipient amount override. Precedence:
// 1. r.amount — explicit override typed in the manual-add dialog
// 2. r.gift_value_cents → "$X" formatted (when CSV import set this)
// 3. p.amount — campaign default ("60 $" etc.)
// 4. "50 $" hard fallback
// This matters for manual recipients where a user pastes a $50 Giftbit
// link into a campaign whose params.amount defaults to "60 $".
let displayAmount = p.amount || '50 $'
if (r.amount) {
displayAmount = r.amount
} else if (r.gift_value_cents) {
const cents = Number(r.gift_value_cents) || 0
// Cents are exact dollars when divisible, else 2-decimal
displayAmount = cents % 100 === 0
? `${cents / 100} $`
: `${(cents / 100).toFixed(2)} $`
}
const vars = {
firstname: r.firstname || (lang === 'en' ? 'dear customer' : 'cher client'),
lastname: r.lastname || '',
email: r.email,
description: r.civic_address || '',
gift_url: r.gift_url,
amount: p.amount || '50 $',
amount: displayAmount,
expiry: p.expiry || '',
commitment_months: p.commitment_months || '3',
year: new Date().getFullYear(),
@ -834,7 +851,7 @@ async function sendCampaignAsync (id) {
try {
sendRes = await email.sendEmail({
to,
subject: p.subject || 'Un cadeau pour vous, de la part de TARGO',
subject: p.subject || 'Un cadeau pour toi, de la part de TARGO',
html,
from: p.from || cfg.MAIL_FROM,
headers: { 'X-MJ-CustomID': customId },
@ -1488,6 +1505,47 @@ async function handle (req, res, method, path) {
// ── Per-campaign wildcard routes (MUST stay below the /templates and
// /webhook fixed paths above, otherwise the wildcard captures them) ─────
// GET /campaigns/:id/report.csv — per-recipient report download
// Columns chosen for operational follow-up (resend, refund, support):
// row, firstname, lastname, email, phone, language, customer_id, civic_address,
// postal_code, gift_value_cents, gift_url, giftbit_uuid, status, excluded,
// sent_at, opened_at, clicked_at, mailjet_uuid, error
// RFC 4180: comma-separated, CRLF endings, fields with comma/quote/newline get
// wrapped in double quotes with embedded " doubled up. We prepend a UTF-8 BOM
// so Excel auto-detects encoding and renders accents correctly.
const reportMatch = path.match(/^\/campaigns\/([^/]+)\/report\.csv$/)
if (reportMatch && method === 'GET') {
const c = loadCampaign(reportMatch[1])
if (!c) return json(res, 404, { error: 'not found' })
const headers = [
'row', 'firstname', 'lastname', 'email', 'phone', 'language', 'customer_id',
'civic_address', 'city', 'postal_code', 'gift_value_cents', 'gift_url', 'giftbit_uuid',
'status', 'excluded', 'sent_at', 'opened_at', 'clicked_at', 'mailjet_uuid', 'error',
]
const esc = (v) => {
if (v == null) return ''
const s = String(v)
return /[",\r\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s
}
const lines = [headers.join(',')]
for (const r of (c.recipients || [])) {
lines.push([
r.row_index, r.firstname, r.lastname, r.email, r.phone, r.language, r.customer_id,
r.civic_address, r.city, r.postal_code, r.gift_value_cents, r.gift_url, r.giftbit_uuid,
r.status, r.excluded ? 'true' : 'false', r.sent_at, r.opened_at, r.clicked_at,
r.mailjet_uuid, r.error,
].map(esc).join(','))
}
const csv = '' + lines.join('\r\n') + '\r\n'
const safeName = (c.name || c.id).replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 80)
res.writeHead(200, {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': `attachment; filename="campaign-${safeName}.csv"`,
'Cache-Control': 'no-store',
})
return res.end(csv)
}
// GET /campaigns/:id — full detail
const detailMatch = path.match(/^\/campaigns\/([^/]+)$/)
if (detailMatch && method === 'GET') {

View File

@ -76,7 +76,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<tbody>
<tr>
<td style="display:none !important;visibility:hidden;mso-hide:all;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
Like you, we believe in steady connections — both the fiber kind and the human kind.
Because great connections aren't just about fiber — they're about people too.
</td>
</tr>
@ -102,7 +102,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px;font-family:'Plus Jakarta Sans', sans-serif;" align="left">
<div>
<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">Like you, we believe in steady connections — both the fiber kind and the human kind.</div>
<div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">Because great connections aren't just about fiber — they're about people too.</div>
<div
aria-label="Your exclusive offer from TARGO" aria-roledescription="email" role="article" lang="und" dir="auto" style="word-spacing:normal;background-color:#F5FAF7;"
@ -221,12 +221,12 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:14px;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;"
>Summer's here, and we've got something special just for you — but only for a little while.</div>
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;"
>Summer is here, and so is something special — for a limited time.</div>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:left;color:#1B2E24;"
>Thanks for keeping it local — it means more than you think.<br />
Like you, we believe in steady connections — both the fiber kind and the human kind.</div>
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:justify;color:#1B2E24;"
>Thank you for choosing local. Your support helps keep our community connected.<br />
Because great connections aren't just about fiber — they're about people too.</div>
</td>
</tr>
@ -237,7 +237,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;">Because our customers trust us, we're now able to offer the <strong>fastest plans around</strong>, with speeds up to <strong>3.5&nbsp;Gbit/s</strong>.<br />
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;">Because our customers trust us, we're now able to offer the <strong>fastest plans around</strong>, with speeds up to <strong>3.5&nbsp;Gbit/s</strong>.<br />
Whether you're looking for more speed, want to beat another offer, or just need to optimize your gear, don't be shy! We're right next door — and we genuinely love lending a hand.</div>
</td>
@ -257,6 +257,60 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ COMPACT INFO CARD (was 3 pills) ════════ -->
@ -299,7 +353,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:700;line-height:1.5;text-align:left;color:#1B2E24;"
>🎁 {{amount}} to spend at hundreds of your favorite stores<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" width="32" alt="Amazon" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">and more</span></span></div>
>🎁 {{amount}} to spend at hundreds of your favorite stores<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">and more</span></span></div>
</td>
</tr>
@ -347,60 +401,6 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ BIG GREEN CTA BUTTON ════════ -->
@ -744,11 +744,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:12px;line-height:1.55;text-align:center;color:#64748B;"
>You're getting this email because you're a TARGO customer at <strong style="color:#1B2E24;">{{description}}</strong>.<br />
Got a question? Shoot us an email at
Got a question? Feel free to email us at
<a href="mailto:support@targo.ca" style="color:#00C853;text-decoration:none;">support@targo.ca</a>
or give us a call at
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514&nbsp;448-0773</a>.
We're here 7&nbsp;days/week to help!</div>
or call us at
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514-448-0773</a>.</div>
</td>
</tr>

File diff suppressed because one or more lines are too long

View File

@ -221,10 +221,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:14px;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;"
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;"
>Avec l'arrivée de l'été, voici un <strong>cadeau pour toi, disponible pour un temps limité</strong>.</div>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:left;color:#1B2E24;"
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:500;line-height:1.5;text-align:justify;color:#1B2E24;"
>On veut te remercier pour ta loyauté envers l'achat local.<br />
Comme toi, on aime les connexions stables et les relations durables.</div>
@ -237,7 +237,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
align="left" style="font-size:0px;padding:10px 25px;padding-bottom:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#374151;">Grâce à la confiance de nos clients, on offre maintenant les forfaits à <strong>la plus haute vitesse dans le secteur</strong>, jusqu'à <strong>3.5&nbsp;Gbit/s</strong>.<br />
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:justify;color:#374151;">Grâce à la confiance de nos clients, on offre maintenant les forfaits à <strong>la plus haute vitesse dans le secteur</strong>, jusqu'à <strong>3.5&nbsp;Gbit/s</strong>.<br />
Que tu souhaites plus de vitesse, battre une autre offre ou faire optimiser des équipements, n'hésite pas. On est juste à côté et on aime aider.</div>
</td>
@ -257,6 +257,60 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ COMPACT INFO CARD (was 3 pills) ════════ -->
@ -299,7 +353,7 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;font-weight:700;line-height:1.5;text-align:left;color:#1B2E24;"
>🎁 {{amount}} chez des centaines de marques<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/31ffdf91-d2de-4ced-8b99-ad2221695abe/content" width="32" alt="Amazon" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="display:inline-block;border:0;margin-right:6px;vertical-align:middle;height:auto;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">et plus</span></span></div>
>🎁 {{amount}} chez des centaines de marques<br><br><span style="display:inline-block;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/4b0b2a4a-5f99-416c-8873-8d3e4389b6f7/content" width="32" alt="Tim Hortons" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/14df433d-583c-4602-a403-d47ee84966a6/content" width="32" alt="Walmart" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/b8d3db5a-d39e-43ce-a84a-2f5dddbf0192/content" width="32" alt="Home Depot" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/9c9dfa18-2a3a-414a-b5ad-16d490c961b5/content" width="32" alt="IGA" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><img src="https://xqy3m.mjt.lu/img2/xqy3m/a1e5f032-a192-4499-97ba-53b939712fa9/content" width="32" alt="Home Hardware" style="width:32px;max-width:32px;height:auto;display:inline-block;border:0;margin-right:6px;vertical-align:middle;"><span style="font-size:13px;color:#64748B;vertical-align:middle;font-weight:400;">et plus</span></span></div>
</td>
</tr>
@ -347,60 +401,6 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ OPTION 1 CHIP ════════ -->
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"
>
<tbody>
<tr>
<td
style="border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb;border-top:1px solid #eef0ee;direction:ltr;font-size:0px;padding:18px 36px 8px;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:526px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="left" style="font-size:0px;padding:0;word-break:break-word;"
>
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:16px;line-height:1.5;text-align:left;color:#1B2E24;"
><span style="display:inline-block;background:#E6F9EE;color:#00C853;font-size:13px;font-weight:700;padding:5px 12px;border-radius:6px;">✅ Option 1</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
<!-- ════════ BIG GREEN CTA BUTTON ════════ -->
@ -744,11 +744,10 @@ table, td { color: #1B2E24; } #u_body a { color: #00C853; text-decoration: under
<div
style="font-family:Plus Jakarta Sans, Helvetica, Arial, sans-serif;font-size:12px;line-height:1.55;text-align:center;color:#64748B;"
>Tu reçois ce courriel parce que tu es client(e) TARGO à <strong style="color:#1B2E24;">{{description}}</strong>.<br />
Une question ? Écris à
Une question ? N'hésite pas à nous écrire à
<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&nbsp;448-0773</a>.
Support&nbsp;7j/7.</div>
ou nous appeler au
<a href="tel:5144480773" style="color:#00C853;text-decoration:none;">514-448-0773</a>.</div>
</td>
</tr>

File diff suppressed because one or more lines are too long