Skip to content

Account dashboard

/account is the logged-in customer's home — designed to feel like a native commerce app rather than a WordPress dashboard. Mobile-first, every section is a self-contained card, and every primary action has a >= 44px tap target.

Layout

┌─────────────────────────────────────────────┐
│  AccountHeader                              │  ← greeting · Plug Points · stats
├──────────────────────┬──────────────────────┤
│  Reorder your last   │  Upcoming delivery   │  ← stacked on phone, side-by-side at md+
├──────────────────────┴──────────────────────┤
│  Your orders (last 10)                      │  ← richer cards w/ status pill + tracking
├─────────────────────────────────────────────┤
│  Saved boxes                                │  ← grid of custom boxes from /box/mine
├─────────────────────────────────────────────┤
│  Account links                              │  ← Subscription, Address, Preferences,
└─────────────────────────────────────────────┘     Sign out

The page itself is a server component (apps/web/app/account/page.tsx). The only client island is <ReorderButton />, which holds a per-component pending flag while it walks the cart store.

Sections

Account header

components/account/AccountHeader.tsx — server component. Renders the greeting, a Plug Points badge, and three quick stats (orders, points, status line with member-since).

Plug Points balance and the member-since date are placeholders — see the follow-ups below.

Reorder your last (one-tap)

components/account/ReorderButton.tsx — client component. Walks the most recent order's line items and calls useCart.add({ id, quantity: 1 }) for each, sequentially. Sequential is deliberate: Woo's Store API rotates the Cart-Token on every mutation, and racing add-to-cart calls can drop items. Once everything is queued the mini-cart pops open via useMobileNav.openMiniCart().

Per-item failures (e.g. a discontinued product) are swallowed so a partial reorder still works.

Upcoming delivery (placeholder)

components/account/UpcomingDelivery.tsx — pure render. Always shows the "no subscription yet" placeholder branch today. The real-data branch is implemented and waiting for WP Subscriptions / Stripe Billing data to start flowing into the page (Phase 2).

Your orders

components/account/OrderCard.tsx — server component. For each line item:

  1. Slugify the product name and look it up in lib/fruit-taxonomy.ts.
  2. If the slug is a known fruit, prefer the unified local PNG via localFruitImage(slug) (under /media/fruits/{slug}.png).
  3. Otherwise fall back to the Woo image URL that came back on the line item.

Tracking link discovery (pickTrackingUrl) tries the well-known meta_data keys used by the popular Woo tracking plugins: tracking_url, _tracking_url, aftership_tracking_url, and the array shape _wc_shipment_tracking_items[0].tracking_link. Only https:// URLs are accepted. The renderer hides the button entirely when none are present.

Saved boxes

components/account/SavedBoxesList.tsx — server component. Reads from a new helper, lib/saved-boxes.ts → getMySavedBoxes(), which forwards the JWT from the fp_auth cookie to GET /fruitplug/v1/box/mine. Returns [] on any failure (network, 401, malformed body) so the empty state is always safe to render.

components/account/AccountLinks.tsx — list of secondary destinations (Subscription, Address, Preferences, Sign out). Sign out reuses the existing <LogoutButton /> so we don't duplicate the auth flow.

Data sources

Section Endpoint Auth Notes
Header (profile) GET /fruitplug/v1/auth/me JWT Plugin's profile read
Orders GET /wc/v3/orders?customer={id} Admin (consumer key/secret) Server-side only
Saved boxes GET /fruitplug/v1/box/mine JWT New getMySavedBoxes() helper
Subscription (not yet wired) (future) Placeholder card

Known placeholders

These are intentional and should be picked up in follow-up slices once the underlying data is available from the WP plugin:

  • Plug Points balance — the loyalty schema isn't wired into /auth/me yet. The header reads MePayload.points_balance and renders / "coming soon" when undefined.
  • Member since — the plugin's /auth/me doesn't always return registered / created_at. Without it the status line falls back to "member of Fruit Plug".
  • Upcoming delivery — requires Phase 2 subscriptions infrastructure. The component contract (UpcomingSubscription) is in place; pass it a non-null subscription prop to activate the live branch.

Indexability

The page sets robots: { index: false, follow: false } and is excluded from the sitemap. Account routes shouldn't be discoverable from search.