From 6f033d52b5bc261dc00d81d451dc78cac7ddb62d Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 31 Jul 2026 21:58:04 +0000 Subject: [PATCH] Add persistent account navigation (sidebar/tabs); fix CTA arrow on Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Konto-Aktionen links (profile, wishlist, logout) used to live at the bottom of the orders page's own content — unreachable once the order list got long enough. New KontoShell + AccountNav give every /konto/* page a persistent sidebar (sm+) / tab bar (mobile) instead, always reachable regardless of list length. The order filters also collapse behind a "Filter" toggle on mobile now, since the tab bar above them left little room for 3 full-width dropdowns. Also: replaced the Unicode "→" arrow in CTA links (Tools.tsx, Blog.tsx, ProductGrid.tsx) with an SVG icon — the glyph isn't covered by the site's custom fonts, so browsers fall back to a system font per-platform; confirmed sitting visibly low relative to the label text on Android/Chrome (Galaxy S22), not reproducible in desktop Chromium. An SVG has no font-fallback path, so it renders identically everywhere. Co-Authored-By: Claude Sonnet 5 --- app/components/ArrowRightIcon.tsx | 17 ++++ app/components/Blog.tsx | 5 +- app/components/Tools.tsx | 12 +-- .../bestellungen/components/OrderFilters.tsx | 56 ++++++++--- app/konto/bestellungen/page.tsx | 30 +----- app/konto/components/AccountNav.tsx | 93 +++++++++++++++++++ app/konto/components/KontoShell.tsx | 29 ++++++ app/konto/components/LogoutButton.tsx | 29 ------ app/konto/merkliste/page.tsx | 31 ++----- app/konto/profil/page.tsx | 28 +++--- app/shop/components/ProductGrid.tsx | 3 +- 11 files changed, 218 insertions(+), 115 deletions(-) create mode 100644 app/components/ArrowRightIcon.tsx create mode 100644 app/konto/components/AccountNav.tsx create mode 100644 app/konto/components/KontoShell.tsx delete mode 100644 app/konto/components/LogoutButton.tsx diff --git a/app/components/ArrowRightIcon.tsx b/app/components/ArrowRightIcon.tsx new file mode 100644 index 0000000..c506f3f --- /dev/null +++ b/app/components/ArrowRightIcon.tsx @@ -0,0 +1,17 @@ +// Replaces the Unicode "→" (U+2192) previously used inline in CTA links +// (Tools.tsx, Blog.tsx, ProductGrid.tsx) — that glyph isn't covered by the +// site's custom web fonts, so browsers fall back to a system font just for +// that one character. The fallback's vertical metrics differ enough by +// platform (confirmed: sat visibly low relative to the label text on a +// Samsung Galaxy S22/Android Chrome, not reproducible in desktop Chromium) +// that centering it via flex `items-center` alone isn't reliable across +// devices. An SVG has no font-fallback path — it renders identically +// everywhere. `currentColor` stroke follows the parent Link's own +// text/hover color, same as every other icon in this codebase. +export function ArrowRightIcon() { + return ( + + + + ); +} diff --git a/app/components/Blog.tsx b/app/components/Blog.tsx index 7aed233..1fc1072 100644 --- a/app/components/Blog.tsx +++ b/app/components/Blog.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import Image from "next/image"; import { getBlogPosts } from "../lib/payload"; import { Reveal, RevealGroup, RevealItem } from "./Reveal"; +import { ArrowRightIcon } from "./ArrowRightIcon"; export async function Blog() { const posts = await getBlogPosts(3); @@ -69,7 +70,7 @@ export async function Blog() { href={featured.href} className="flex items-center gap-1 font-bold text-body text-text-primary whitespace-nowrap hover:text-brand transition-colors" > - + Zum Beitrag @@ -122,7 +123,7 @@ export async function Blog() { href={post.href} className="flex items-center gap-1 font-bold text-body whitespace-nowrap hover:text-brand transition-colors" > - + Zum Beitrag diff --git a/app/components/Tools.tsx b/app/components/Tools.tsx index 05e7539..de9583d 100644 --- a/app/components/Tools.tsx +++ b/app/components/Tools.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import Image from "next/image"; import { Reveal, RevealGroup, RevealItem } from "./Reveal"; +import { ArrowRightIcon } from "./ArrowRightIcon"; import { getWerkzeugeCards } from "../lib/payload"; // Content now lives in Payload (WerkzeugeCards collection). Icons use a @@ -87,17 +88,14 @@ export async function Tools() { {tool.description}

