From 37998aad46c468c1d61d744c017f62da547cdd9b Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 17 Jun 2026 14:45:33 -0600 Subject: [PATCH] feat(admin): add OrdersFilterButton (status filter via native dialog) --- .../admin/orders/OrdersFilterButton.tsx | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/components/admin/orders/OrdersFilterButton.tsx diff --git a/src/components/admin/orders/OrdersFilterButton.tsx b/src/components/admin/orders/OrdersFilterButton.tsx new file mode 100644 index 0000000..1188bf2 --- /dev/null +++ b/src/components/admin/orders/OrdersFilterButton.tsx @@ -0,0 +1,114 @@ +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useRef, useState } from "react"; + +/** + * Filter button for the Orders list. Opens a native as a + * right-edge sheet (matching the MoreSheet pattern) with status options. + * Picking a status pushes a new URL with the `status` search param — + * the server component re-renders with the filter applied. + * + * The status values here are the v2 StatusPill vocabulary + * (placed/ready/picked-up/cancelled) which is what the orders page + * filters on. Empty string = clear filter (all orders). + */ +export function OrdersFilterButton() { + const router = useRouter(); + const searchParams = useSearchParams(); + const dialogRef = useRef(null); + const [open, setOpen] = useState(false); + + useEffect(() => { + const dialog = dialogRef.current; + if (!dialog) return; + if (open && !dialog.open) dialog.showModal(); + if (!open && dialog.open) dialog.close(); + }, [open]); + + // Sync the dialog's open state when the user closes via Escape + // (native behavior — fires a "close" event, not a React event). + useEffect(() => { + const dialog = dialogRef.current; + if (!dialog) return; + const onClose = () => setOpen(false); + dialog.addEventListener("close", onClose); + return () => dialog.removeEventListener("close", onClose); + }, []); + + const currentStatus = searchParams.get("status") ?? ""; + + function apply(status: string) { + const params = new URLSearchParams(searchParams.toString()); + if (status) params.set("status", status); + else params.delete("status"); + router.push(`/admin/v2/orders?${params.toString()}`); + setOpen(false); + } + + return ( + <> + + { if (e.target === dialogRef.current) setOpen(false); }} + className="w-full max-w-[640px] p-0 m-0 ml-auto h-full max-h-screen backdrop:bg-black/40" + style={{ backgroundColor: "var(--color-bg)", color: "var(--color-text)" }} + > +
+
+

Filter

+ +
+
+ {["", "placed", "ready", "picked-up", "cancelled"].map((s) => ( + + ))} +
+
+
+ + ); +} + +export default OrdersFilterButton;