diff --git a/landing/app/pages/newsletter-bestaetigt.vue b/landing/app/pages/newsletter-bestaetigt.vue new file mode 100644 index 0000000..e3905d6 --- /dev/null +++ b/landing/app/pages/newsletter-bestaetigt.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/landing/i18n/locales/de.json b/landing/i18n/locales/de.json index a1a7e63..59cfb54 100644 --- a/landing/i18n/locales/de.json +++ b/landing/i18n/locales/de.json @@ -289,8 +289,8 @@ "description": "Erhalten Sie Updates zur Entwicklung der Web-App für die IT-Mitbestimmung und seien Sie unter den Ersten, die vom Release oder neuen Funktionen erfahren.", "placeholder": "Ihre E-Mail-Adresse", "submit": "Anmelden", - "submitted": "Angemeldet!", - "success": "Vielen Dank! Wir halten Sie auf dem Laufenden.", + "submitted": "E-Mail gesendet!", + "success": "Bitte bestätigen Sie Ihr Abonnement über den Link in der E-Mail, die wir Ihnen soeben gesendet haben.", "privacyNote": "Mit der Anmeldung stimmen Sie unserer {link} zu. Wir versenden keinen Spam.", "privacyLink": "Datenschutzerklärung", "validation": { @@ -303,6 +303,15 @@ "noSpam": "Kein Spam" } }, + "newsletterConfirmed": { + "meta": { + "title": "Newsletter bestätigt – GremiumHub", + "description": "Vielen Dank für die Bestätigung Ihres Newsletter-Abonnements." + }, + "title": "Abonnement bestätigt!", + "description": "Vielen Dank! Sie sind jetzt für unseren Newsletter angemeldet und erhalten Updates zur Entwicklung von GremiumHub.", + "backToHome": "Zur Startseite" + }, "expertAccess": { "badge": "Expertennetzwerk", "title": "Externer Sachverstand – Kooperation mit Spezialkanzlei für Betriebsräte", diff --git a/landing/i18n/locales/en.json b/landing/i18n/locales/en.json index 54bff6c..5bc4c72 100644 --- a/landing/i18n/locales/en.json +++ b/landing/i18n/locales/en.json @@ -289,8 +289,8 @@ "description": "Receive updates on the development of GremiumHub and be among the first to learn about new features.", "placeholder": "Your email address", "submit": "Subscribe", - "submitted": "Subscribed!", - "success": "Thank you! We'll keep you updated.", + "submitted": "Email sent!", + "success": "Please confirm your subscription via the link in the email we just sent you.", "privacyNote": "By subscribing, you agree to our {link}. We don't send spam.", "privacyLink": "Privacy Policy", "validation": { @@ -303,6 +303,15 @@ "noSpam": "No spam" } }, + "newsletterConfirmed": { + "meta": { + "title": "Newsletter Confirmed – GremiumHub", + "description": "Thank you for confirming your newsletter subscription." + }, + "title": "Subscription Confirmed!", + "description": "Thank you! You are now subscribed to our newsletter and will receive updates on the development of GremiumHub.", + "backToHome": "Back to Home" + }, "expertAccess": { "badge": "Expert Network", "title": "External Expertise – Directly from the Process", diff --git a/landing/nuxt.config.ts b/landing/nuxt.config.ts index dff1e43..095331c 100644 --- a/landing/nuxt.config.ts +++ b/landing/nuxt.config.ts @@ -11,7 +11,11 @@ export default defineNuxtConfig({ brevoSenderEmail: 'NOT_SET', brevoSenderName: 'NOT_SET', brevoContactEmail: 'NOT_SET', - brevoNewsletterListId: 'NOT_SET' + brevoNewsletterListId: 'NOT_SET', + brevoDoiTemplateId: 'NOT_SET', + public: { + siteUrl: 'NOT_SET' + } }, // Font configuration - Bricolage Grotesque for headings, DM Sans for body diff --git a/landing/server/api/newsletter/subscribe.post.ts b/landing/server/api/newsletter/subscribe.post.ts index 48ba438..d57e232 100644 --- a/landing/server/api/newsletter/subscribe.post.ts +++ b/landing/server/api/newsletter/subscribe.post.ts @@ -29,29 +29,71 @@ export default defineEventHandler(async (event) => { }) } + // Check if DOI template ID is configured + if (!config.brevoDoiTemplateId) { + console.error('BREVO_DOI_TEMPLATE_ID is not configured') + throw createError({ + statusCode: 500, + statusMessage: 'Newsletter service is not configured' + }) + } + + console.log('DOI_TEMPLATE_ID', config.brevoDoiTemplateId) + + // Check if public site URL is configured (for DOI redirect) + if (!config.public.siteUrl) { + console.error('NUXT_PUBLIC_SITE_URL is not configured') + throw createError({ + statusCode: 500, + statusMessage: 'Newsletter service is not configured' + }) + } + + // Check if newsletter list ID is configured + if (!config.brevoNewsletterListId) { + console.error('BREVO_NEWSLETTER_LIST_ID is not configured') + throw createError({ + statusCode: 500, + statusMessage: 'Newsletter service is not configured' + }) + } + + const templateId = parseInt(config.brevoDoiTemplateId, 10) + if (isNaN(templateId)) { + console.error('BREVO_DOI_TEMPLATE_ID is not a valid number') + throw createError({ + statusCode: 500, + statusMessage: 'Newsletter service is not configured' + }) + } + + const listId = parseInt(config.brevoNewsletterListId, 10) + if (isNaN(listId)) { + console.error('BREVO_NEWSLETTER_LIST_ID is not a valid number') + throw createError({ + statusCode: 500, + statusMessage: 'Newsletter service is not configured' + }) + } + + // Build the redirection URL to the confirmation page + const redirectionUrl = `${config.public.siteUrl}/newsletter-bestaetigt` + try { - // Build request body for Brevo Contacts API - const brevoBody: { - email: string - updateEnabled: boolean - listIds?: number[] - } = { + // Build request body for Brevo Double Opt-In API + const brevoBody = { email, - updateEnabled: true + templateId, + redirectionUrl, + includeListIds: [listId] } - // Add list ID if configured - if (config.brevoNewsletterListId) { - const listId = parseInt(config.brevoNewsletterListId, 10) - if (!isNaN(listId)) { - brevoBody.listIds = [listId] - } - } + console.log('Brevo DOI request body:', brevoBody) - const response = await $fetch('https://api.brevo.com/v3/contacts', { + await $fetch('https://api.brevo.com/v3/contacts/doubleOptinConfirmation', { method: 'POST', headers: { - 'accept': 'application/json', + accept: 'application/json', 'api-key': config.brevoApiKey, 'content-type': 'application/json' }, @@ -59,18 +101,18 @@ export default defineEventHandler(async (event) => { }) return { - success: true, - id: (response as { id?: number })?.id + success: true } } catch (error: unknown) { // Handle Brevo API errors const fetchError = error as { statusCode?: number; data?: { code?: string; message?: string } } // Contact already exists (duplicate_parameter error) - treat as success + // User will receive another confirmation email if (fetchError.data?.code === 'duplicate_parameter') { return { success: true, - message: 'Already subscribed' + message: 'Confirmation email sent' } }