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 ( + + + + ); +}