From 475ee3fa160568b9772e1074df588b4e3b6108a8 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 30 Jul 2026 22:11:12 +0000 Subject: [PATCH] Replace order-filter chip wall with 3 compact selects; subgrid-align trust badges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OrderFilters.tsx: filter chips (7 status + 5 payment-status + N year options as pills) read as cluttered and ate a lot of vertical space. Replaced with 3 native s in a row instead of a wall of filter chips (tried +// first, reverted 2026-07-30 — with 7 status options + 5 payment-status +// options + N years, a chip per option read as cluttered and ate a lot of +// vertical space). Client Component only for the onChange→navigate wiring; +// the actual filtering still happens server-side in page.tsx via the same +// URL search params, so this stays a plain GET-style filter (shareable/ +// bookmarkable/back-button-safe), not client-side state. +export function OrderFilters({ + statusOptions, + paymentStatusOptions, + years, +}: { + statusOptions: { value: string; label: string }[]; + paymentStatusOptions: { value: string; label: string }[]; + years: string[]; +}) { + const router = useRouter(); + const searchParams = useSearchParams(); + + const status = searchParams.get("status") ?? ""; + const paymentStatus = searchParams.get("paymentStatus") ?? ""; + const year = searchParams.get("year") ?? ""; + const hasAnyFilter = Boolean(status || paymentStatus || year); + + function setParam(key: string, value: string) { + const params = new URLSearchParams(searchParams.toString()); + if (value) params.set(key, value); + else params.delete(key); + const qs = params.toString(); + router.push(qs ? `/konto/bestellungen?${qs}` : "/konto/bestellungen"); + } + + const selectClass = + "w-full sm:w-auto min-w-0 border border-border rounded-sm px-3 py-2 text-body-sm text-text-primary bg-bg-base outline-none focus:border-brand transition-colors"; + + return ( +
+ + + + {hasAnyFilter && ( + + )} +
+ ); +} diff --git a/app/konto/bestellungen/page.tsx b/app/konto/bestellungen/page.tsx index e6d00ad..73f6388 100644 --- a/app/konto/bestellungen/page.tsx +++ b/app/konto/bestellungen/page.tsx @@ -1,4 +1,5 @@ import type { Metadata } from "next"; +import { Suspense } from "react"; import { redirect } from "next/navigation"; import Link from "next/link"; import { Reveal } from "../../components/Reveal"; @@ -13,6 +14,7 @@ import { import { OrderStatusBadge } from "../components/OrderStatusBadge"; import { PaymentStatusBadge } from "../components/PaymentStatusBadge"; import { LogoutButton } from "../components/LogoutButton"; +import { OrderFilters } from "./components/OrderFilters"; // robots: noindex — account area, same reasoning as /checkout. export const metadata: Metadata = { @@ -34,45 +36,8 @@ const PAYMENT_STATUS_FILTER_LABEL: Record = { partially_refunded: "Teilweise erstattet", }; -function FilterChip({ - href, - active, - children, -}: { - href: string; - active: boolean; - children: React.ReactNode; -}) { - return ( - - {children} - - ); -} - -// Every filter is expressed as plain URL search params (?status=…&paymentStatus=…&year=…) -// rather than client-side state — this page stays a Server Component, each -// chip is just a to the same route with one param changed/removed, -// and the filtered result is shareable/bookmarkable/back-button-safe for -// free. buildFilterHref() only ever changes ONE param at a time, keeping -// the other active filters intact (a status filter + a year filter can -// both be active together). -function buildFilterHref(current: Record, key: string, value: string | undefined): string { - const params = new URLSearchParams(); - const next = { ...current, [key]: value }; - for (const [k, v] of Object.entries(next)) { - if (v) params.set(k, v); - } - const qs = params.toString(); - return qs ? `/konto/bestellungen?${qs}` : "/konto/bestellungen"; -} +const STATUS_OPTIONS = Object.entries(ORDER_STATUS_LABEL).map(([value, label]) => ({ value, label })); +const PAYMENT_STATUS_OPTIONS = Object.entries(PAYMENT_STATUS_FILTER_LABEL).map(([value, label]) => ({ value, label })); export default async function KontoBestellungenPage({ searchParams, @@ -83,13 +48,11 @@ export default async function KontoBestellungenPage({ if (!session) redirect("/konto/login"); const { status, paymentStatus, year } = await searchParams; - const activeFilters = { status, paymentStatus, year }; const [orders, availableYears] = await Promise.all([ getCustomerOrders(session.token, session.customer.id, true, { status, paymentStatus, year }), getCustomerOrderYears(session.token, session.customer.id), ]); - const hasAnyFilter = Boolean(status || paymentStatus || year); return ( <> @@ -103,42 +66,9 @@ export default async function KontoBestellungenPage({

{availableYears.length > 0 && ( -
- - Alle Status - - {Object.entries(ORDER_STATUS_LABEL).map(([value, label]) => ( - - {label} - - ))} -
- - Alle Zahlungsstatus - - {Object.entries(PAYMENT_STATUS_FILTER_LABEL).map(([value, label]) => ( - - {label} - - ))} -
- - Alle Jahre - - {availableYears.map((y) => ( - - {y} - - ))} - {hasAnyFilter && ( - - Filter zurücksetzen - - )} -
+ + + )} {availableYears.length === 0 ? (