diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json index 5c85c8d4d..cadcdadca 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/de.json @@ -91,7 +91,8 @@ "needsRecipients": "Trage Empfänger-Emails in den Settings ein.", "mailSubject": "Bitte ausfüllen: {title}", "mailBody": "{description}Hier kannst du antworten:\n{url}\n\nDanke!", - "confirmSend": "Welle an {n} Empfänger senden? Dein Mail-Programm öffnet sich mit BCC-Liste und Link. Nach dem Versand stempeln wir den Zeitpunkt." + "confirmSend": "Welle an {n} Empfänger senden? Dein Mail-Programm öffnet sich mit BCC-Liste und Link. Nach dem Versand stempeln wir den Zeitpunkt.", + "confirmBulk": "Welle an {n} Empfänger via Mana-Mail senden? Antworten landen direkt in deinem Forms-Inbox." }, "autoSync": { "title": "Auto-Sync — bei Antwort erzeugen", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json index 13c9017c6..769fb87cd 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/en.json @@ -91,7 +91,8 @@ "needsRecipients": "Add recipient emails in the settings.", "mailSubject": "Please fill in: {title}", "mailBody": "{description}Reply here:\n{url}\n\nThanks!", - "confirmSend": "Send wave to {n} recipients? Your mail client opens with the BCC list and link prefilled. We'll stamp the timestamp after." + "confirmSend": "Send wave to {n} recipients? Your mail client opens with the BCC list and link prefilled. We'll stamp the timestamp after.", + "confirmBulk": "Send wave to {n} recipients via Mana Mail? Replies land directly in your forms inbox." }, "autoSync": { "title": "Auto-sync — create on submit", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json index bec3d3ec6..3260f52c9 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/es.json @@ -91,7 +91,8 @@ "needsRecipients": "Añade emails de destinatarios en los ajustes.", "mailSubject": "Por favor rellena: {title}", "mailBody": "{description}Responde aquí:\n{url}\n\n¡Gracias!", - "confirmSend": "¿Enviar ola a {n} destinatarios? Tu cliente de correo se abrirá con la lista BCC y el enlace prellenado. Marcamos la marca de tiempo después." + "confirmSend": "¿Enviar ola a {n} destinatarios? Tu cliente de correo se abrirá con la lista BCC y el enlace prellenado. Marcamos la marca de tiempo después.", + "confirmBulk": "¿Enviar ola a {n} destinatarios vía Mana-Mail? Las respuestas llegan directamente a tu bandeja de Forms." }, "autoSync": { "title": "Auto-sync — crear al recibir respuesta", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json index fbdffdab2..ff1b63cf4 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/fr.json @@ -91,7 +91,8 @@ "needsRecipients": "Ajoute des emails de destinataires dans les paramètres.", "mailSubject": "Merci de remplir : {title}", "mailBody": "{description}Réponds ici :\n{url}\n\nMerci !", - "confirmSend": "Envoyer la vague à {n} destinataires ? Ton client mail s'ouvre avec la liste BCC et le lien préremplis. On marque l'horodatage ensuite." + "confirmSend": "Envoyer la vague à {n} destinataires ? Ton client mail s'ouvre avec la liste BCC et le lien préremplis. On marque l'horodatage ensuite.", + "confirmBulk": "Envoyer la vague à {n} destinataires via Mana-Mail ? Les réponses arrivent directement dans ta boîte Forms." }, "autoSync": { "title": "Auto-sync — créer à la soumission", diff --git a/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json b/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json index e8e07d9ef..a012fc6b0 100644 --- a/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json +++ b/apps/mana/apps/web/src/lib/i18n/locales/forms/it.json @@ -91,7 +91,8 @@ "needsRecipients": "Aggiungi email dei destinatari nelle impostazioni.", "mailSubject": "Per favore compila: {title}", "mailBody": "{description}Rispondi qui:\n{url}\n\nGrazie!", - "confirmSend": "Invia l'ondata a {n} destinatari? Il tuo client di posta si apre con la lista BCC e il link precompilati. Marchiamo l'orario dopo." + "confirmSend": "Invia l'ondata a {n} destinatari? Il tuo client di posta si apre con la lista BCC e il link precompilati. Marchiamo l'orario dopo.", + "confirmBulk": "Invia l'ondata a {n} destinatari via Mana-Mail? Le risposte arrivano direttamente nella tua casella Forms." }, "autoSync": { "title": "Auto-sync — crea al ricevere risposta", diff --git a/apps/mana/apps/web/src/lib/modules/forms/index.ts b/apps/mana/apps/web/src/lib/modules/forms/index.ts index db04b4448..4c1532cac 100644 --- a/apps/mana/apps/web/src/lib/modules/forms/index.ts +++ b/apps/mana/apps/web/src/lib/modules/forms/index.ts @@ -29,6 +29,9 @@ export { export { computeCohort, cohortLabel, sortCohortsDesc } from './lib/cohort'; export type { RecurrenceFrequency } from './lib/cohort'; export { nextWaveDueAt, isWaveDue, buildWaveMailto, parseRecipientEmails } from './lib/wave'; +export { sendWaveViaBulkMail, WavePreconditionError } from './lib/wave-mail'; +export type { WaveBulkSendResult } from './lib/wave-mail'; +export { startWaveScheduler, stopWaveScheduler } from './lib/wave-scheduler'; // ─── Types ─────────────────────────────────────────────── export { diff --git a/apps/mana/apps/web/src/lib/modules/forms/lib/wave-mail.ts b/apps/mana/apps/web/src/lib/modules/forms/lib/wave-mail.ts new file mode 100644 index 000000000..9926b95f9 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/forms/lib/wave-mail.ts @@ -0,0 +1,181 @@ +/** + * Wave bulk-send via mana-mail (M10c). + * + * Headless wave-send for recurring forms: instead of opening a + * mailto: link (the M10b bridge, which requires a user gesture and + * caps at ~50 BCC recipients), we POST directly to mana-mail's + * /v1/mail/bulk-send with a minimal HTML/text payload built from + * the form's title + description + share-link. + * + * Pre-conditions: + * - The form has `recurrence` configured + non-empty + * `recipientEmails` + a published unlisted share-link. + * - The user has filled BroadcastSettings.defaultFromEmail and + * legalAddress in /broadcasts/settings (DSGVO mandate). + * + * If those preconditions aren't met, the caller falls back to the + * M10b mailto bridge. + */ + +import { browser } from '$app/environment'; +import type { BroadcastSettings } from '$lib/modules/broadcasts/types'; +import type { Form } from '../types'; + +export interface WaveBulkSendResult { + campaignId: string; + accepted: number; + delivered: number; + failed: number; + errors: Array<{ email: string; reason: string }>; +} + +export class WavePreconditionError extends Error { + constructor(message: string) { + super(message); + this.name = 'WavePreconditionError'; + } +} + +function getMailUrl(): string { + if (browser) { + const fromWindow = (window as unknown as { __PUBLIC_MANA_MAIL_URL__?: string }) + .__PUBLIC_MANA_MAIL_URL__; + if (fromWindow) return fromWindow; + } + return import.meta.env.PUBLIC_MANA_MAIL_URL || 'http://localhost:3042'; +} + +/** + * Render a minimal HTML body for the wave email. Inline-styled because + * email clients ignore