diff --git a/AGENTS.md b/AGENTS.md
index 270ebcc..880ec30 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -71,7 +71,13 @@ After discovering the project's specific touchpoints, use a checklist like this
## Data Fetching with React Query
-This project uses **@tanstack/react-query** (v5) for client-side data fetching. The `QueryClientProvider` is set up in `pages/_app.tsx`.
+This project uses **@tanstack/react-query** (v5) for client-side data fetching.
+
+> ⚠️ **Two separate provider trees.** This app mixes the **Pages Router** and the **App Router**, and they do **not** share a `QueryClientProvider`:
+> - **Pages Router** (`pages/**`, customer-facing pages): the `QueryClientProvider` is set up in `pages/_app.tsx`. Components rendered here (e.g. `components/pickup/TimeslotSelector.tsx`) get a client automatically.
+> - **App Router** (`app/(payload)/**`, Payload admin custom views): these render **outside** `pages/_app.tsx` and therefore have **no** QueryClient. Using `useQuery`/`useMutation` there without a provider throws `No QueryClient set, use QueryClientProvider to set one` at runtime.
+>
+> The Payload admin `app/(payload)/layout.tsx` is **auto-generated ("DO NOT MODIFY")**, so do not wrap it. Instead, any admin (App Router) component that uses React Query must wrap its own content in **`components/admin/AdminQueryProvider.tsx`**. The standard pattern is to split the view into an inner component (which holds the hooks) and a default export that wraps the inner component in `AdminQueryProvider` — see `components/admin/OrdersByTimeslotView.tsx`. Keep `AdminQueryProvider`'s defaults in sync with the Pages Router provider in `pages/_app.tsx`.
### Guidelines
@@ -99,13 +105,14 @@ This project uses **@tanstack/react-query** (v5) for client-side data fetching.
6. **Do not install `react-query`** (v3). The package is `@tanstack/react-query` (v5).
+7. **Admin (App Router) views** must wrap their content in `components/admin/AdminQueryProvider.tsx` (see the "two separate provider trees" note above). Do not rely on the `pages/_app.tsx` provider for anything under `app/(payload)/**`.
+
### Refactoring roadmap
The following components still use raw `useEffect` + `fetch` and should be migrated to React Query as they are touched:
- `components/ordercontainer/OrderContainer.tsx` — order resumption & pending orders fetch
- `components/admin/PendingVerificationView.tsx`
-- `components/admin/OrdersByTimeslotView.tsx`
- `components/admin/ScheduleCalendarView.tsx`
- `components/admin/NotifyTimeslotsView.tsx`
- `components/payment/StripePaymentForm.tsx`
@@ -113,4 +120,8 @@ The following components still use raw `useEffect` + `fetch` and should be migra
- `pages/my-orders.tsx`
- `pages/order_complete.tsx`
-When refactoring these components, follow the pattern established in `components/pickup/TimeslotSelector.tsx`.
+Already migrated (use as references):
+
+- ✅ `components/admin/OrdersByTimeslotView.tsx` — admin (App Router) view; wraps its default export in `AdminQueryProvider`.
+
+When refactoring Pages Router components, follow the pattern established in `components/pickup/TimeslotSelector.tsx`. When refactoring **admin (App Router) views** (the remaining `components/admin/*` entries above), additionally wrap the view in `AdminQueryProvider` as shown in `components/admin/OrdersByTimeslotView.tsx`.
diff --git a/components/admin/AdminQueryProvider.tsx b/components/admin/AdminQueryProvider.tsx
new file mode 100644
index 0000000..b7299e1
--- /dev/null
+++ b/components/admin/AdminQueryProvider.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import type React from "react";
+import { useState } from "react";
+
+/**
+ * Provides a React Query client for Payload admin custom views.
+ *
+ * Payload admin views render inside the App Router (`app/(payload)`) tree,
+ * which is NOT wrapped by the `QueryClientProvider` in `pages/_app.tsx`
+ * (that only covers the Pages Router). Each admin view that uses React Query
+ * must therefore wrap its content in this provider so a QueryClient is
+ * available. Defaults are kept in sync with the Pages Router provider.
+ */
+export default function AdminQueryProvider({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 30 * 1000, // 30 seconds
+ retry: 1,
+ },
+ },
+ }),
+ );
+
+ return (
+ {children}
+ );
+}
diff --git a/components/admin/OrdersByTimeslotView.tsx b/components/admin/OrdersByTimeslotView.tsx
index bfdbb3b..67ef1ac 100644
--- a/components/admin/OrdersByTimeslotView.tsx
+++ b/components/admin/OrdersByTimeslotView.tsx
@@ -4,6 +4,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useRef, useState } from "react";
import type { OrderStatusValue } from "../../types/orderStatus";
import { OrderStatus, PAID_STATUSES } from "../../types/orderStatus";
+import AdminQueryProvider from "./AdminQueryProvider";
import BackToDashboard from "./BackToDashboard";
interface TimeslotData {
@@ -83,7 +84,7 @@ async function updateOrderStatus(
* This is the view the admin uses in-person to manage pickups —
* see which orders are coming for each timeslot and mark them as picked up.
*/
-export default function OrdersByTimeslotView() {
+function OrdersByTimeslotViewInner() {
const [filter, setFilter] = useState("upcoming");
const queryClient = useQueryClient();
@@ -749,3 +750,18 @@ export default function OrdersByTimeslotView() {
);
}
+
+/**
+ * Default export wraps the view in its own QueryClientProvider.
+ *
+ * Payload admin custom views render outside the Pages Router tree, so they
+ * don't inherit the QueryClient from `pages/_app.tsx`. Wrapping here makes
+ * the view self-sufficient.
+ */
+export default function OrdersByTimeslotView() {
+ return (
+
+
+
+ );
+}