Skip to content

Streaks

Status

UI live (mock data). wp_fruitplug_streaks exists; the PWA streaks dashboard is shipped at /account/streaks and renders a mock 4-week streak until GET /fruitplug/v1/streaks/me is wired up.

Fruit Plug rewards customers for ordering once a week. One completed order inside the current ISO week = +1 to current_length. Skip a week, current_length resets to 0; best_length never decreases.

Tier ladder

Tier Weeks in a row PWA reward (planned)
Bronze 1–3 Free delivery on first sub box
Silver 4–7 5% discount code emailed
Gold 12–25 Free Japanese Selection upsell on the next box
Platinum 26+ Monthly premium fruit drop + early access to limited boxes

Thresholds live in apps/web/lib/streaks.ts as STREAK_TIERS so the UI, docs, and eventual WP response all agree on the numbers.

Data flow

flowchart LR
  A[daily cron<br/>fruitplug_daily_streaks] --> B{user has<br/>completed order<br/>this ISO week?}
  B -- yes --> C[current_length++<br/>best_length = max]
  B -- no  --> D[current_length = 0]
  C --> E[(wp_fruitplug_streaks)]
  D --> E
  E --> F[GET /fruitplug/v1/streaks/me]
  F --> G[apps/web/lib/streaks.ts<br/>getStreakState]
  G --> H[/account/streaks/]

PWA surfaces

  • /account/streaks — hero with current length + best-ever, 12-week calendar grid (Check dot = ordered, Circle dot = missed), tier-ladder cards, streak-rewards list.
  • /account hero — will surface tier badge + "X-week streak" copy once the live endpoint is wired.
  • Header drawer — planned: small amber "streak" badge next to the user's name.

Planned WP endpoint

GET /wp-json/fruitplug/v1/streaks/me (requires login)

{
  "user_id": 42,
  "current_length": 4,
  "best_length": 7,
  "tier": "silver",
  "last_week_iso": "2026-W16",
  "recent_weeks": [
    { "iso": "2026-W05", "ordered": true },
    { "iso": "2026-W06", "ordered": false }
  ],
  "rewards": [
    { "threshold": 1, "claimed_at": "2026-03-28T00:00:00Z" },
    { "threshold": 4, "claimed_at": "2026-04-18T00:00:00Z" },
    { "threshold": 12, "claimed_at": null },
    { "threshold": 26, "claimed_at": null }
  ]
}

The recent_weeks array is ordered oldest → newest so the PWA can render the grid left-to-right without a sort.