Keine Bestellungen gefunden, die zu den gewählten Filtern passen.
) : (
{orders.map((order) => (
diff --git a/app/lib/customerAuth.ts b/app/lib/customerAuth.ts
index fa8295d..2a5ef34 100644
--- a/app/lib/customerAuth.ts
+++ b/app/lib/customerAuth.ts
@@ -498,15 +498,61 @@ function excludeFailedPaymentAttemptsQuery(): Record {
};
}
+// "Offen" in the account UI's PaymentStatusBadge covers two distinct
+// backend values (an unconfirmed Stripe payment vs. an Überweisung order
+// awaiting manual reconciliation) — the filter chip mirrors that same
+// grouping rather than exposing the internal distinction as two options.
+const OPEN_PAYMENT_STATUSES = ["pending", "not_applicable"] as const;
+
+export type CustomerOrderFilters = {
+ status?: string;
+ paymentStatus?: string;
+ /** Calendar year as a string, e.g. "2026" — matches createdAt within
+ * [Jan 1, Jan 1 of next year). */
+ year?: string;
+};
+
+function customerOrderFilterQuery(filters: CustomerOrderFilters | undefined, whereIndex: number): Record {
+ if (!filters) return {};
+ const params: Record = {};
+ let i = whereIndex;
+ if (filters.status) {
+ params[`where[and][${i}][status][equals]`] = filters.status;
+ i += 1;
+ }
+ if (filters.paymentStatus) {
+ if (filters.paymentStatus === "open") {
+ OPEN_PAYMENT_STATUSES.forEach((value, j) => {
+ params[`where[and][${i}][or][${j}][paymentStatus][equals]`] = value;
+ });
+ } else {
+ params[`where[and][${i}][paymentStatus][equals]`] = filters.paymentStatus;
+ }
+ i += 1;
+ }
+ if (filters.year && /^\d{4}$/.test(filters.year)) {
+ const year = Number(filters.year);
+ params[`where[and][${i}][createdAt][greater_than_equal]`] = new Date(Date.UTC(year, 0, 1)).toISOString();
+ params[`where[and][${i}][createdAt][less_than]`] = new Date(Date.UTC(year + 1, 0, 1)).toISOString();
+ }
+ return params;
+}
+
// `excludeFailedPaymentAttempts` defaults to true (list views) — the one
// exception is /api/account/export/route.ts's GDPR data export, which
// passes false: a legal completeness export must include every order
// row that exists about this customer, not just the ones normally shown
// in "Meine Bestellungen".
-export async function getCustomerOrders(token: string, customerId: number, excludeFailedPaymentAttempts = true): Promise {
+export async function getCustomerOrders(
+ token: string,
+ customerId: number,
+ excludeFailedPaymentAttempts = true,
+ filters?: CustomerOrderFilters,
+): Promise {
const params = new URLSearchParams({
"where[and][0][customer][equals]": String(customerId),
...(excludeFailedPaymentAttempts ? excludeFailedPaymentAttemptsQuery() : {}),
+ ...customerOrderFilterQuery(filters, excludeFailedPaymentAttempts ? 2 : 1),
sort: "-createdAt",
depth: "0",
limit: "50",
@@ -537,6 +583,16 @@ export async function getCustomerOrders(token: string, customerId: number, exclu
}));
}
+// All years that have at least one (non-filtered-out) order for this
+// customer — powers the year filter's option list without hardcoding a
+// range. Cheap: reuses the same excludeFailedPaymentAttempts query, no
+// separate collection/aggregation endpoint needed for this order volume.
+export async function getCustomerOrderYears(token: string, customerId: number): Promise {
+ const orders = await getCustomerOrders(token, customerId, true);
+ const years = new Set(orders.map((o) => new Date(o.createdAt).getUTCFullYear().toString()));
+ return Array.from(years).sort((a, b) => Number(b) - Number(a));
+}
+
export type CustomerOrderDetail = CustomerOrder & {
id: number;
// 'manual' (Überweisung) vs 'stripe' (Kreditkarte/PayPal) — see