Dialogue « Envoyer une invitation » → bascule Test / Envoi de masse. Mode masse :
- Choix du MODÈLE de courriel (défaut « Invitation Fête 20 ans (TARGO) » + modèles de
/campaigns/templates) — rend le module réutilisable.
- Audience par FILTRES liste clients (clients actifs / abonnement actif) OU IMPORT CSV
(email seul obligatoire ; firstname/lastname/language/customer_id/phone optionnels, alias FR/EN).
- Bouton « Aperçu de l'audience » → décompte live + échantillon (ex. 7943 clients actifs avec
courriel, 759 sans courriel écartés). Choix canal Mailjet (suivi)/Gmail.
Hub : resolveAudience({mode,filters,csv}) (recherche aveugle clients via erp.list + Service
Subscription pour « abonnement actif » ; dédup + courriel requis) + POST /events/:id/audience.
Filtre territoire retiré (champ ERPNext non peuplé → 0 résultat, éviter la confusion).
⚠️ L'ENVOI DE MASSE réel (création campagne + blast + suivi) reste GATÉ/non branché — bannière
« dernière étape à activer ». Test-à-soi inchangé. 29/29 tests unitaires. Déployé hub + SPA.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
1.7 KiB
JavaScript
30 lines
1.7 KiB
JavaScript
/**
|
|
* api/events.js — Client des endpoints Hub /events (vue staff RSVP).
|
|
* Miroir de services/targo-hub/lib/events.js. Passe par le proxy /ops/hub
|
|
* (jeton HUB_SERVICE_TOKEN injecté) → routes /events gatées (PII).
|
|
*
|
|
* listEvents() → { events:[{id,name,date_iso,active}] }
|
|
* listRsvps(eventId) → { event:{...,public_url}, responses:[], count, headcount, matched, unmatched }
|
|
* deleteRsvp(eventId, key) → { ok }
|
|
* sendInvite(eventId, body) → envoi d'invitations (GATÉ — non branché pour l'instant)
|
|
*/
|
|
import { HUB_URL } from 'src/config/hub'
|
|
|
|
async function hubFetch (path, { method = 'GET', body } = {}) {
|
|
const opts = { method, headers: { 'Content-Type': 'application/json' } }
|
|
if (body) opts.body = JSON.stringify(body)
|
|
const res = await fetch(`${HUB_URL}${path}`, opts)
|
|
const text = await res.text()
|
|
let data
|
|
try { data = text ? JSON.parse(text) : {} } catch { throw new Error(`Réponse invalide de ${path}`) }
|
|
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
|
return data
|
|
}
|
|
|
|
export async function listEvents () { return hubFetch('/events') }
|
|
export async function listRsvps (eventId) { return hubFetch(`/events/${encodeURIComponent(eventId)}/rsvps`) }
|
|
export async function deleteRsvp (eventId, key) { return hubFetch(`/events/${encodeURIComponent(eventId)}/rsvps/${encodeURIComponent(key)}`, { method: 'DELETE' }) }
|
|
export async function sendInvite (eventId, body) { return hubFetch(`/events/${encodeURIComponent(eventId)}/send`, { method: 'POST', body }) }
|
|
export async function previewAudience (eventId, body) { return hubFetch(`/events/${encodeURIComponent(eventId)}/audience`, { method: 'POST', body }) }
|
|
export async function listTemplates () { return hubFetch('/campaigns/templates') }
|