- {/* flex items-center + arrow as its own span, not inline text - — the → glyph sits low relative to the surrounding text's - cap-height in the font used here, off-center against the - label if it's just part of the same text node (fixed - 2026-07-24, same pattern ProductGrid.tsx's "Mehr - erfahren" link already uses). */} + {/* SVG arrow, not a Unicode "→" character — see + ArrowRightIcon.tsx's own comment on why (font-fallback + vertical-metrics mismatch, platform-dependent). */} - + {tool.ctaLabel} diff --git a/app/konto/bestellungen/components/OrderFilters.tsx b/app/konto/bestellungen/components/OrderFilters.tsx index 097795f..f0c0a68 100644 --- a/app/konto/bestellungen/components/OrderFilters.tsx +++ b/app/konto/bestellungen/components/OrderFilters.tsx @@ -1,5 +1,6 @@ "use client"; +import { useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { CustomSelect } from "../../../components/CustomSelect"; @@ -31,6 +32,16 @@ export function OrderFilters({ const paymentStatus = searchParams.get("paymentStatus") ?? ""; const year = searchParams.get("year") ?? ""; const hasAnyFilter = Boolean(status || paymentStatus || year); + const activeCount = [status, paymentStatus, year].filter(Boolean).length; + + // Collapsed by default on mobile — with the account tab bar now also + // stacked above this (see KontoShell/AccountNav), 3 full-width dropdowns + // always visible left little room for the actual order list. Desktop + // (sm+) ignores this entirely and always shows the row inline, same as + // before. Starts expanded whenever a filter is already active (arriving + // via a shared/bookmarked filtered URL shouldn't hide what's applied) — + // a lazy initializer since it only needs to run once, on mount. + const [expanded, setExpanded] = useState(() => hasAnyFilter); function setParam(key: string, value: string) { const params = new URLSearchParams(searchParams.toString()); @@ -43,19 +54,38 @@ export function OrderFilters({ const yearOptions = years.map((y) => ({ value: y, label: y })); return ( -
- setParam("status", v)} /> - setParam("paymentStatus", v)} /> - setParam("year", v)} /> - {hasAnyFilter && ( - - )} +
+ +
+ setParam("status", v)} /> + setParam("paymentStatus", v)} /> + setParam("year", v)} /> + {hasAnyFilter && ( + + )} +
); } diff --git a/app/konto/bestellungen/page.tsx b/app/konto/bestellungen/page.tsx index c067048..98efeda 100644 --- a/app/konto/bestellungen/page.tsx +++ b/app/konto/bestellungen/page.tsx @@ -3,7 +3,6 @@ import { Suspense } from "react"; import { redirect } from "next/navigation"; import Link from "next/link"; import { Reveal } from "../../components/Reveal"; -import { Footer } from "../../components/Footer"; import { formatPrice, formatDate } from "../../lib/format"; import { getSessionCustomer, @@ -14,7 +13,7 @@ import { import { getOrderFilterEnabled } from "../../lib/payload"; import { OrderStatusBadge } from "../components/OrderStatusBadge"; import { PaymentStatusBadge } from "../components/PaymentStatusBadge"; -import { LogoutButton } from "../components/LogoutButton"; +import { KontoShell } from "../components/KontoShell"; import { OrderFilters } from "./components/OrderFilters"; // robots: noindex — account area, same reasoning as /checkout. @@ -60,9 +59,8 @@ export default async function KontoBestellungenPage({ ]); return ( - <> -
- + +

Meine Bestellungen

@@ -135,25 +133,7 @@ export default async function KontoBestellungenPage({
)} - {/* self-center below sm — this row otherwise inherits the parent - Reveal's items-start (left-aligned); centered here per its - own request, but only the row itself (self-center keeps its - shrink-to-fit content width, doesn't stretch it to the full - parent width the way w-full/justify-center on the parent - would). Back to left-aligned (matching the rest of the page) - from sm up. */} -
- - Profil & Adresse - - - Weiter einkaufen - - -
- - -