diff --git a/apps/ops/src/modules/campaigns/pages/CampaignDetailPage.vue b/apps/ops/src/modules/campaigns/pages/CampaignDetailPage.vue
index 79e236c..b66e81a 100644
--- a/apps/ops/src/modules/campaigns/pages/CampaignDetailPage.vue
+++ b/apps/ops/src/modules/campaigns/pages/CampaignDetailPage.vue
@@ -17,6 +17,19 @@
:loading="creatingReminder" @click="confirmCreateReminder">
Cloner cette campagne pour les destinataires qui n'ont PAS cliqué le cadeau encore
+
+
+ Modifier le contenu HTML du template dans un nouvel onglet (Unlayer)
+
+
+ Sujet, expéditeur, montant affiché, choix de template (FR/EN), etc.
+
@@ -125,6 +138,59 @@
Campagne introuvable
+
+
+
+
+
+ Éditer les paramètres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pour modifier le contenu du courriel
+ (texte, mise en page, conditions, etc.), clique "Éditer le template"
+ à côté — ça ouvre l'éditeur Unlayer dans un nouvel onglet. Les modifications de template
+ s'appliquent à toutes les campagnes qui utilisent ce template ; pour
+ une version unique à cette campagne, crée d'abord un template variant
+ (+ Nouveau dans l'éditeur) et sélectionne-le ici.
+
+
+
+
+
+
+
+
+
+
@@ -132,7 +198,7 @@
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useQuasar } from 'quasar'
-import { getCampaign, sendCampaign, campaignSseUrl, campaignReportCsvUrl, retryRecipient, createReminderCampaign } from 'src/api/campaigns'
+import { getCampaign, sendCampaign, campaignSseUrl, campaignReportCsvUrl, retryRecipient, createReminderCampaign, updateCampaign, listTemplates } from 'src/api/campaigns'
const route = useRoute()
const router = useRouter()
@@ -143,6 +209,53 @@ const loading = ref(true)
const resending = ref(false)
const creatingReminder = ref(false)
+// Edit-params dialog state. editParams is a snapshot of campaign.params
+// that the form mutates; on save we PATCH only that subset back to the
+// hub, which merges over the existing params on disk.
+const editParamsOpen = ref(false)
+const savingParams = ref(false)
+const editParams = ref({
+ name: '', subject: '', from: '', amount: '', commitment_months: 3,
+ expiry: '', template_fr: 'gift-email-fr', template_en: 'gift-email-en',
+})
+
+// Template lists for the FR/EN dropdowns in the dialog. Refreshed lazily
+// when the dropdown opens so newly-created templates show up without a
+// page reload.
+const loadingTemplates = ref(false)
+const frTemplateOptions = ref([{ label: 'gift-email-fr', value: 'gift-email-fr' }])
+const enTemplateOptions = ref([{ label: 'gift-email-en', value: 'gift-email-en' }])
+async function loadTemplateLists () {
+ loadingTemplates.value = true
+ try {
+ const tpls = await listTemplates()
+ const opt = (t, preferred) => ({
+ label: preferred ? t.name : `${t.name} · sans suffixe de langue`,
+ value: t.name,
+ })
+ const fr = [], en = []
+ for (const t of tpls) {
+ if (!t?.name) continue
+ if (t.name.endsWith('-fr')) fr.push(opt(t, true))
+ else if (t.name.endsWith('-en')) en.push(opt(t, true))
+ else { fr.push(opt(t, false)); en.push(opt(t, false)) }
+ }
+ const sortFn = (a, b) => {
+ const aPref = !a.label.includes('sans suffixe')
+ const bPref = !b.label.includes('sans suffixe')
+ if (aPref !== bPref) return aPref ? -1 : 1
+ return a.value.localeCompare(b.value)
+ }
+ fr.sort(sortFn); en.sort(sortFn)
+ if (fr.length) frTemplateOptions.value = fr
+ if (en.length) enTemplateOptions.value = en
+ } catch (e) {
+ $q.notify({ type: 'warning', message: 'Templates non chargés : ' + e.message })
+ } finally {
+ loadingTemplates.value = false
+ }
+}
+
let es = null
const columns = [
@@ -185,6 +298,14 @@ function giftbitAdminUrl (giftUrl) {
const reportCsvUrl = computed(() => campaignReportCsvUrl(id))
+// Deep link to the template editor for the FR template configured on
+// this campaign (most TARGO customers are FR). The editor route also
+// accepts no name and lets the user pick from a dropdown.
+const templateEditorHref = computed(() => {
+ const t = campaign.value?.params?.template_fr || 'gift-email-fr'
+ return `/ops/#/campaigns/templates/${encodeURIComponent(t)}`
+})
+
// Eligibility for the "Créer une relance" button: campaign sent at least
// once (not draft), it's not itself a reminder, and at least one recipient
// is in a non-clicked / non-revoked / non-expired state. The hub re-checks
@@ -283,6 +404,53 @@ async function retryFailedRow (row) {
}
}
+// Open the params dialog, snapshotting current campaign.params + name
+// into the editable form. Lazily load the template lists so the
+// dropdowns are populated when the dialog mounts.
+function openEditParams () {
+ const p = campaign.value?.params || {}
+ editParams.value = {
+ name: campaign.value?.name || '',
+ subject: p.subject || '',
+ from: p.from || '',
+ amount: p.amount || '',
+ commitment_months: p.commitment_months || 3,
+ expiry: p.expiry || '',
+ template_fr: p.template_fr || 'gift-email-fr',
+ template_en: p.template_en || 'gift-email-en',
+ }
+ editParamsOpen.value = true
+ loadTemplateLists()
+}
+
+// PATCH the campaign with the new name + params. Hub merges params over
+// the existing ones (spread) so unspecified fields stay put. Live UI
+// refresh via load() so the recap + counters reflect the new values.
+async function saveEditParams () {
+ savingParams.value = true
+ try {
+ await updateCampaign(id, {
+ name: editParams.value.name,
+ params: {
+ subject: editParams.value.subject,
+ from: editParams.value.from,
+ amount: editParams.value.amount,
+ commitment_months: editParams.value.commitment_months,
+ expiry: editParams.value.expiry,
+ template_fr: editParams.value.template_fr,
+ template_en: editParams.value.template_en,
+ },
+ })
+ await load()
+ editParamsOpen.value = false
+ $q.notify({ type: 'positive', message: 'Paramètres enregistrés' })
+ } catch (e) {
+ $q.notify({ type: 'negative', message: 'Erreur : ' + e.message })
+ } finally {
+ savingParams.value = false
+ }
+}
+
// Confirmation + creation of the reminder campaign. The hub returns the
// new draft campaign; we navigate to its detail page so the operator can
// review the recipients list and click "Lancer l'envoi" when ready.