gigafibre-fsm/services/targo-hub/scripts/gen-rating-invite-templates.js
louispaulb 6628eaaa5b feat(ops+hub): sender picker, per-user notifs, editable rating emails, planif & tickets UX
- Comms: sélecteur d'expéditeur « De: » (défaut groupe Support TARGO) via resolveSendFrom + alias vérifiés
- Notifs: prefs de feeds PAR utilisateur (/conversations/notif-prefs) + cloche à bascules ; boot tooltip-ux (clic prioritaire + anti-empilement)
- Courriel: invitation à évaluer = modèle Unlayer éditable (transactional-rating-invite-*) ; test-send via Gmail + expansion {{rating}} ; logo TARGO auto-hébergé sur le magasin d'actifs du hub
- Planif: bloc « sans déplacement » (damier, début de quart, alerte si pas de quart), quart éditable dans l'éditeur de jour, icônes de compétence en vue jour (TV pour télé), clic cellule → éditeur, clic gauche lane → liste / clic droit → menu quart, lien ↗ ticket par job
- Tickets: défaut « Non fermés » + correction du filtre « Mes tickets » (owner)
- Inbox: poll Gmail 1 min + rafraîchir à la demande (poll-now)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 22:41:51 -04:00

115 lines
7.2 KiB
JavaScript

