This commit is contained in:
Engin committed 2026-09-10 14:31:05 +02:00
1 parent cc2b84dcad
commit 3bda7d6eb6
28 files changed
+2330 -49

No files matched your search

+93
View File
@@ -0,0 +1,93 @@
"use server";
import { transporter } from "@/lib/mailer";
export interface ContactFormState {
success: boolean;
error?: string;
fieldErrors?: Partial<Record<
"unternehmen" | "vorname" | "nachname" | "email" | "telefon" | "nachricht" | "datenschutz",
string
>>;
}
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Erlaubt Ziffern, Leerzeichen, +, -, /, Klammern - deckt gängige Telefonformate ab.
const PHONE_REGEX = /^[0-9+\-/() ]{6,20}$/;
export async function sendContactMessage(
_prevState: ContactFormState,
formData: FormData,
): Promise<ContactFormState> {
const unternehmen = (formData.get("unternehmen") as string | null)?.trim() ?? "";
const vorname = (formData.get("vorname") as string | null)?.trim() ?? "";
const nachname = (formData.get("nachname") as string | null)?.trim() ?? "";
const email = (formData.get("email") as string | null)?.trim() ?? "";
const telefon = (formData.get("telefon") as string | null)?.trim() ?? "";
const nachricht = (formData.get("nachricht") as string | null)?.trim() ?? "";
const datenschutz = formData.get("datenschutz");
const fieldErrors: ContactFormState["fieldErrors"] = {};
if (!vorname) fieldErrors.vorname = "Bitte geben Sie Ihren Vornamen an.";
if (!nachname) fieldErrors.nachname = "Bitte geben Sie Ihren Nachnamen an.";
if (!email) {
fieldErrors.email = "Bitte geben Sie Ihre E-Mail-Adresse an.";
} else if (!EMAIL_REGEX.test(email)) {
fieldErrors.email = "Bitte geben Sie eine gültige E-Mail-Adresse an.";
}
if (!telefon) {
fieldErrors.telefon = "Bitte geben Sie Ihre Telefonnummer an.";
} else if (!PHONE_REGEX.test(telefon)) {
fieldErrors.telefon = "Bitte geben Sie eine gültige Telefonnummer an.";
}
if (!nachricht) {
fieldErrors.nachricht = "Bitte geben Sie eine Nachricht ein.";
}
if (datenschutz !== "on") {
fieldErrors.datenschutz = "Bitte bestätigen Sie die Datenschutzhinweise.";
}
if (Object.keys(fieldErrors).length > 0) {
return { success: false, fieldErrors };
}
try {
await transporter.sendMail({
from: `"Basiclean Kontaktformular" <${process.env.SMTP_USER}>`,
to: process.env.CONTACT_RECEIVER,
replyTo: email,
subject: `Neue Kontaktanfrage von ${vorname} ${nachname}`,
text: [
`Unternehmen: ${unternehmen || "-"}`,
`Name: ${vorname} ${nachname}`,
`E-Mail: ${email}`,
`Telefon: ${telefon}`,
"",
"Nachricht:",
nachricht,
].join("\n"),
html: `
<h2>Neue Kontaktanfrage</h2>
<p><strong>Unternehmen:</strong> ${unternehmen || "-"}</p>
<p><strong>Name:</strong> ${vorname} ${nachname}</p>
<p><strong>E-Mail:</strong> ${email}</p>
<p><strong>Telefon:</strong> ${telefon}</p>
<p><strong>Nachricht:</strong></p>
<p>${nachricht.replace(/\n/g, "<br />")}</p>
`,
});
return { success: true };
} catch (error) {
console.error("Fehler beim Senden der Kontaktanfrage:", error);
return {
success: false,
error: "Die Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es später erneut.",
};
}
}
+189
View File
@@ -0,0 +1,189 @@
.hero {
background: var(--color-background-linear);
padding-block: var(--space-16);
position: relative;
overflow: hidden;
}
.inner {
max-width: var(--container-width);
margin-inline: auto;
padding-inline: var(--space-4);
position: relative;
z-index: 1;
}
.decoration {
position: absolute;
top: var(--space-8);
right: var(--space-8);
display: none;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: var(--space-3);
pointer-events: none;
z-index: 0;
}
.dot {
width: 14px;
height: 14px;
border-radius: var(--radius-full);
background-color: var(--color-accent);
opacity: 0.8;
}
.heading {
color: var(--color-heading);
max-width: 24ch;
position: relative;
z-index: 2;
}
.lead {
margin-top: var(--space-3);
font-size: var(--font-size-base);
max-width: 60ch;
position: relative;
z-index: 2;
}
/* --- Form Section --- */
.formSection {
padding-block: var(--space-16);
}
.formInner {
max-width: var(--container-width);
margin-inline: auto;
padding-inline: var(--space-4);
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: var(--space-2);
padding: var(--space-2) var(--space-2) var(--space-2) 0;
}
/* Links: kein eigener Hintergrund - bleibt leer/weiß */
.formPanel {
padding: var(--space-8) var(--space-6) var(--space-6);
}
/*
Rechts: Hintergrundbild mit dunklem Verlauf zur Lesbarkeit.
Inhalt wird als Flex-Column mit space-between verteilt:
Überschrift oben links, Kontaktdaten unten links.
*/
.infoPanel {
position: relative;
background-image:
linear-gradient(
180deg,
rgb(0 0 0 / 35%) 0%,
rgb(0 0 0 / 20%) 45%,
rgb(0 0 0 / 65%) 100%
),
url("/images/contact.jpg");
background-size: cover;
background-position: center;
padding: var(--space-8) var(--space-6) var(--space-6);
color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 420px;
border-radius: var(--space-2);
}
.infoHeading {
color: #fff;
max-width: 20ch;
}
.infoList {
display: flex;
flex-direction: column;
gap: var(--space-3);
list-style: none;
padding: 0;
}
.infoItem {
display: flex;
flex-direction: column;
gap: 2px;
}
.infoLabel {
font-size: var(--font-size-small, 0.875rem);
font-weight: var(--font-weight-medium);
color: rgb(255 255 255 / 80%);
}
.infoValue {
font-size: var(--font-size-base);
font-weight: var(--font-weight-medium);
color: #fff;
text-decoration: none;
}
a.infoValue:hover {
text-decoration: underline;
}
/* Tablet: 768px – 50/50 Aufteilung */
@media (min-width: 768px) {
.hero,
.formSection {
padding-block: var(--space-24);
}
.decoration {
display: grid;
top: -24px;
}
.dot {
width: 24px;
height: 24px;
}
.formInner {
flex-direction: row;
align-items: stretch;
background: var(--color-background-linear);
}
.formPanel {
width: 50%;
padding: var(--space-8);
}
.infoPanel {
width: 50%;
padding: var(--space-10) var(--space-8) var(--space-8);
min-height: 480px;
}
}
/* Desktop: 1024px – zurück auf 40/60 */
@media (min-width: 1024px) {
.hero {
padding-block: var(--space-36);
}
.decoration {
top: -42px;
}
.formPanel {
width: 40%;
padding: var(--space-10);
}
.infoPanel {
width: 60%;
padding: var(--space-12) var(--space-10) var(--space-10);
min-height: 560px;
}
}
+73 -9
View File
@@ -1,10 +1,74 @@
export default function Kontakt() {
return <>
<h1>Gebäudereinigung für ein makelloses Arbeitsumfeld</h1>
<h2>Gebäudereinigung für ein makelloses Arbeitsumfeld</h2>
<h3>Gebäudereinigung für ein makelloses Arbeitsumfeld</h3>
<h4>Gebäudereinigung für ein makelloses Arbeitsumfeld</h4>
<h5>Gebäudereinigung für ein makelloses Arbeitsumfeld</h5>
<h6>Gebäudereinigung für ein makelloses Arbeitsumfeld</h6>
</>;
import type { Metadata } from "next";
import styles from "./page.module.css";
import ContactForm from "@/components/ContactForm";
import FaqSection from "@/components/faq/FaqSection";
import { homepageFaq } from "@/components/faq/faqData";
export const metadata: Metadata = {
title: "Kontakt",
description:
"Kontaktieren Sie Basiclean für ein individuelles Angebot zu Reinigung und Hausmeisterdiensten in Hannover und Umgebung.",
};
export default function Page() {
return (
<main>
<section className={styles.hero} aria-labelledby="hero-heading">
<div className={styles.inner}>
<div className={styles.decoration} aria-hidden="true">
{Array.from({ length: 18 }).map((_, i) => (
<span key={i} className={styles.dot} />
))}
</div>
<h1 id="hero-heading" className={styles.heading}>
Kontaktieren Sie uns
</h1>
<p className={`lead ${styles.lead}`}>
Sie haben Fragen oder möchten ein individuelles Angebot anfragen?
Füllen Sie das Formular aus – wir melden uns schnellstmöglich bei
Ihnen zurück.
</p>
</div>
</section>
<section className={styles.formSection}>
<div className={styles.formInner}>
<div className={styles.formPanel}>
<ContactForm />
</div>
<div className={styles.infoPanel}>
<h2 className={styles.infoHeading}>So erreichen Sie uns</h2>
<ul className={styles.infoList}>
<li className={styles.infoItem}>
<span className={styles.infoLabel}>Telefon</span>
<a href="tel:+4951112345678" className={styles.infoValue}>
0511 123 456 78
</a>
</li>
<li className={styles.infoItem}>
<span className={styles.infoLabel}>E-Mail</span>
<a href="mailto:info@basiclean.de" className={styles.infoValue}>
info@basiclean.de
</a>
</li>
<li className={styles.infoItem}>
<span className={styles.infoLabel}>Adresse</span>
<span className={styles.infoValue}>
Musterstraße 12, 30159 Hannover
</span>
</li>
</ul>
</div>
</div>
</section>
<section>
<FaqSection title="Häufige Fragen" items={homepageFaq} />
</section>
</main>
);
}