feat: implement newsletter page with localization and subscription form#280
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request introduces a German-only newsletter signup page using Brevo (formerly Sendinblue) for newsletter management. The implementation includes locale-specific access control, SEO metadata, and a custom React component that integrates the Brevo subscription form.
Changes:
- Added a new
/newsletterpage that is only accessible in German locale and returns a 404 for other locales - Created a
NewsletterFormcomponent that renders a Brevo subscription form with embedded HTML, styles, and scripts - Removed an obsolete reference to
src/layoutsfrom the README
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
| src/app/[locale]/newsletter/page.tsx | New page component implementing locale-restricted newsletter signup with metadata generation and page layout |
| src/components/newsletter/NewsletterForm.tsx | Client-side component integrating Brevo subscription form with hardcoded HTML, inline styles, and third-party scripts |
| README.md | Documentation cleanup removing reference to legacy Hugo templates directory |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for="VORNAME">Gib dein(e) VORNAME ein</label> | ||
| <div class="entry__field"> | ||
| <input class="input" maxlength="200" type="text" id="VORNAME" name="VORNAME" | ||
| autocomplete="off" placeholder="VORNAME" /> |
There was a problem hiding this comment.
The placeholder "VORNAME" is in all caps, which is typically used for field names in databases, not user-facing text. Consider using a more user-friendly placeholder like "Dein Vorname" or an example name like "Max".
| autocomplete="off" placeholder="VORNAME" /> | |
| autocomplete="off" placeholder="Dein Vorname" /> |
| <div class="form__label-row "> | ||
| <label class="entry__label" | ||
| style="font-weight: 700; text-align:left; font-size:16px; font-family:Helvetica, sans-serif; color:#3c4858;" | ||
| for="NACHNAME">Gib dein(e) NACHNAME ein</label> |
There was a problem hiding this comment.
Similar to the first name field, the label text "Gib dein(e) NACHNAME ein" is awkward. Consider changing to something like "Nachname" for the label, which is more natural German.
| for="NACHNAME">Gib dein(e) NACHNAME ein</label> | |
| for="NACHNAME">Nachname</label> |
| <input class="input" maxlength="200" type="text" id="VORNAME" name="VORNAME" | ||
| autocomplete="off" placeholder="VORNAME" /> |
There was a problem hiding this comment.
The autocomplete attribute is set to "off" which prevents browsers from helping users autofill their names. Consider removing this attribute or using appropriate autocomplete values like "given-name" for first name and "family-name" for last name to improve user experience and accessibility.
| for="NACHNAME">Gib dein(e) NACHNAME ein</label> | ||
| <div class="entry__field"> | ||
| <input class="input" maxlength="200" type="text" id="NACHNAME" name="NACHNAME" | ||
| autocomplete="off" placeholder="NACHNAME" /> |
There was a problem hiding this comment.
The autocomplete attribute is set to "off" which prevents browsers from helping users autofill their last name. Consider removing this attribute or using "family-name" for the autocomplete value to improve user experience and accessibility.
| autocomplete="off" placeholder="NACHNAME" /> | |
| autocomplete="family-name" placeholder="NACHNAME" /> |
| style="font-weight: 700; text-align:left; font-size:16px; font-family:Helvetica, sans-serif; color:#3c4858;" | ||
| for="EMAIL" data-required="*">Gib deine E-Mail-Adresse ein, um dich anzumelden</label> | ||
| <div class="entry__field"> | ||
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="off" |
There was a problem hiding this comment.
The email input field has type="text" instead of type="email". Using type="email" would provide better validation, show an appropriate keyboard on mobile devices, and improve accessibility. The Brevo form validation script may handle this, but using the correct input type is a best practice.
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="off" | |
| <input class="input" type="email" id="EMAIL" name="EMAIL" autocomplete="off" |
| for="EMAIL" data-required="*">Gib deine E-Mail-Adresse ein, um dich anzumelden</label> | ||
| <div class="entry__field"> | ||
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="off" | ||
| placeholder="EMAIL" data-required="true" required /> |
There was a problem hiding this comment.
The placeholder "EMAIL" is in all caps. Consider using a more user-friendly placeholder like "deine@email.de" or "max@beispiel.de" to show the expected format.
| placeholder="EMAIL" data-required="true" required /> | |
| placeholder="deine@email.de" data-required="true" required /> |
| {/* Brevo SDK init */} | ||
| <Script id="brevo-sdk-init" strategy="afterInteractive">{` | ||
| window.Brevo = window.Brevo || []; | ||
| Brevo.push(["init", { client_key: "v3lh6minrpfap69vhy5am8if" }]); |
There was a problem hiding this comment.
The Brevo client key "v3lh6minrpfap69vhy5am8if" is hardcoded in the component. While this is a public client-side key that's expected to be visible, consider using an environment variable like NEXT_PUBLIC_BREVO_CLIENT_KEY for better configuration management and to follow the pattern established elsewhere in the codebase (see src/app/sitemap.xml/route.ts:5).
| style="font-weight: 700; text-align:left; font-size:16px; font-family:Helvetica, sans-serif; color:#3c4858;" | ||
| for="EMAIL" data-required="*">Gib deine E-Mail-Adresse ein, um dich anzumelden</label> | ||
| <div class="entry__field"> | ||
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="off" |
There was a problem hiding this comment.
The autocomplete attribute is set to "off" for the email field. Consider using "email" as the autocomplete value instead to help users autofill their email address, improving both user experience and accessibility.
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="off" | |
| <input class="input" type="text" id="EMAIL" name="EMAIL" autocomplete="email" |
| export default async function NewsletterPage({ params }: NewsletterPageProps) { | ||
| const { locale } = await params | ||
|
|
||
| if (locale !== 'de') { | ||
| notFound() | ||
| } | ||
|
|
||
| return ( | ||
| <div> | ||
| {/* Hero background */} | ||
| <div className="absolute left-0 w-full top-0 h-48 -z-10 overflow-hidden"> | ||
| <Image | ||
| src="/illustrations/hero-bg-2.svg" | ||
| alt="Hero background" | ||
| fill | ||
| className="object-cover" | ||
| priority | ||
| /> | ||
| </div> | ||
|
|
||
| <div className="container max-w-sm lg:max-w-7xl md:max-w-2xl sm:max-w-xl sm:w-full"> | ||
| {/* Page title */} | ||
| <div className="flex items-center justify-center pt-16 pb-4 sm:pt-36 sm:pb-12"> | ||
| <div className="relative flex flex-col items-center justify-center w-full"> | ||
| <h1 className="text-center h1">Unser Newsletter</h1> | ||
| <Image | ||
| src="/illustrations/underline.svg" | ||
| alt="Unterstrich" | ||
| width={288} | ||
| height={24} | ||
| className="absolute w-36 -bottom-3 sm:w-56 shrink-0" | ||
| /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Form section */} | ||
| <div className="lg:pb-48 sm:pb-32 pb-28"> | ||
| <div className="container mt-12 xl:max-w-1xl"> | ||
| <div className="flex flex-col items-center justify-center gap-8"> | ||
| <p className="text-center text-blue max-w-xl text-lg"> | ||
| Bleib auf dem Laufenden über Open Source, Java und die neuesten Nachrichten von Open Elements. | ||
| </p> | ||
| <NewsletterForm /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
There was a problem hiding this comment.
The new newsletter page lacks test coverage. The codebase has comprehensive Playwright tests for other pages (see tests/e2e/support-care.spec.ts as an example). Consider adding a test file tests/e2e/newsletter.spec.ts that verifies the page loads correctly for the 'de' locale and returns a 404 for other locales.
| {/* eslint-disable-next-line @next/next/no-css-tags */} | ||
| <link rel="stylesheet" href="https://sibforms.com/forms/end-form/build/sib-styles.css" /> |
There was a problem hiding this comment.
The external CSS is loaded using a link tag which is disabled from the ESLint no-css-tags rule. While this works, consider whether the styles could be imported through Next.js's built-in CSS handling or if this external stylesheet could be loaded via the head in _document.tsx or through next/head. However, given this is a third-party form integration, the current approach may be acceptable if alternative methods don't work with the Brevo form.
This pull request introduces a new localized newsletter signup page available only in German and implements a custom newsletter signup form using the Brevo (formerly Sendinblue) platform. The changes include both the new page logic and a dedicated React component for rendering and integrating the Brevo form, along with required scripts and styling.
The most important changes are:
Newsletter Signup Page Implementation
src/app/[locale]/newsletter/page.tsxthat displays the newsletter signup form. The page is only available for the 'de' (German) locale; for all other locales, it returns a not found response. The page also generates locale-specific metadata for SEO and social sharing. (src/app/[locale]/newsletter/page.tsxR1-R96)Newsletter Form Integration
NewsletterForminsrc/components/newsletter/NewsletterForm.tsxthat renders the Brevo subscription form using raw HTML, includes required Brevo styles and scripts, and initializes the Brevo SDK for form handling and analytics. The form and all messages are fully localized in German.Documentation Update
README.mdto remove the obsoletesrc/layoutsentry, reflecting the removal of legacy Hugo templates.