# QA Audit Inventory — 2026-06-25 > Generated for [Loop 010 — full product evaluation](https://signals.forwardfuture.com/loop-library/loops/full-product-evaluation-loop/). > Scope: Admin shell (all 19 modules) + public/auth pages, tested as `platform_admin` via `dev_session` cookie. > Source: 5 parallel `explore` subagents enumerated every UI control, server action, state, and workflow. ## Inventory organization - `R1`–`R4`: Role/identity assumptions - `A1`–`A11`: Admin shell, layout, dashboard - `M1`–`M11`: Admin modules (orders, stops, products, pickup, shipping, communications, wholesale, settings, analytics, users, import) - `P1`–`P19`: Public + auth pages - `T1`–`T6`: Cross-cutting patterns, design system, server-action catalog Each item: route → purpose → buttons → forms → modals → states → workflows → server actions. --- ## R — Roles & identity ### R1 — `dev_session` cookie impersonation **File:** `src/lib/admin-permissions.ts` lines 36-39. Gated by `NODE_ENV !== "production"`. Values: `platform_admin`, `brand_admin`, `store_employee`. Returns admin user via `buildDevAdmin(role)` bypassing Neon Auth. ### R2 — Real Neon Auth (Better Auth) **File:** `src/lib/auth.ts`. Email/password sign-in, password reset, OAuth via `signInWithGoogleAction`. Session via `getSession()`. **Not exercised** in this audit (auth is stubbed). ### R3 — RBAC permission flags **File:** `admin_users` table; flags: `can_manage_orders`, `can_manage_products`, `can_manage_stops`, `can_manage_customers`, `can_manage_wholesale`, `can_manage_billing`, `can_manage_settings`, `can_manage_water_log`, `can_manage_time_tracking`, `can_manage_route_trace`, `can_manage_reports`, `can_manage_communications`, `can_manage_pickup`, `can_manage_messages`, `can_manage_refunds`, `can_manage_users`, `active`. Default brand_admin = orders/products/stops/customers/pickup/messages/reports = `true`, rest `false`. ### R4 — Tenant scoping (brand isolation) - App-layer: `effectiveBrandId = brandId ?? adminUser.brand_id ?? null`; threaded through page → client. - DB: `current_brand_id()` GUC + RLS policies on most tables (`brand_id = current_brand_id() OR is_platform_admin()`). - Platform admin: `brand_id IS NULL`, sees all brands via `withPlatformAdmin()`. --- ## A — Admin shell, layout, dashboard ### A1 — `/admin/layout.tsx` Server root layout. Gates access via `getAdminUser()`, redirects on `must_change_password`. Mounts: `AdminSidebar` (desktop), `MobileTabBar` (mobile via v2 layout), `CommandPalette`, `PWAInstallPrompt`, `ToastContainer`, `RouteAnnouncer`, `SmoothViewTransition`. Brand/brand-list/addons fetched with empty fallbacks on error. ### A2 — `/admin/page.tsx` (legacy dashboard) Redirects to `/admin/v2`. Two paths: `role === "store_employee"` → 2-card landing (Pickup Lookup + Wholesale); otherwise fetches `getDashboardStats()` + `getBillingOverview()` server-side, renders `DashboardClient`. ### A3 — `/admin/v2/layout.tsx` + `/admin/v2/page.tsx` (mobile-first dashboard) Mobile "Today" dashboard. Fetches summary/stops/orders in parallel. `Promise.all` failure → silent empty state. Renders `EmptyState` ("No brand selected" / "Nothing to show yet"). `pending_fulfillment > 0` → amber callout link `/admin/v2/orders?status=placed`. Top 5 today stops + top 5 pending orders. Pull-to-refresh wrapper. ### A4 — `AdminShell.tsx` Responsive shell: `useMediaQuery("(min-width: 1024px)")` swaps desktop sidebar vs mobile (`OfflineBanner` + `MobileTabBar`). Mounts `OfflineBanner` only on mobile. `pb-20` mobile bottom padding for tab bar. ### A5 — `AdminSidebar.tsx` Desktop nav + mobile slide-in. Nav groups: Workspace / Operations / Communications / Growth / Tracking / Insights / Settings. Brand selector pill. Sign-out button (`signOutAction`). Hamburger on ``. Sections: Operations (Pickup, Shipping, Reports, Analytics), Marketing (Communications, Sales), Settings (Settings, Advanced). Saves focus on open, restores on close. ### A8 — `CommandPalette.tsx` Cmd/Ctrl+K modal. Fuzzy search over static entry list (`./command-palette-data.ts`). Up to 8 results. ↑↓ to navigate, Enter to select, ESC closes. `body.style.overflow = "hidden"` while open. Honors `prefers-reduced-motion`. ### A9 — `BrandSelector.tsx` Pill dropdown for active brand. Calls `setActiveBrand(brandId)` server action, then `router.refresh()`. Outside-click + ESC close. "All brands" option for platform_admin only. Multi-brand badge. ### A10 — `AdminAccessDenied.tsx` Static card with "Go to Login" + "Return to homepage" links. ### A11 — `OfflineBanner.tsx` Sticky top banner: warning-soft when syncing, danger-soft when offline. Polls pendingCount every 1 s. Hidden pre-mount to avoid hydration mismatch. --- ## M — Admin modules ### M1 — Orders - `/admin/orders` (legacy) → `AdminOrdersPanel`: KPI tiles, status tabs (all/pending/picked_up), search (name/phone/id), stop multi-filter dropdown, table with checkbox + bulk pickup, pagination (PAGE_SIZE 20), `+ New Order` modal. Modal fields: customer name (required), email (optional), phone (optional), stop (optional), items[product+qty+price+fulfillment]. Workflows: create→full-page-reload, single pickup (optimistic), bulk pickup (loop). - `/admin/orders/[id]` → `OrderEditForm` + `OrderPaymentSection` + `OrderPickupAction`. Edit fields: items qty/price, discount (must be ≥ 0), customer name (required), email/phone, status (pending/confirmed/cancelled — **fulfilled not exposed**), pickup toggle, internal notes. Payment fields: processor (none/manual/stripe/square/cash/venmo/other), payment status, transaction id, refund amount (0 < x ≤ remaining balance) + reason. - `/admin/orders/new` → redirect to `/admin/orders?new=true` (opens modal). - `/admin/v2/orders` → card-style list, `?status=` filter (placed/ready/picked-up/cancelled), LIMIT 50, no pagination. - `/admin/v2/orders/[id]` → mobile detail with `FulfillmentTimeline` + sticky action bar (`OrderActionButtons`). ### M2 — Stops - `/admin/stops` → `AdminStopsPanel`: status tabs (all/active/draft/inactive), search city/location, `Add Stop` modal, `Upload Schedule` modal, per-row Edit/Publish/Duplicate/Delete. Delete uses inline popover confirm. PAGE_SIZE 50. - `/admin/stops/[id]` → product assignment + edit form + `MessageCustomersSection`. - `/admin/stops/new` → inline `NewStopForm` (not modal). Fields: city/state/location/date/time (required), brand select, active, address/zip/cutoff (optional). Also supports `?duplicate={id}`. - `/admin/v2/stops` → card list bucketed by Morning/Afternoon/Evening/Anytime, `?date=` filter, LIMIT 200. - Modals: `AddStopModal`, `EditStopModal`, `AddLocationModal`, `EditLocationModal`, `LocationsTab`. ### M3 — Products - `/admin/products` → `ProductsClient` (legacy): list with brand scoping (platform_admin sees all). No client pagination. - `/admin/products/new` → `NewProductForm`: name (required), description, price, type (pickup/shipping/both), brand (locked for non-platform_admin), taxable, pickup_type, active, image upload (drag/drop, 5MB cap, client-side resize to 1200px). - `/admin/products/import` → CSV upload → `parseProductCSV` → preview → `importProductsBatch`. - `/admin/v2/products` → mobile card list with `?filter=` (all/in-stock/low/out/hidden) + `StockAdjustButton` per card. ### M4 — Pickup - `/admin/pickup` → `DriverPickupPanel`. Pending orders (filter by stop + search), per-order `Pick Up` button (calls `markPickupComplete`). Picked-up section collapsible (last 72h). Toast `✓ Order picked up` 3s. ### M5 — Shipping - `/admin/shipping` → `ShippingFulfillmentPanel`. Pre-loaded via `getShippingOrders()`. (Details in deeper inspection.) - `/admin/settings/shipping` (advanced subtab) → `AdvancedShipping`: static UI mock — no server action calls. Carrier select (fedex/ups/usps/dhl), account/api fields, eye toggle, **Test Connection is simulated (1s setTimeout), Save Settings is simulated (500ms setTimeout)**. ### M6 — Communications (Harvest Reach) Unified hub at `/admin/communications` with 8 tabs: - **Campaigns**: list with type/status filters. `New Campaign` modal (name + campaignType). Edit panel: name, type, template, audience target (stop/zip_code/customer_history/all_customers), stop_id/dates, subject, body, schedule (now/later + datetime-local). Save Draft / Schedule Campaign / Send Campaign buttons. `upsertCampaign`, `deleteCampaign`, `getCampaignTemplates`, `getCommunicationSegments`, `previewCampaignAudience`, `sendCampaign` server actions. - **Compose**: legacy redirect target. - **Templates**: list + edit (template_type, subject, body_text, body_html, campaign_type). Server: `getCommunicationTemplates`, `getTemplateById`. - **Contacts**: search + source filter, pagination (50/page, offset), per-row delete (confirm), export CSV (`exportContacts`). Bulk: none. Card layout on `` "Visit Store" + "View Stops". 3D-tilt parallax. ### P15 — `/waitlist` ``. - Fields: name (optional text), email (required), referral select (Google/Social/Friend/Event/Podcast/Blog/Other). - Button: Join the Waitlist (spinner → success card). - Server: `POST /api/waitlist`. - States: idle, isSubmitting, error, success. ### P16 — `/cart` ``. - Per-item buttons: − (decrease qty), + (increase qty), Remove. - Modal: "Choose Pickup Stop" (stop buttons + Cancel). - Buttons: Continue to Checkout (state-gated: Cart is Empty / Select Pickup Stop First / Remove Incompatible Items First / Choose Correct Stop / Continue Shopping). - States: empty, stop mismatch alert, incompatible items, availability error, loading stops. ### P17 — `/checkout` `` + Stripe Express iframe. - Fields: customer_name (text), customer_email (required), customer_phone (tel optional), stop_id (select required when no stop), shipping_address/city/state (maxLength 2 upper)/postal_code (when ship items). - Buttons: Change pickup stop, "Use secure hosted checkout →" (`POST createRetailStripeCheckoutSession`), `` Back to storefront. - States: empty cart, hostedLoading, hostedError, hasPickup/hasShed/hasStop/hasShip branching. ### P18 — Auth API routes | Route | Method | Behavior | |---|---|---| | `/api/auth/sign-in` | POST | `{success}` or 400/401/500 with `{error,message}`. | | `/api/auth/sign-out` | POST | Server sign-out + redirect `/login`. | | `/api/auth/forgot-password` | POST | Always `{success:true}` (anti-enumeration). Calls `requestPasswordReset`. | | `/api/auth/reset-password` | POST | Min 8 chars. | | `/api/auth/change-password` | POST | Auth-required; uses `setUserPassword`. | | `/api/auth/[...nextauth]` | GET/POST | Neon Auth handler proxy. | ### P19 — Other public pages - `/water` — likely the field portal; brief enumeration. - `/test-simple.html` — diagnostic HTML. - `/api/feed/changelog.xml` — referenced but not in scope. --- ## T — Cross-cutting patterns ### T1 — Server-action catalog (top-level actions touched by the audited routes) `getAdminUser`, `getAdminOrders`, `getAdminOrderDetail`, `markPickupComplete`, `createAdminOrder`, `updateOrder`, `updateOrderItem`, `deleteOrderItem`, `createRefund`, `getShippingOrders`, `createStop`, `updateStop`, `deleteStop`, `publishStop`, `assignProductToStop`, `unassignProductFromStop`, `createLocation`, `updateLocation`, `deleteLocation`, `createProduct`, `uploadProductImage`, `importProductsBatch`, `parseProductCSV`, `analyzeImport`, `executeImport`, `getAnalyticsMetrics`, `getRevenueChart`, `getTopProducts`, `getRecentOrders`, `getCustomerGrowth`, `getConversionFunnel`, `getCommunicationCampaigns`, `getCommunicationTemplates`, `getCommunicationContacts`, `getMessageLogs`, `getCommunicationSettings`, `upsertCommunicationSettings`, `getAbandonedCarts`, `manuallyCloseAbandonedCart`, `resendAbandonedCartEmail`, `sendStopBlast`, `previewContactImport`, `importContactsBatch`, `exportContacts`, `getWholesaleCustomers`, `getWholesaleOrders`, `getWholesaleSettings`, `updateWholesaleSettings`, `sendPriceSheetToCustomer`, `bulkFulfillOrders`, `recordWholesaleDeposit`, `createAdminUser`, `getAdminUsers`, `getBrands`, `getPaymentSettings`, `savePaymentSettings`, `syncSquareNow`, `getSyncLog`, `getResendCredentials`, `saveResendCredentials`, `testResendConnection`, `getTwilioCredentials`, `saveTwilioCredentials`, `testTwilioConnection`, `getStripeConnectStatus`, `createStripeConnectLink`, `refreshStripeConnectLink`, `disconnectStripeConnect`, `createStripeDashboardLink`, `getWaterIrrigators`, `getWaterHeadgatesAdmin`, `getWaterEntries`, `createWaterHeadgate`, `updateWaterHeadgate`, `deleteWaterHeadgate`, `regenerateHeadgateToken`, `createWaterUser`, `resetWaterIrrigatorPin`, `deleteWaterUser`, `getWaterAdminSettings`, `saveWaterAdminSettings`, `regenerateAdminPin`, `getTimeTrackingWorkers`, `getTimeTrackingTasks`, `getTimeTrackingSummary`, `getWorkerTimeLogs`, `createTimeWorker`, `updateTimeWorker`, `deleteTimeWorker`, `resetTimeWorkerPin`, `createTimeTask`, `updateTimeTask`, `deleteTimeTask`, `getRouteTraceStats`, `getRouteTraceLots`, `getRouteTraceLotDetail`, `getLotOrders`, `getHarvestLotsReadyToHaul`, `getFieldYieldSummary`, `getInventoryByCrop`, `getRecentLotEvents`, `signOutAction`, `signInWithGoogleAction`, `toggleBrandFeature`. ### T2 — Server-action categories - **CRUD**: orders, stops, products, customers, campaigns, templates, contacts, wholesale_*, time_tracking_*, water_log_*, harvest_lots. - **Bulk**: `bulkFulfillOrders`, `sendPriceSheetToCustomer` (wholesale); `markPickupComplete` loop (orders). - **Auth**: sign-in, sign-out, password reset/change, OAuth. - **Integration side-effects**: `syncSquareNow`, `sendCampaign`, `sendStopBlast`, `savePaymentSettings` → Stripe OAuth URL generation. - **Reads**: `get*` actions returning typed rows for page/server-component hydration. ### T3 — Server-component vs client-component split - Every route's `page.tsx` is `"use server"` and resolves `getAdminUser()` + brand + initial datasets in parallel. - Heavy interactive panels (orders, stops, products, communications, settings, water-log) are `"use client"` components mounted by their page. - v2 dashboard surfaces (`/admin/v2/*`) lean more on server components with `pool.query` directly. ### T4 — Auth gating patterns - Page-level: `if (!adminUser) redirect("/login")` or ``. - Permission check: `if (!adminUser.can_manage_X) redirect("/admin/pickup")`. - Brand-scope: `effectiveBrandId = brandId ?? adminUser.brand_id ?? firstBrandId`. - Direct DB queries in pages use `getActiveBrandId()` helper; admin queries use `pool.query` with explicit `WHERE brand_id = $1` or `1=1` for platform_admin. ### T5 — Modal inventory | Modal | Triggered from | Server action | |---|---|---| | `GlassModal` shell | many | n/a (UI only) | | `ElegantModal` shell | some | n/a | | `AdminOrdersPanel` New Order modal | inline fixed overlay | `createAdminOrder` | | `AddStopModal` | `AdminStopsPanel` Add | `createStop` | | `EditStopModal` | inline | `createStop` / `updateStop` | | `NewStopForm` (inline, not modal) | `/admin/stops/new` | `createStop` | | `AddLocationModal` / `EditLocationModal` | `LocationsTab` | `createLocation` / `updateLocation` | | `CreateUserModal` | settings Users | `createAdminUser` | | `CampaignListPanel` New Campaign | `/admin/communications` | `upsertCampaign` | | `AbandonedCartDashboard` detail | abandoned carts page | read-only | | `BrandFeatureCards` Enable confirm | settings Add-ons | `toggleBrandFeature` | | `HeadgatesManager` Edit/QR | headgates page | `updateWaterHeadgate`, `regenerateHeadgateToken` | | Cart stop picker | `/cart` | n/a | | `UpgradePlanModal` (lazy) | `DashboardClient` / `DashboardUpgradeButton` | n/a | | `CommandPalette` | global Cmd+K | n/a | ### T6 — Edge cases inventory (extracted for Phase 3) - **Auth/role**: dev_session values, Neon Auth failures, must_change_password redirect, role-mismatched user without brand links. - **Empty/zero states**: 0 brands, 0 stops, 0 orders, 0 contacts, 0 campaigns, 0 templates, 0 wholesale customers. - **Long data**: very long product names, unicode (αβγ δεζ 🌿🍊 谢谢), special chars (`