feat(legal): add imprint and privacy policy pages with responsive layout and fixed footer integration
This commit is contained in:
parent
76d239df7b
commit
949dde7aba
7 changed files with 368 additions and 33 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* @file layout.tsx
|
* @file (app)/layout.tsx
|
||||||
* @description Server component layout protecting authenticated routes, verifying user session status, and rendering the responsive application shell structure with global toast notifications.
|
* @description Server component layout protecting authenticated routes by verifying user sessions and arranging the main dashboard application structure with responsive sidebars, headers, navigation bars, error toasts, and footers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { auth } from "@/auth";
|
import { auth } from "@/auth";
|
||||||
|
|
@ -9,15 +9,16 @@ import Sidebar from "@/app/components/layout/Sidebar";
|
||||||
import Header from "@/app/components/layout/Header";
|
import Header from "@/app/components/layout/Header";
|
||||||
import Navbar from "@/app/components/layout/Navbar";
|
import Navbar from "@/app/components/layout/Navbar";
|
||||||
import ErrorToast from "../components/ErrorToast";
|
import ErrorToast from "../components/ErrorToast";
|
||||||
|
import Footer from "@/app/components/layout/Footer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the protected application layout wrapper.
|
* Renders the main application layout for authenticated views, checking user sessions
|
||||||
* Checks for an active user session and redirects to the login page if unauthenticated.
|
* and assembling the responsive page shell.
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @param {Object} props - The component props.
|
* @param {Object} props - The component props.
|
||||||
* @param {React.ReactNode} props.children - The child components or pages to render within the layout.
|
* @param {React.ReactNode} props.children - The inner child components/pages to render inside the authenticated layout.
|
||||||
* @returns {Promise<JSX.Element>} The rendered application layout structure.
|
* @returns {Promise<JSX.Element>} The rendered application layout component.
|
||||||
*/
|
*/
|
||||||
export default async function AppLayout({
|
export default async function AppLayout({
|
||||||
children,
|
children,
|
||||||
|
|
@ -38,9 +39,13 @@ export default async function AppLayout({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col min-w-0 overflow-hidden pb-16 md:pb-0">
|
<div className="flex-1 flex flex-col min-w-0 overflow-hidden pb-16 md:pb-0">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="flex-1 overflow-y-auto p-4 md:p-6 lg:p-8">
|
|
||||||
{children}
|
<div className="flex-1 overflow-y-auto flex flex-col">
|
||||||
</main>
|
<main className="flex-1 p-4 md:p-6 lg:p-8">{children}</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
|
||||||
<div className="md:hidden flex">
|
<div className="md:hidden flex">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* @file AuthLayout.tsx
|
* @file (auth)/layout.tsx
|
||||||
* @description Layout component wrapping authentication views with a centered container structure.
|
* @description Layout component wrapping authentication views with a centered container structure and fixed bottom footer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Footer from "../components/layout/Footer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a centered layout wrapper optimized for authentication pages (login, registration, etc.).
|
* Renders a centered layout wrapper optimized for authentication pages (login, registration, etc.),
|
||||||
|
* including a fixed footer at the bottom of the viewport.
|
||||||
*
|
*
|
||||||
* @param {Object} props - The component props.
|
* @param {Object} props - The component props.
|
||||||
* @param {React.ReactNode} props.children - The inner child components/pages to render inside the layout.
|
* @param {React.ReactNode} props.children - The inner child components/pages to render inside the layout.
|
||||||
|
|
@ -20,6 +22,10 @@ export default function AuthLayout({
|
||||||
return (
|
return (
|
||||||
<div className="flex h-dvh w-full items-center justify-center p-4">
|
<div className="flex h-dvh w-full items-center justify-center p-4">
|
||||||
<div className="w-full max-w-md">{children}</div>
|
<div className="w-full max-w-md">{children}</div>
|
||||||
|
|
||||||
|
<div className="absolute bottom-0 left-0 right-0">
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
app/components/layout/Footer.tsx
Normal file
45
app/components/layout/Footer.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* @file app/components/layout/Footer.tsx
|
||||||
|
* @description Client component rendering the site footer with a copyright notice and links to legal pages like Imprint and Privacy Policy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the application footer containing dynamic copyright information and legal navigation links.
|
||||||
|
*
|
||||||
|
* @returns {JSX.Element} The rendered footer component.
|
||||||
|
*/
|
||||||
|
export default function Footer() {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="w-full bg-background text-xs px-3 py-3 border-t border-border">
|
||||||
|
<div className="max-w-5xl mx-auto flex flex-col md:flex-row justify-between items-center gap-2">
|
||||||
|
{/* Left: Copyright Notice */}
|
||||||
|
<p className="order-1 md:order-1">
|
||||||
|
© {currentYear} André Kempf. All rights reserved.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Right: Legal & Compliance Links */}
|
||||||
|
<div className="flex gap-4 order-2 md:order-3">
|
||||||
|
<Link
|
||||||
|
href="/imprint"
|
||||||
|
className="hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
|
Imprint
|
||||||
|
</Link>
|
||||||
|
<span>|</span>
|
||||||
|
<Link
|
||||||
|
href="/privacy"
|
||||||
|
className="hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
119
app/imprint/page.tsx
Normal file
119
app/imprint/page.tsx
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* @file app/imprint/page.tsx
|
||||||
|
* @description Server component rendering the legal notice (imprint) page with a title header and back button navigation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the imprint/legal notice page containing the main title and layout container.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<JSX.Element>} The rendered imprint page component.
|
||||||
|
*/
|
||||||
|
export default async function ImprintPage() {
|
||||||
|
return (
|
||||||
|
<section className="flex-1 overflow-y-auto w-full">
|
||||||
|
<div className="relative p-6 max-w-5xl mx-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-foreground mb-1 tracking-tight">
|
||||||
|
Legal Notice<span className="text-primary">.</span>
|
||||||
|
</h1>
|
||||||
|
<div className="space-y-6 text-sm mt-3 text-foreground-muted leading-relaxed">
|
||||||
|
{/* Information in accordance with § 5 DDG & § 18 MStV */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Information Pursuant to §
|
||||||
|
5 DDG
|
||||||
|
</h2>
|
||||||
|
<div className="font-mono text-xs bg-background/60 p-4 rounded-xl border border-border text-foreground space-y-1">
|
||||||
|
<p className="font-semibold">André Kempf</p>
|
||||||
|
<p className="text-primary-hover">Full-Stack Web Developer</p>
|
||||||
|
<p>Großschneidersweg 2a</p>
|
||||||
|
<p>76149 Karlsruhe, Germany</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-foreground-muted">
|
||||||
|
Also responsible for content pursuant to § 18 Abs. 2 MStV.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Contact
|
||||||
|
</h2>
|
||||||
|
<div className="p-4 rounded-xl bg-background/60 border border-border font-mono text-xs">
|
||||||
|
<p className="text-foreground-muted">
|
||||||
|
<span className="text-foreground">Email:</span>{" "}
|
||||||
|
<span className="text-primary hover:underline cursor-pointer">
|
||||||
|
dev@andre-kempf.com
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Disclaimer */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-4">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Disclaimer & Legal Notes
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<h3 className="font-semibold text-foreground text-sm">
|
||||||
|
Liability for Content
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
As a service provider, I am responsible for my own content on
|
||||||
|
these pages according to general laws pursuant to § 7 Abs. 1
|
||||||
|
DDG. However, according to §§ 8 to 10 DDG, I am not obligated to
|
||||||
|
monitor transmitted or stored third-party information or to
|
||||||
|
investigate circumstances that indicate illegal activity.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 pt-3 border-t border-border">
|
||||||
|
<h3 className="font-semibold text-foreground text-sm">
|
||||||
|
Liability for Links
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
My website contains links to external third-party websites over
|
||||||
|
whose content I have no control. Therefore, I cannot accept any
|
||||||
|
liability for these external contents. The respective provider
|
||||||
|
or operator of the pages is always responsible for the content
|
||||||
|
of the linked pages.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 pt-3 border-t border-border">
|
||||||
|
<h3 className="font-semibold text-foreground text-sm">
|
||||||
|
Copyright
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
The content and works created on these pages are subject to
|
||||||
|
German copyright law. Duplication, processing, distribution, or
|
||||||
|
any form of commercialization beyond the scope of copyright law
|
||||||
|
require the prior written consent of the author or creator.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dispute Resolution */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Dispute Resolution
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
The European Commission provides a platform for online dispute
|
||||||
|
resolution (OS):{" "}
|
||||||
|
<a
|
||||||
|
href="https://ec.europa.eu/consumers/odr/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline break-all"
|
||||||
|
>
|
||||||
|
https://ec.europa.eu/consumers/odr/
|
||||||
|
</a>
|
||||||
|
.<br />I am neither willing nor obligated to participate in
|
||||||
|
dispute resolution proceedings before a consumer arbitration
|
||||||
|
board.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
app/page.tsx
45
app/page.tsx
|
|
@ -6,13 +6,14 @@
|
||||||
import { auth } from "@/auth";
|
import { auth } from "@/auth";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import Footer from "@/app/components/layout/Footer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the welcome landing page. Checks for an active session and redirects
|
* Renders the welcome page containing branding elements, authentication status checks,
|
||||||
* if logged in; otherwise, provides navigation links to sign in or register.
|
* and navigation links for signing in or registering.
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @returns {Promise<JSX.Element>} The rendered welcome page component.
|
* @returns {Promise<JSX.Element>} The rendered welcome landing page component.
|
||||||
*/
|
*/
|
||||||
export default async function WelcomePage() {
|
export default async function WelcomePage() {
|
||||||
const session = typeof auth === "function" ? await auth() : null;
|
const session = typeof auth === "function" ? await auth() : null;
|
||||||
|
|
@ -21,27 +22,29 @@ export default async function WelcomePage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-dvh w-full flex-col items-center justify-center p-6 text-center">
|
<div className="flex flex-col h-dvh w-full justify-between">
|
||||||
<div className="max-w-md space-y-6">
|
<div className="flex-1 flex flex-col items-center justify-center p-6 text-center">
|
||||||
<h1 className="text-4xl font-extrabold tracking-tight">Flowstate</h1>
|
<div className="max-w-md space-y-6">
|
||||||
<p className="text-foreground-muted">Welcome Page</p>
|
<h1 className="text-4xl font-extrabold tracking-tight">Flowstate</h1>
|
||||||
<div className="flex items-center justify-center gap-4">
|
<p className="text-foreground-muted">Welcome Page</p>
|
||||||
<Link
|
<div className="flex items-center justify-center gap-4">
|
||||||
href="/login/"
|
<Link
|
||||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-foreground text-background hover:opacity-70 transition-opacity"
|
href="/login/"
|
||||||
>
|
className="px-4 py-2 text-sm font-medium rounded-lg bg-foreground text-background hover:opacity-70 transition-opacity"
|
||||||
Sign In
|
>
|
||||||
</Link>
|
Sign In
|
||||||
|
</Link>
|
||||||
|
|
||||||
{/* TODO */}
|
<Link
|
||||||
<Link
|
href="/register/"
|
||||||
href="/register/"
|
className="px-4 py-2 text-sm font-medium rounded-lg border border-border hover:opacity-70 transition-opacity"
|
||||||
className="px-4 py-2 text-sm font-medium rounded-lg border border-border hover:opacity-70 transition-opacity"
|
>
|
||||||
>
|
Register
|
||||||
Register
|
</Link>
|
||||||
</Link>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
145
app/privacy/page.tsx
Normal file
145
app/privacy/page.tsx
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
/**
|
||||||
|
* @file app/privacy/page.tsx
|
||||||
|
* @description Server component rendering the privacy policy page with a title header and back button navigation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the privacy policy page containing the main title and layout container alongside a back navigation button.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<JSX.Element>} The rendered privacy policy page component.
|
||||||
|
*/
|
||||||
|
export default async function PrivacyPage() {
|
||||||
|
return (
|
||||||
|
<section className="flex-1 overflow-y-auto w-full">
|
||||||
|
<div className="relative p-6 max-w-5xl mx-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-foreground mb-1 tracking-tight">
|
||||||
|
Privacy Policy<span className="text-primary">.</span>
|
||||||
|
</h1>
|
||||||
|
<div className="space-y-6 text-sm mt-3 text-foreground-muted leading-relaxed">
|
||||||
|
{/* Overview */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-4">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Data Protection at a
|
||||||
|
Glance
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="font-semibold text-foreground text-sm">
|
||||||
|
General Information
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
The following notes provide a simple overview of what happens to
|
||||||
|
your personal data when you visit this website. Personal data is
|
||||||
|
any data that can be used to personally identify you.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="font-semibold text-foreground text-sm">
|
||||||
|
Data Collection on This Website
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Data processing on this website is carried out by the website
|
||||||
|
operator. Your data is collected when you provide it to us, or
|
||||||
|
automatically by our IT systems when you visit the site.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Responsible Party */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Responsible Party
|
||||||
|
(Controller)
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
The controller responsible for data processing on this website is:
|
||||||
|
</p>
|
||||||
|
<div className="font-mono text-xs bg-background/60 p-4 rounded-xl border border-border text-foreground space-y-1">
|
||||||
|
<p className="font-semibold">André Kempf</p>
|
||||||
|
<p>Großschneidersweg 2a</p>
|
||||||
|
<p>76149 Karlsruhe, Germany</p>
|
||||||
|
<p className="pt-2">
|
||||||
|
Email:{" "}
|
||||||
|
<span className="text-primary hover:underline cursor-pointer">
|
||||||
|
dev@andre-kempf.com
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hosting & Server Log Files */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Hosting & Server
|
||||||
|
Infrastructure
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
This website is hosted externally on web servers operated by
|
||||||
|
Netcup GmbH. Personal data processed on this website is stored on
|
||||||
|
the host's secure servers.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The hosting provider automatically processes technical access data
|
||||||
|
in server environment variables necessary to establish a stable
|
||||||
|
connection and deliver page assets securely.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Server Analytics */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Server Analytics &
|
||||||
|
Privacy-First Tracking
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
To evaluate website reach and optimize user experience, this
|
||||||
|
website processes minimal access metrics (e.g., total daily views,
|
||||||
|
coarse device category, and daily visitor counts).
|
||||||
|
</p>
|
||||||
|
<div className="p-4 rounded-xl bg-background/60 border border-border space-y-2 text-xs font-mono">
|
||||||
|
<p className="text-emerald-400 font-semibold">
|
||||||
|
// Key privacy guarantees:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc list-inside space-y-1 text-foreground-muted">
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">No Cookies:</strong> We do
|
||||||
|
not store cookies or local storage identifiers on your device.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">Anonymized IPs:</strong>{" "}
|
||||||
|
IP addresses are instantly hashed with a daily salt and never
|
||||||
|
stored in plain text.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong className="text-foreground">
|
||||||
|
Zero Third Parties:
|
||||||
|
</strong>{" "}
|
||||||
|
Analytics data is processed locally on our own server and
|
||||||
|
never shared with external tracking services.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-foreground-muted">
|
||||||
|
The legal basis for this processing is our legitimate interest in
|
||||||
|
maintaining and optimizing our online portfolio (Art. 6(1)(f)
|
||||||
|
GDPR).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Your Rights */}
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
|
||||||
|
<h2 className="text-base font-semibold text-foreground flex items-center gap-2 font-mono">
|
||||||
|
<span className="text-primary">//</span> Your Rights
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
You have the right at any time to receive information free of
|
||||||
|
charge about the origin, recipient, and purpose of your stored
|
||||||
|
personal data. You also have a right to request the correction or
|
||||||
|
deletion of this data.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -6,4 +6,16 @@
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>1.0</priority>
|
<priority>1.0</priority>
|
||||||
</url>
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://flowstate.andre-kempf.com/imprint</loc>
|
||||||
|
<lastmod>2026-08-14</lastmod>
|
||||||
|
<changefreq>yearly</changefreq>
|
||||||
|
<priority>0.3</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://flowstate.andre-kempf.com/privacy</loc>
|
||||||
|
<lastmod>2026-08-14</lastmod>
|
||||||
|
<changefreq>yearly</changefreq>
|
||||||
|
<priority>0.3</priority>
|
||||||
|
</url>
|
||||||
</urlset>
|
</urlset>
|
||||||
Loading…
Reference in a new issue