Initial commit
This commit is contained in:
commit
bcca176137
88 files changed
+8725
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,8 @@
|
||||
|
||||
export default function Datenschutz() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Datenschutz</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 80dvh;
|
||||
padding: 0 var(--space-4);
|
||||
overflow: hidden;
|
||||
background-color: var(--color-background);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: clamp(12rem, 40vw, 28rem);
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.05em;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
color: transparent;
|
||||
-webkit-text-stroke: 1px rgba(23, 23, 23, 0.06);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
letter-spacing: var(--letter-spacing-wide);
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-soft);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.heading {
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: var(--space-7);
|
||||
max-width: 38ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.cta {
|
||||
gap: var(--btn-gap);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wrapper {
|
||||
padding: 0 var(--space-6);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import styles from "./error.module.css";
|
||||
import {
|
||||
ArrowCounterClockwiseIcon,
|
||||
ArrowLeftIcon,
|
||||
} from "@phosphor-icons/react/dist/ssr";
|
||||
|
||||
interface ErrorProps {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export default function Error({ error, reset }: ErrorProps) {
|
||||
useEffect(() => {
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<main className={styles.wrapper}>
|
||||
<span className={styles.ghost} aria-hidden="true">
|
||||
500
|
||||
</span>
|
||||
|
||||
<div className={styles.content}>
|
||||
<p className={styles.label}>Serverfehler</p>
|
||||
<h1 className={styles.heading}>Etwas ist schiefgelaufen</h1>
|
||||
<p className={styles.description}>
|
||||
Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut oder
|
||||
komm später zurück.
|
||||
</p>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={reset}
|
||||
className={`btn btn--filled ${styles.cta}`}
|
||||
>
|
||||
<ArrowCounterClockwiseIcon className="btn-icon" size={18} />
|
||||
Erneut versuchen
|
||||
</button>
|
||||
|
||||
<Link href="/" className={`btn btn--outline ${styles.cta}`}>
|
||||
<ArrowLeftIcon className="btn-icon" size={18} />
|
||||
Zurück zur Startseite
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,58 @@
|
||||
@import "../styles/tokens.css";
|
||||
@import "../styles/buttons.css";
|
||||
@import "../styles/forms.css";
|
||||
@import "../styles/typography.css";
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
font-size: 112.5%; /* 16px × 1.125 = 18px */
|
||||
}
|
||||
html.has-menu-open {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--font-body-color);
|
||||
background: var(--color-background);
|
||||
font-family: var(--font-body);
|
||||
font-size: 1rem;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
line-height: var(--line-height-body);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin-inline: auto;
|
||||
padding: 0 var(--space-4);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
padding: 0 var(--space-4);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
main {
|
||||
padding: 0 var(--space-6);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024" viewBox="0 0 1024 1024"><metadata><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Description><dc:creator>RealFaviconGenerator</dc:creator><dc:source>https://realfavicongenerator.net</dc:source></rdf:Description></rdf:RDF></metadata><image width="1024" height="1024" xlink:href="data:image/png;base64,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 truncated
|
||||
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,8 @@
|
||||
|
||||
export default function Impressum() {
|
||||
return (
|
||||
<main>
|
||||
<h1>impressum</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import CookieConsentBanner from "@/components/CookieConsent";
|
||||
import type { Metadata, Viewport } from "next";
|
||||
import { Manrope, Nunito_Sans } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
import Footer from "@/components/Footer";
|
||||
import Header from "@/components/Header";
|
||||
|
||||
const nunitoSans = Nunito_Sans({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "600", "700"],
|
||||
variable: "--font-body",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const manrope = Manrope({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700"],
|
||||
variable: "--font-heading",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
};
|
||||
|
||||
const siteName = "ENVU";
|
||||
const title = "ENVU - Code Connect";
|
||||
const description = "Wir bieten Ihnen maßgeschneiderte Lösungen.";
|
||||
const siteUrl = "https://envu.de";
|
||||
|
||||
const ogImage = {
|
||||
url: "/images/envu.png",
|
||||
alt: "Envu Wallpaper",
|
||||
width: 1200,
|
||||
height: 628,
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(siteUrl),
|
||||
title: {
|
||||
default: title,
|
||||
template: `%s | ${siteName}`,
|
||||
},
|
||||
description,
|
||||
keywords: "Envu, Hannover",
|
||||
authors: [{ name: siteName }],
|
||||
robots: "index, follow",
|
||||
alternates: {
|
||||
canonical: siteUrl,
|
||||
},
|
||||
appleWebApp: {
|
||||
title: siteName,
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
siteName,
|
||||
locale: "de_DE",
|
||||
title,
|
||||
description,
|
||||
images: [ogImage],
|
||||
url: siteUrl,
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
site: "@envu",
|
||||
title,
|
||||
description,
|
||||
images: [{ url: ogImage.url, alt: ogImage.alt }],
|
||||
},
|
||||
};
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "LocalBusiness",
|
||||
name: siteName,
|
||||
url: siteUrl,
|
||||
description,
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
addressLocality: "Hannover",
|
||||
addressCountry: "DE",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<html lang="de" className={`${nunitoSans.variable} ${manrope.variable}`}>
|
||||
<body>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
<CookieConsentBanner />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<main>
|
||||
<p>Laden...</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "ENVU",
|
||||
"short_name": "ENVU",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/favicon/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 80dvh;
|
||||
padding: 0 var(--space-4);
|
||||
overflow: hidden;
|
||||
background-color: var(--color-background);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: clamp(12rem, 40vw, 28rem);
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.05em;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
color: transparent;
|
||||
-webkit-text-stroke: 1px rgba(23, 23, 23, 0.06);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
letter-spacing: var(--letter-spacing-wide);
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-soft);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.heading {
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: var(--space-7);
|
||||
max-width: 38ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.cta {
|
||||
gap: var(--btn-gap);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wrapper {
|
||||
padding: 0 var(--space-6);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import styles from "./not-found.module.css";
|
||||
import { ArrowLeftIcon } from "@phosphor-icons/react/dist/ssr";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Seite nicht gefunden",
|
||||
description: "Die gesuchte Seite existiert nicht.",
|
||||
};
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<main className={styles.wrapper}>
|
||||
<span className={styles.ghost} aria-hidden="true">
|
||||
404
|
||||
</span>
|
||||
|
||||
<div className={styles.content}>
|
||||
<p className={styles.label}>Fehler 404</p>
|
||||
<h1 className={styles.heading}>Seite nicht gefunden</h1>
|
||||
<p className={styles.description}>
|
||||
Die gesuchte Seite existiert leider nicht oder wurde verschoben.
|
||||
</p>
|
||||
|
||||
<Link href="/" className={`btn btn--filled ${styles.cta}`}>
|
||||
<ArrowLeftIcon className="btn-icon" size={18} />
|
||||
Zurück zur Startseite
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Whitespace-only changes.
+192
@@ -0,0 +1,192 @@
|
||||
import { ArrowRightIcon } from "@phosphor-icons/react/dist/ssr";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Startseite</h1>
|
||||
<h2>Startseite</h2>
|
||||
<h3>Startseite</h3>
|
||||
<h4>Startseite</h4>
|
||||
<p>Startseite</p>
|
||||
<br />
|
||||
<button className="btn btn--filled">Filled</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--outline">Outline</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--ghost">Ghost</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--tertiary">Tertiary</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--filled" disabled>
|
||||
Disabled
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--filled">
|
||||
<ArrowRightIcon className="btn-icon animate" size={18} />
|
||||
Jetzt starten
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--outline">
|
||||
<ArrowRightIcon className="btn-icon animate" size={18} />
|
||||
Outline
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--ghost">
|
||||
<ArrowRightIcon className="btn-icon animate" size={18} />
|
||||
Ghost
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--tertiary">
|
||||
<ArrowRightIcon className="btn-icon animate" size={18} />
|
||||
Tertiary
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<button className="btn btn--filled" disabled>
|
||||
<ArrowRightIcon className="btn-icon animate" size={18} />
|
||||
Disabled
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
<p>
|
||||
<a href="#">Webdesign</a> und Webentwicklung bilden das Fundament jeder
|
||||
erfolgreichen digitalen Präsenz. Während Webdesign die visuelle
|
||||
Gestaltung, Benutzerführung und das Nutzererlebnis in den Vordergrund
|
||||
stellt, sorgt die Webentwicklung für die technische Umsetzung im
|
||||
Hintergrund. Beide Disziplinen greifen eng ineinander – ein
|
||||
ansprechendes Design entfaltet seine volle Wirkung nur durch sauberen,
|
||||
performanten Code.
|
||||
</p>
|
||||
<p>
|
||||
Moderne Websites müssen auf allen Geräten funktionieren, schnell laden
|
||||
und barrierefrei zugänglich sein. Suchmaschinenoptimierung, Ladezeiten
|
||||
und intuitive Navigation sind dabei keine optionalen Extras, sondern
|
||||
entscheidende Faktoren für den Erfolg. Wer heute im Netz wahrgenommen
|
||||
werden möchte, braucht mehr als nur eine schöne Oberfläche – er braucht
|
||||
eine durchdachte digitale Strategie, die Design und Technik vereint.
|
||||
</p>
|
||||
<br />
|
||||
<h2>Kontakt</h2>
|
||||
<form>
|
||||
{/* Name */}
|
||||
<div className="form-group">
|
||||
<label className="form-label form-label--required" htmlFor="name">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Max Mustermann"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* E-Mail */}
|
||||
<div className="form-group">
|
||||
<label className="form-label form-label--required" htmlFor="email">
|
||||
E-Mail
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="max@beispiel.de"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Telefon */}
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="telefon">
|
||||
Telefon
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="telefon"
|
||||
name="telefon"
|
||||
placeholder="+49 511 123456"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Betreff */}
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="betreff">
|
||||
Betreff
|
||||
</label>
|
||||
<select id="betreff" name="betreff">
|
||||
<option value="">Bitte auswählen</option>
|
||||
<option value="anfrage">Allgemeine Anfrage</option>
|
||||
<option value="angebot">Angebotsanfrage</option>
|
||||
<option value="support">Support</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Nachricht */}
|
||||
<div className="form-group">
|
||||
<label
|
||||
className="form-label form-label--required"
|
||||
htmlFor="nachricht"
|
||||
>
|
||||
Nachricht
|
||||
</label>
|
||||
<textarea
|
||||
id="nachricht"
|
||||
name="nachricht"
|
||||
placeholder="Ihre Nachricht..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Datenschutz Checkbox */}
|
||||
<div className="form-group">
|
||||
<label className="form-check">
|
||||
<input type="checkbox" />
|
||||
<span className="form-check-label">
|
||||
Ich habe die Datenschutzerklärung gelesen und stimme zu.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Error-Beispiel zum Testen */}
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="fehler">
|
||||
Fehler-Beispiel
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="fehler"
|
||||
className="is-error"
|
||||
defaultValue="Falscher Wert"
|
||||
/>
|
||||
<span className="form-error-msg">
|
||||
Bitte gib einen gültigen Wert ein.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Disabled-Beispiel */}
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="deaktiviert">
|
||||
Deaktiviert
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="deaktiviert"
|
||||
disabled
|
||||
defaultValue="Nicht bearbeitbar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit */}
|
||||
<button type="submit" className="btn btn--filled">
|
||||
Nachricht senden
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
const BASE_URL = "https://deinedomain.de"; // ← hier deine Domain eintragen
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: "*", // gilt für alle Crawler (Google, Bing etc.)
|
||||
allow: "/", // alles erlaubt
|
||||
disallow: [
|
||||
"/api/", // API-Routen nicht indexieren
|
||||
"/admin/", // falls vorhanden: Admin-Bereich sperren
|
||||
"/datenschutz",
|
||||
"/impressum",
|
||||
],
|
||||
},
|
||||
],
|
||||
sitemap: `${BASE_URL}/sitemap.xml`, // ← Sitemap verlinken
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// app/sitemap.ts
|
||||
// Erreichbar unter: https://deinedomain.de/sitemap.xml
|
||||
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
const BASE_URL = "https://deinedomain.de"; // ← Domain anpassen
|
||||
|
||||
// ─── Typen ───────────────────────────────────────────────────────────────────
|
||||
// interface BlogPost {
|
||||
// slug: string;
|
||||
// updatedAt: Date;
|
||||
// }
|
||||
|
||||
// ─── Daten laden (nur aktivieren wenn Blog vorhanden) ────────────────────────
|
||||
// async function getBlogPosts(): Promise<BlogPost[]> {
|
||||
// Hier API/CMS-Call, z.B.:
|
||||
// const res = await fetch(`${BASE_URL}/api/posts`);
|
||||
// return res.json();
|
||||
// }
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
// ─── Statische Seiten ──────────────────────────────────────────────────────
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: BASE_URL,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${BASE_URL}/about`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${BASE_URL}/kontakt`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.6,
|
||||
},
|
||||
{
|
||||
url: `${BASE_URL}/datenschutz`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.3,
|
||||
},
|
||||
{
|
||||
url: `${BASE_URL}/impressum`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.3,
|
||||
},
|
||||
];
|
||||
|
||||
// ─── Dynamische Blog-Seiten (auskommentiert bis Blog vorhanden) ────────────
|
||||
// const posts = await getBlogPosts();
|
||||
// const blogPages: MetadataRoute.Sitemap = posts.map((post) => ({
|
||||
// url: `${BASE_URL}/blog/${post.slug}`,
|
||||
// lastModified: post.updatedAt,
|
||||
// changeFrequency: "weekly",
|
||||
// priority: 0.7,
|
||||
// }));
|
||||
|
||||
// ─── Alles zusammenführen ──────────────────────────────────────────────────
|
||||
return [
|
||||
...staticPages,
|
||||
// ...blogPages, ← aktivieren wenn Blog live ist
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
"use client";
|
||||
|
||||
export default function TestErrorPage() {
|
||||
throw new Error("Testfehler – Error-Seite prüfen");
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,8 @@
|
||||
|
||||
export default function UeberUns() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Über Uns</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user