update
This commit is contained in:
1 parent
8757431c79
commit
b2a409e6b7
14 files changed
+286
-175
No files matched your search
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
import CookieConsentBanner from "@/components/CookieConsent";
|
|
||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
import { Manrope, Nunito_Sans } from "next/font/google";
|
import { Manrope, Nunito_Sans } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
import Header from "@/components/Header";
|
import Header from "@/components/Header";
|
||||||
|
import CookieConsentLoader from "@/components/CookieConsentLoader";
|
||||||
|
|
||||||
const nunitoSans = Nunito_Sans({
|
const nunitoSans = Nunito_Sans({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@@ -94,10 +94,10 @@ export default function RootLayout({
|
|||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||||
/>
|
/>
|
||||||
|
<CookieConsentLoader />
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
<CookieConsentBanner />
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,5 +29,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:global(#cc-main .cm) {
|
:global(#cc-main .cm) {
|
||||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08) !important;
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.08) !important;
|
||||||
}
|
}
|
||||||
+108
-80
@@ -1,94 +1,122 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import * as CookieConsent from "vanilla-cookieconsent";
|
|
||||||
import "vanilla-cookieconsent/dist/cookieconsent.css";
|
|
||||||
import "./CookieConsent.module.css";
|
import "./CookieConsent.module.css";
|
||||||
|
|
||||||
export default function CookieConsentBanner() {
|
export default function CookieConsentBanner() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
CookieConsent.run({
|
let mounted = true;
|
||||||
categories: {
|
let idleId: number | undefined;
|
||||||
necessary: {
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
||||||
enabled: true,
|
|
||||||
readOnly: true,
|
const loadConsent = async () => {
|
||||||
|
const [cookieConsentModule] = await Promise.all([
|
||||||
|
import("vanilla-cookieconsent"),
|
||||||
|
import("vanilla-cookieconsent/dist/cookieconsent.css"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mod = cookieConsentModule as {
|
||||||
|
run?: (config: unknown) => void;
|
||||||
|
default?: {
|
||||||
|
run?: (config: unknown) => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const CookieConsent = mod.run ? mod : mod.default;
|
||||||
|
|
||||||
|
if (!mounted || !CookieConsent?.run) return;
|
||||||
|
|
||||||
|
CookieConsent.run({
|
||||||
|
categories: {
|
||||||
|
necessary: {
|
||||||
|
enabled: true,
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
analytics: {},
|
||||||
|
marketing: {},
|
||||||
},
|
},
|
||||||
analytics: {},
|
language: {
|
||||||
marketing: {},
|
default: "de",
|
||||||
},
|
translations: {
|
||||||
language: {
|
de: {
|
||||||
default: "de",
|
consentModal: {
|
||||||
translations: {
|
title: "Ihre Privatsphäre ist uns wichtig",
|
||||||
de: {
|
description:
|
||||||
consentModal: {
|
"Wir setzen notwendige und optionale Cookies ein. Ihre Auswahl können Sie jederzeit unter <a href='/datenschutz'>Datenschutz</a> ändern.",
|
||||||
title: "Ihre Privatsphäre ist uns wichtig",
|
acceptAllBtn: "Alle akzeptieren",
|
||||||
description:
|
acceptNecessaryBtn: "Nur notwendige",
|
||||||
"Wir verwenden Cookies, um Ihnen die bestmögliche Nutzung unserer Website zu ermöglichen. " +
|
showPreferencesBtn: "Einstellungen anpassen",
|
||||||
"Einige Cookies sind technisch notwendig, andere helfen uns, die Website zu verbessern " +
|
footer:
|
||||||
"und Ihnen relevante Inhalte anzuzeigen. Sie können Ihre Auswahl jederzeit unter " +
|
"<a href='/impressum'>Impressum</a>" +
|
||||||
"<a href='/datenschutz'>Datenschutz</a> anpassen oder widerrufen.",
|
"<a href='/datenschutz'>Datenschutzerklärung</a>",
|
||||||
acceptAllBtn: "Alle akzeptieren",
|
},
|
||||||
acceptNecessaryBtn: "Nur notwendige",
|
preferencesModal: {
|
||||||
showPreferencesBtn: "Einstellungen anpassen",
|
title: "Cookie-Einstellungen",
|
||||||
footer:
|
acceptAllBtn: "Alle akzeptieren",
|
||||||
"<a href='/impressum'>Impressum</a>" +
|
acceptNecessaryBtn: "Nur notwendige",
|
||||||
"<a href='/datenschutz'>Datenschutzerklärung</a>",
|
savePreferencesBtn: "Auswahl speichern",
|
||||||
},
|
closeIconLabel: "Schließen",
|
||||||
preferencesModal: {
|
sections: [
|
||||||
title: "Cookie-Einstellungen",
|
{
|
||||||
acceptAllBtn: "Alle akzeptieren",
|
title: "Ihre Privatsphäre",
|
||||||
acceptNecessaryBtn: "Nur notwendige",
|
description:
|
||||||
savePreferencesBtn: "Auswahl speichern",
|
"Wir setzen Cookies ein, um die Funktionalität unserer Website sicherzustellen und unser Angebot kontinuierlich zu verbessern. Sie entscheiden selbst, welche Kategorien Sie zulassen möchten.",
|
||||||
closeIconLabel: "Schließen",
|
},
|
||||||
sections: [
|
{
|
||||||
{
|
title: "Technisch notwendig",
|
||||||
title: "Ihre Privatsphäre",
|
description:
|
||||||
description:
|
"Diese Cookies sind für den reibungslosen Betrieb der Website unbedingt erforderlich und können nicht deaktiviert werden.",
|
||||||
"Wir setzen Cookies ein, um die Funktionalität unserer Website sicherzustellen " +
|
linkedCategory: "necessary",
|
||||||
"und unser Angebot kontinuierlich zu verbessern. Sie entscheiden selbst, " +
|
},
|
||||||
"welche Kategorien Sie zulassen möchten.",
|
{
|
||||||
},
|
title: "Analyse & Statistik",
|
||||||
{
|
description:
|
||||||
title: "Technisch notwendig",
|
"Diese Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren.",
|
||||||
description:
|
linkedCategory: "analytics",
|
||||||
"Diese Cookies sind für den reibungslosen Betrieb der Website unbedingt erforderlich " +
|
},
|
||||||
"und können nicht deaktiviert werden. Sie speichern keine personenbezogenen Daten " +
|
{
|
||||||
"und werden ausschließlich als Reaktion auf von Ihnen durchgeführte Aktionen gesetzt " +
|
title: "Marketing & externe Medien",
|
||||||
"(z. B. Spracheinstellungen oder Sicherheits-Token).",
|
description:
|
||||||
linkedCategory: "necessary",
|
"Diese Cookies ermöglichen die Einbindung externer Inhalte wie YouTube-Videos, Google Maps, Instagram-, Facebook- und X-Inhalte.",
|
||||||
},
|
linkedCategory: "marketing",
|
||||||
{
|
},
|
||||||
title: "Analyse & Statistik",
|
{
|
||||||
description:
|
title: "Weitere Informationen",
|
||||||
"Diese Cookies helfen uns zu verstehen, wie Besucher mit unserer Website interagieren. " +
|
description:
|
||||||
"Alle Daten werden anonymisiert gesammelt und dienen ausschließlich zur Verbesserung " +
|
"Bei Fragen wenden Sie sich bitte über das <a href='/kontakt'>Kontaktformular</a> an uns. Mehr Details finden Sie in unserer <a href='/datenschutz'>Datenschutzerklärung</a>.",
|
||||||
"unseres Angebots. Wir nutzen Google Analytics (GA4) – Ihre IP-Adresse wird dabei " +
|
},
|
||||||
"anonymisiert und nicht gespeichert.",
|
],
|
||||||
linkedCategory: "analytics",
|
},
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Marketing & externe Medien",
|
|
||||||
description:
|
|
||||||
"Diese Cookies ermöglichen die Einbindung externer Inhalte wie YouTube-Videos, " +
|
|
||||||
"Google Maps, Instagram-, Facebook- und X (Twitter)-Inhalte. " +
|
|
||||||
"Ohne Ihre Zustimmung werden diese Inhalte nicht geladen und keine Daten übertragen. " +
|
|
||||||
"Bitte beachten Sie, dass bei Aktivierung personenbezogene Daten " +
|
|
||||||
"an Drittanbieter (ggf. auch in die USA) übertragen werden können.",
|
|
||||||
linkedCategory: "marketing",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Weitere Informationen",
|
|
||||||
description:
|
|
||||||
"Bei Fragen zu unserer Cookie-Richtlinie wenden Sie sich bitte an uns über das " +
|
|
||||||
"<a href='/kontakt'>Kontaktformular</a>. Mehr Details finden Sie in unserer " +
|
|
||||||
"<a href='/datenschutz'>Datenschutzerklärung</a>.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (window.requestIdleCallback) {
|
||||||
|
idleId = window.requestIdleCallback(
|
||||||
|
() => {
|
||||||
|
void loadConsent();
|
||||||
|
},
|
||||||
|
{ timeout: 2000 },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
void loadConsent();
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
|
||||||
|
if (idleId && window.cancelIdleCallback) {
|
||||||
|
window.cancelIdleCallback(idleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const CookieConsentBanner = dynamic(() => import("./CookieConsent"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function CookieConsentLoader() {
|
||||||
|
return <CookieConsentBanner />;
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--footer-bg: #171717;
|
--footer-bg: #272727 ;
|
||||||
--footer-text: #a1a1aa;
|
--footer-text: #a1a1aa;
|
||||||
--footer-text-hover: #ffffff;
|
--footer-text-hover: #ffffff;
|
||||||
--footer-title: #ffffff;
|
--footer-title: #ffffff;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function Footer() {
|
|||||||
{/* Bottom: Copyright + Social */}
|
{/* Bottom: Copyright + Social */}
|
||||||
<div className={styles.footer__bottom}>
|
<div className={styles.footer__bottom}>
|
||||||
<p className={styles.footer__copy}>
|
<p className={styles.footer__copy}>
|
||||||
© {currentYear} ENVU. Alle Rechte vorbehalten.
|
© {currentYear}. Alle Rechte vorbehalten.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className={styles.footer__social}>
|
<div className={styles.footer__social}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import styles from "./Header.module.css";
|
import styles from "./Header.module.css";
|
||||||
import Navbar from "./navbar/Navbar";
|
import Navbar from "./Navbar/Navbar";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
background: var(--color-background-linear);
|
background: var(--color-background-linear);
|
||||||
padding-block: var(--space-16);
|
padding-block: var(--space-16);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
max-width: var(--container-width);
|
max-width: var(--container-width);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding-inline: var(--space-4);
|
padding-inline: var(--space-4);
|
||||||
@@ -14,68 +17,45 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
right: auto;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
right: var(--space-4);
|
width: min(78vw, 360px);
|
||||||
width: 100%;
|
height: auto;
|
||||||
height: 80%;
|
opacity: 0.05;
|
||||||
opacity: 0.08;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-5);
|
gap: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
|
||||||
max-width: 15ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lead {
|
|
||||||
max-width: 40ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
gap: var(--space-4);
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions :global(.btn) {
|
|
||||||
width: fit-content;
|
|
||||||
font-weight: var(--font-weight-semibold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions :global(.btn--tertiary) {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
color: var(--font-body-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet */
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.hero {
|
.hero {
|
||||||
padding-block: var(--space-28);
|
padding-block: var(--space-28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.heroDecoration {
|
.heroDecoration {
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
height: 90%;
|
width: min(42vw, 420px);
|
||||||
width: 50%;
|
opacity: 0.06;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-6);
|
gap: var(--space-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
max-width: 24ch;
|
max-width: 24ch;
|
||||||
}
|
}
|
||||||
@@ -85,12 +65,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Desktop */
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.hero {
|
.hero {
|
||||||
padding-block: var(--space-36);
|
padding-block: var(--space-36);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
padding-inline: var(--space-6);
|
padding-inline: var(--space-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heroDecoration {
|
||||||
|
width: min(44vw, 560px);
|
||||||
|
opacity: 0.08;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
-11
@@ -1,17 +1,22 @@
|
|||||||
import { ArrowRightIcon, PhoneIcon } from "@phosphor-icons/react/dist/ssr";
|
import { ArrowRightIcon, PhoneIcon } from "@phosphor-icons/react/dist/ssr";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import styles from "./HeroSection.module.css";
|
import styles from "./HeroSection.module.css";
|
||||||
|
import { companyData } from "@/data/companyData";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function HeroSection() {
|
export default function HeroSection() {
|
||||||
return (
|
return (
|
||||||
<section className={styles.hero} aria-labelledby="hero-heading">
|
<section className={styles.hero} aria-labelledby="hero-heading">
|
||||||
<img
|
|
||||||
src="/logo/logo-mininmal-basiclean-grey.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true"
|
|
||||||
className={styles.heroDecoration}
|
|
||||||
/>
|
|
||||||
<div className={styles.inner}>
|
<div className={styles.inner}>
|
||||||
|
<Image
|
||||||
|
src="/logo/logo-mininmal-basiclean-grey.svg"
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
className={styles.heroDecoration}
|
||||||
|
width={620}
|
||||||
|
height={620}
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<h1 id="hero-heading" className={styles.heading}>
|
<h1 id="hero-heading" className={styles.heading}>
|
||||||
Gebäudereinigung für ein makelloses Arbeitsumfeld
|
Gebäudereinigung für ein makelloses Arbeitsumfeld
|
||||||
@@ -28,18 +33,18 @@ export default function HeroSection() {
|
|||||||
Jetzt Angebot anfragen
|
Jetzt Angebot anfragen
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/Leistungen" className="btn btn--outline">
|
<Link href="/Leistungen" className="btn btn--outline">
|
||||||
{/* <span className="btn-icon animate" aria-hidden="true">
|
|
||||||
<ArrowRightIcon size={18} weight="bold" />
|
|
||||||
</span> */}
|
|
||||||
Leistungen ansehen
|
Leistungen ansehen
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<a href="tel:+491726210606" className="btn btn--tertiary">
|
<a
|
||||||
|
href={`tel:${companyData.contact.phone.value}`}
|
||||||
|
className="btn btn--tertiary"
|
||||||
|
>
|
||||||
<span className="btn-icon" aria-hidden="true">
|
<span className="btn-icon" aria-hidden="true">
|
||||||
<PhoneIcon size={18} weight="bold" />
|
<PhoneIcon size={18} weight="bold" />
|
||||||
</span>
|
</span>
|
||||||
0172 6210606
|
{companyData.contact.phone.display}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,17 +5,23 @@ import {
|
|||||||
ClockIcon,
|
ClockIcon,
|
||||||
} from "@phosphor-icons/react/dist/ssr";
|
} from "@phosphor-icons/react/dist/ssr";
|
||||||
|
|
||||||
export type ContactInfo = {
|
export type CompanyContactKey = "phone" | "email" | "openingHours";
|
||||||
|
|
||||||
|
export type CompanyContactItem = {
|
||||||
|
type: "company";
|
||||||
|
contactKey: CompanyContactKey;
|
||||||
|
icon: ElementType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StaticContactItem = {
|
||||||
|
type: "static";
|
||||||
icon: ElementType;
|
icon: ElementType;
|
||||||
label: string;
|
label: string;
|
||||||
href?: string;
|
href?: string;
|
||||||
obfuscated?: {
|
|
||||||
user?: string;
|
|
||||||
domain?: string;
|
|
||||||
tel?: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ContactInfo = CompanyContactItem | StaticContactItem;
|
||||||
|
|
||||||
export type ContactCtaData = {
|
export type ContactCtaData = {
|
||||||
heading: string;
|
heading: string;
|
||||||
lead: string;
|
lead: string;
|
||||||
@@ -31,18 +37,19 @@ export const contactCtaData: ContactCtaData = {
|
|||||||
buttonHref: "/kontakt",
|
buttonHref: "/kontakt",
|
||||||
contacts: [
|
contacts: [
|
||||||
{
|
{
|
||||||
|
type: "company",
|
||||||
|
contactKey: "phone",
|
||||||
icon: PhoneIcon,
|
icon: PhoneIcon,
|
||||||
label: "0172 621 0606",
|
|
||||||
obfuscated: { tel: "01726210606" },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
type: "company",
|
||||||
|
contactKey: "email",
|
||||||
icon: EnvelopeSimpleIcon,
|
icon: EnvelopeSimpleIcon,
|
||||||
label: "info@basiclean.de",
|
|
||||||
obfuscated: { user: "info", domain: "basiclean.de" },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
type: "company",
|
||||||
|
contactKey: "openingHours",
|
||||||
icon: ClockIcon,
|
icon: ClockIcon,
|
||||||
label: "Mo. - Fr. 08:00 - 17:00 Uhr",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -1,32 +1,35 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { ArrowRightIcon } from "@phosphor-icons/react/dist/ssr";
|
import { ArrowRightIcon } from "@phosphor-icons/react/dist/ssr";
|
||||||
|
import { companyData } from "@/data/companyData";
|
||||||
import styles from "./ContactCtaSection.module.css";
|
import styles from "./ContactCtaSection.module.css";
|
||||||
import { contactCtaData, type ContactInfo } from "./ContactCtaData";
|
import { contactCtaData, type ContactInfo } from "./ContactCtaData";
|
||||||
|
|
||||||
function ObfuscatedLink({ item }: { item: ContactInfo }) {
|
function resolveCompanyContact(
|
||||||
const ref = useRef<HTMLAnchorElement>(null);
|
item: Extract<ContactInfo, { type: "company" }>,
|
||||||
|
) {
|
||||||
|
if (item.contactKey === "phone") {
|
||||||
|
return {
|
||||||
|
label: companyData.contact.phone.display,
|
||||||
|
href: `tel:${companyData.contact.phone.value}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
if (item.contactKey === "email") {
|
||||||
if (!ref.current) return;
|
const email = `${companyData.contact.email.user}@${companyData.contact.email.domain}`;
|
||||||
|
|
||||||
if (item.obfuscated?.tel) {
|
return {
|
||||||
ref.current.href = `tel:${item.obfuscated.tel}`;
|
label: email,
|
||||||
} else if (item.obfuscated?.user && item.obfuscated?.domain) {
|
href: `mailto:${email}`,
|
||||||
ref.current.href = `mailto:${item.obfuscated.user}@${item.obfuscated.domain}`;
|
};
|
||||||
}
|
}
|
||||||
}, [item.obfuscated]);
|
|
||||||
|
|
||||||
const Icon = item.icon;
|
if (item.contactKey === "openingHours") {
|
||||||
|
return {
|
||||||
|
label: companyData.contact.openingHours.display,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return null;
|
||||||
<a ref={ref} href="#" className={styles.contactItem}>
|
|
||||||
<Icon size={20} weight="regular" aria-hidden="true" />
|
|
||||||
{item.label}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ContactCtaSection() {
|
export default function ContactCtaSection() {
|
||||||
@@ -55,16 +58,50 @@ export default function ContactCtaSection() {
|
|||||||
<hr className={styles.divider} aria-hidden="true" />
|
<hr className={styles.divider} aria-hidden="true" />
|
||||||
|
|
||||||
<div className={styles.contactRow}>
|
<div className={styles.contactRow}>
|
||||||
{contacts.map((item: ContactInfo) =>
|
{contacts.map((item) => {
|
||||||
item.obfuscated ? (
|
const icon = (
|
||||||
<ObfuscatedLink key={item.label} item={item} />
|
<item.icon size={20} weight="regular" aria-hidden="true" />
|
||||||
) : (
|
);
|
||||||
|
|
||||||
|
if (item.type === "company") {
|
||||||
|
const resolved = resolveCompanyContact(item);
|
||||||
|
|
||||||
|
if (!resolved) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={item.contactKey}
|
||||||
|
href={resolved.href}
|
||||||
|
className={styles.contactItem}
|
||||||
|
aria-label={resolved.label}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
{resolved.label}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.href) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={item.label}
|
||||||
|
href={item.href}
|
||||||
|
className={styles.contactItem}
|
||||||
|
aria-label={item.label}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<span key={item.label} className={styles.contactItem}>
|
<span key={item.label} className={styles.contactItem}>
|
||||||
<item.icon size={20} weight="regular" aria-hidden="true" />
|
{icon}
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
),
|
);
|
||||||
)}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export const companyData = {
|
||||||
|
name: "Basiclean",
|
||||||
|
legalName: "Basiclean",
|
||||||
|
slogan: "Wo Sauberkeit Standard ist",
|
||||||
|
contact: {
|
||||||
|
email: {
|
||||||
|
user: "info",
|
||||||
|
domain: "basiclean.de",
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
display: "0172 621 0606",
|
||||||
|
value: "+491726210606",
|
||||||
|
},
|
||||||
|
openingHours: {
|
||||||
|
display: "Mo. - Fr. 08:00 - 17:00 Uhr",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { companyData } from "./companyData";
|
||||||
|
|
||||||
|
export function getCompanyEmail() {
|
||||||
|
return `${companyData.contact.email.user}@${companyData.contact.email.domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCompanyEmailHref() {
|
||||||
|
return `mailto:${getCompanyEmail()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCompanyPhoneDisplay() {
|
||||||
|
return companyData.contact.phone.display;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCompanyPhoneHref() {
|
||||||
|
return `tel:${companyData.contact.phone.value}`;
|
||||||
|
}
|
||||||
+7
-4
@@ -1,12 +1,15 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const allowedOrigin = process.env.NEXT_ALLOWED_DEV_ORIGIN;
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
output: "standalone",
|
||||||
trailingSlash: false,
|
trailingSlash: false,
|
||||||
poweredByHeader: false,
|
poweredByHeader: false,
|
||||||
allowedDevOrigins: [
|
|
||||||
"192.168.178.59", // deine PC-IP
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (allowedOrigin) {
|
||||||
|
nextConfig.allowedDevOrigins = [allowedOrigin];
|
||||||
|
}
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
Reference in new issue
Block a user