feat: Security CSRF Fix, Logout optimierung, Footer Anpassung, Rechtstexteanpassung
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 1m33s

This commit is contained in:
Nick Szaunig committed 2026-09-03 13:51:50 +02:00
1 parent 0753a3bc8a
commit 4d3100e9ba
28 files changed
+802 -121

No files matched your search

+99 -8
View File
@@ -1,7 +1,7 @@
"use client";
import Link from "next/link";
import { useState, useEffect, useRef, type RefObject } from "react";
import { useState, useEffect, useRef, type RefObject, type ReactNode } from "react";
import { AnimatePresence, motion } from "motion/react";
import { useCartStore, cartItemCount, cartTotalCents } from "@/store/cart-store";
import { formatPrice } from "@/lib/format";
@@ -512,12 +512,88 @@ export function CartDrawer() {
);
}
export function Footer({
companyInfo,
// Erstes vollstaendiges Geschaeftsjahr — Startpunkt der im Footer
// angezeigten Copyright-Spanne "2023–<aktuelles Jahr>". Das Endjahr wird
// bewusst NICHT gespeichert, sondern bei jedem Render live aus
// new Date().getFullYear() berechnet, damit es nie per Hand nachgepflegt
// werden muss.
const FOUNDING_YEAR = 2023;
const SOCIAL_ICON_PROPS = { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5 } as const;
function TikTokIcon() {
return (
<svg {...SOCIAL_ICON_PROPS} aria-hidden="true">
<path d="M14 4v10.5a3.5 3.5 0 1 1-3.5-3.5" />
<path d="M14 4c.3 1.8 1.6 3.6 4 4" />
</svg>
);
}
function YouTubeIcon() {
return (
<svg {...SOCIAL_ICON_PROPS} aria-hidden="true">
<rect x="2" y="5" width="20" height="14" rx="4" />
<path d="M10 9l6 3-6 3V9z" fill="currentColor" stroke="none" />
</svg>
);
}
function InstagramIcon() {
return (
<svg {...SOCIAL_ICON_PROPS} aria-hidden="true">
<rect x="3" y="3" width="18" height="18" rx="5" />
<circle cx="12" cy="12" r="4" />
<circle cx="17.5" cy="6.5" r="0.75" fill="currentColor" stroke="none" />
</svg>
);
}
interface SocialLinks {
tiktok: string;
youtube: string;
instagram: string;
}
/**
* Ein Icon+Name-Link wird nur gerendert, wenn im Admin-Bereich (Inhalte ->
* Social Media) tatsaechlich eine URL hinterlegt ist — nie ein toter/
* Platzhalter-Link (siehe getSocialLinks()/content-registry.ts).
*/
function SocialLink({
href,
name,
icon,
}: {
companyInfo: { name: string; email: string; address: { street: string; zip: string; city: string } };
href: string;
/** Plattformname (Eigenname, z.B. "TikTok") — unlokalisiert, dient als sichtbarer Linktext. */
name: string;
icon: ReactNode;
}) {
const { t } = useI18n();
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={t.footer.socialAriaLabel(name)}
className="flex items-center gap-1.5 text-cream/70 hover:text-cream transition-colors"
>
{icon}
<span className="text-xs">{name}</span>
</a>
);
}
export function Footer({
companyInfo,
socialLinks,
}: {
companyInfo: { name: string; email: string; address: { street: string; zip: string; city: string } };
socialLinks: SocialLinks;
}) {
const { t } = useI18n();
const hasAnySocialLink = socialLinks.tiktok || socialLinks.youtube || socialLinks.instagram;
const wabenhainLabels: Record<(typeof footerNav.wabenhain)[number]["key"], string> = {
aboutUs: t.footer.aboutUs,
@@ -527,8 +603,6 @@ export function Footer({
};
const einkaufenLabels: Record<(typeof footerNav.einkaufen)[number]["key"], string> = {
shop: t.nav.shop,
shipping: t.footer.shipping,
pickup: t.footer.pickup,
businessCustomers: t.footer.businessCustomers,
trackOrder: t.footer.trackOrder,
};
@@ -565,8 +639,25 @@ export function Footer({
<FooterColumn title={t.footer.columnShopping} links={footerNav.einkaufen} labels={einkaufenLabels} />
<FooterColumn title={t.footer.columnLegal} links={footerNav.rechtliches} labels={rechtlichesLabels} />
</div>
<div className="mt-16 pt-8 border-t border-cream/15 flex flex-col md:flex-row justify-between gap-4 text-xs text-cream/70">
<span>© {new Date().getFullYear()} Wabenhain</span>
<div className="mt-16 pt-8 border-t border-cream/15 flex flex-col md:flex-row md:items-center justify-between gap-4 text-xs text-cream/70">
<div className="flex flex-wrap items-center gap-x-6 gap-y-3">
<span>
© {FOUNDING_YEAR}–{new Date().getFullYear()} {companyInfo.name}
</span>
{hasAnySocialLink ? (
<div className="flex items-center gap-4">
{socialLinks.tiktok ? (
<SocialLink href={socialLinks.tiktok} name="TikTok" icon={<TikTokIcon />} />
) : null}
{socialLinks.youtube ? (
<SocialLink href={socialLinks.youtube} name="YouTube" icon={<YouTubeIcon />} />
) : null}
{socialLinks.instagram ? (
<SocialLink href={socialLinks.instagram} name="Instagram" icon={<InstagramIcon />} />
) : null}
</div>
) : null}
</div>
<span>{t.footer.bottomLine}</span>
</div>
</Container>