'use strict'
// Génère les modèles « invitation à évaluer » éditables dans Unlayer :
// transactional-rating-invite-<lang>.html (envoyé ; marqueurs {{firstname}} + {{rating}})
// transactional-rating-invite-<lang>.json (design Unlayer natif — schéma calqué sur gift-email-fr-native)
const fs = require('fs')
const path = require('path')
const DIR = process.argv[2] || __dirname
const LOGO = 'https://msg.gigafibre.ca/campaigns/assets/6a2bcb6057d9881c08304a5d2fea8723e2a15b05061fbbe3590bd4a0ee714477.png'
const C = {
fr: {
lang: 'fr',
title: 'Ton avis compte pour nous',
preheader: 'Comment évaluerais-tu ton expérience avec TARGO ?',
greet: 'Bonjour {{firstname}},',
intro: "Merci de faire confiance à TARGO. Si tu as quelques secondes pour nous évaluer, ce serait vraiment apprécié :",
outro: "C'est grâce à ta rétroaction qu'on peut toujours mieux te servir. Si quelque chose ne va pas, réponds simplement à ce courriel — on est là pour t'aider.",
foot: 'TARGO — merci pour ta confiance',
},
en: {
lang: 'en',
title: 'Your feedback matters',
preheader: 'How would you rate your experience with TARGO?',
greet: 'Hi {{firstname}},',
intro: 'Thanks for choosing TARGO. If you have a few seconds to rate us, it would mean a lot:',
outro: "Your feedback helps us serve you better. If something went wrong, just reply to this email — we're here to help.",
foot: 'TARGO — thank you for your trust',
},
}
const STRIP = '<div style="height:4px;line-height:4px;font-size:0;background:#00C853">&nbsp;</div>'
const STARS_BOX = '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#E6F9EE;border:1px solid #b6f0cf;border-radius:12px"><tr><td align="center" style="padding:14px 8px">{{rating}}</td></tr></table>'
// ── HTML envoyé (table email ; identique au design approuvé, avec marqueurs) ──
function htmlDoc (c) {
return `<!doctype html><html lang="${c.lang}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>`
+ `<body style="margin:0;background:#f4f6f8;font-family:-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:#1e293b">`
+ `<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:40px 16px 28px">`
+ `<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;background:#ffffff;border-radius:14px;overflow:hidden;border:1px solid #e2e8f0">`
+ `<tr><td style="background:#ffffff;padding:24px 28px 14px"><img src="${LOGO}" alt="TARGO" width="132" style="width:132px;max-width:132px;height:auto;display:block;border:0"></td></tr>`
+ `<tr><td style="height:4px;background:#00C853;font-size:0;line-height:0;mso-line-height-rule:exactly">&nbsp;</td></tr>`
+ `<tr><td style="padding:28px 30px 4px"><h1 style="margin:0 0 10px;font-size:21px;font-weight:800;color:#0f172a">${c.title}</h1>`
+ `<p style="margin:0 0 4px;font-size:15px;line-height:1.6;color:#475569">${c.greet}</p>`
+ `<p style="margin:0;font-size:15px;line-height:1.6;color:#475569">${c.intro}</p></td></tr>`
+ `<tr><td style="padding:14px 24px 6px">${STARS_BOX}</td></tr>`
+ `<tr><td style="padding:8px 30px 26px"><p style="margin:0;font-size:14px;line-height:1.6;color:#64748b">${c.outro}</p></td></tr>`
+ `<tr><td style="background:#f8fafc;padding:16px 30px;border-top:1px solid #eef2f7;font-size:12px;color:#94a3b8">${c.foot}</td></tr>`
+ `</table></td></tr></table></body></html>`
}
// ── Design Unlayer natif (blocs éditables) ──
const FLAGS = { selectable: true, draggable: true, duplicatable: true, deletable: true, hideable: true, hideDesktop: false, displayCondition: null }
function txt (n, text, o) {
return { id: `u_content_text_${n}`, type: 'text', values: Object.assign({}, FLAGS, {
containerPadding: o.pad, anchor: '', fontWeight: o.weight || 400, fontSize: o.size, color: o.color,
textAlign: o.align || 'left', lineHeight: o.lh || '160%', linkStyle: { inherit: true },
_meta: { htmlID: `u_content_text_${n}`, htmlClassNames: 'u_content_text' }, text }) }
}
function img (n, o) {
return { id: `u_content_image_${n}`, type: 'image', values: Object.assign({}, FLAGS, {
containerPadding: o.pad, anchor: '',
src: { url: o.url, width: o.width, height: 'auto', autoWidth: false, maxWidth: o.width + 'px' },
textAlign: o.align || 'left', altText: o.alt || '', action: { name: 'web', values: { href: '', target: '_blank' } },
_meta: { htmlID: `u_content_image_${n}`, htmlClassNames: 'u_content_image' } }) }
}
function htm (n, html, pad) {
return { id: `u_content_html_${n}`, type: 'html', values: Object.assign({}, FLAGS, {
containerPadding: pad, anchor: '', _meta: { htmlID: `u_content_html_${n}`, htmlClassNames: 'u_content_html' }, html }) }
}
function col (n, contents) {
return { id: `u_column_${n}`, contents, values: Object.assign({}, FLAGS, {
_meta: { htmlID: `u_column_${n}`, htmlClassNames: 'u_column' }, padding: '0px', border: {}, borderRadius: '0px', backgroundColor: '' }) }
}
function row (n, cn, contents, bg) {
return { id: `u_row_${n}`, cells: [1], columns: [col(cn, contents)], values: Object.assign({}, FLAGS, {
columns: false, backgroundColor: bg, columnsBackgroundColor: '',
backgroundImage: { url: '', fullWidth: true, repeat: 'no-repeat', size: 'custom', position: 'center' },
padding: '0px', anchor: '', borderRadius: '', _meta: { htmlID: `u_row_${n}`, htmlClassNames: 'u_row' } }) }
}
function design (c) {
const rows = [
row(1, 1, [
img(1, { pad: '24px 30px 4px', url: LOGO, width: 132, align: 'left', alt: 'TARGO' }),
htm(1, STRIP, '0px'),
txt(1, c.title, { pad: '24px 30px 2px', size: '21px', weight: 700, color: '#0f172a', lh: '130%' }),
txt(2, c.greet, { pad: '10px 30px 2px', size: '15px', color: '#475569' }),
txt(3, c.intro, { pad: '2px 30px 12px', size: '15px', color: '#475569' }),
htm(2, STARS_BOX, '6px 24px 8px'),
txt(4, c.outro, { pad: '10px 30px 26px', size: '14px', color: '#64748b' }),
], '#ffffff'),
row(2, 2, [
txt(5, c.foot, { pad: '16px 30px', size: '12px', color: '#94a3b8' }),
], '#f8fafc'),
]
return {
counters: { u_row: 2, u_column: 2, u_content_text: 5, u_content_image: 1, u_content_html: 2 },
body: { id: 'u_body', rows, values: {
popupPosition: 'center', popupWidth: '600px', popupHeight: 'auto', borderRadius: '10px',
contentAlign: 'center', contentVerticalAlign: 'center', contentWidth: '600px',
fontFamily: { label: 'Arial', value: 'arial,helvetica,sans-serif' },
textColor: '#1e293b', popupBackgroundColor: '#FFFFFF', backgroundColor: '#f4f6f8', preheaderText: c.preheader,
linkStyle: { body: true, linkColor: '#00C853', linkHoverColor: '#005026', linkUnderline: true, linkHoverUnderline: true },
_meta: { htmlID: 'u_body', htmlClassNames: 'u_body' } } },
schemaVersion: 16,
}
}
for (const lang of ['fr', 'en']) {
const c = C[lang]
fs.writeFileSync(path.join(DIR, `transactional-rating-invite-${lang}.html`), htmlDoc(c), 'utf8')
fs.writeFileSync(path.join(DIR, `transactional-rating-invite-${lang}.json`), JSON.stringify(design(c), null, 2), 'utf8')
console.log(`wrote transactional-rating-invite-${lang}.html + .json`)
}