Changelog¶
Chronological log of what's shipped. Most recent first.
2026-04-24 (evening) — SEO baseline + JWT plumbing¶
SEO (PWA)¶
- 🎉
SEO_SITE_URL+SEO_INDEXABLEenv switches. Dev defaults to noindex/Disallow; production flips one env var to go live. Canonicals andmetadataBasealways point atfruitplug.co.ukregardless of where the bytes are served from. - 🎉
app/robots.ts— dynamic robots gated onSEO_INDEXABLE. When on, allows crawling and advertises the sitemap; blocks/api/,/cart,/checkout,/account. - 🎉
app/sitemap.ts— dynamic sitemap pulling products + categories from the Woo Store API, plus static pages and box-builder templates. 1h revalidate. Empty whenSEO_INDEXABLEis off. - 🎉 JSON-LD structured data —
Organization+WebSiteon home;Product(withOffer+AggregateRating) +BreadcrumbListon PDPs;BreadcrumbList+CollectionPageon category pages. Builders inlib/seo/structured-data.ts, rendered via<JsonLd />component (HTML-escaped). - 🎉 Canonicals + OG on every SSR page. PDPs carry their primary product image as
og:image.
Auth foundation (plugin + PWA, not yet deployed)¶
- 🎉 HS256 JWT helper in
fruitplug-api(Fruitplug\Auth\Jwt) — no third-party deps. Signing key derived fromwp_salt('auth')so regenerating salts invalidates every issued token. - 🎉
POST /fruitplug/v1/auth/token— username/email + password → JWT. 14-day TTL. Returns safe user payload (id, username, email, display_name, roles). - 🎉
GET /fruitplug/v1/auth/me—Authorization: Bearer <jwt>→ user identity. - 🎉 Bearer-auth filter — hooks
determine_current_userso any REST request carrying a valid Bearer auto-becomes that user beforepermission_callbackruns. Every existingis_user_logged_in()gate just works, no endpoint changes needed. - 🎉 PWA auth proxy —
/api/auth/loginstores the JWT in anhttpOnlycookie (fp_auth), so the browser never touches the token directly./api/auth/logoutclears it./api/auth/meproxies through. - 🎉
lib/fruitplug-api.tsupdated to forwardAuthorization: Bearerfrom the cookie whenforwardAuth: true. - 🎉
lib/woo-admin.tsserver helper for admin-authenticated Woo REST v3 reads (usesWC_CONSUMER_KEY/WC_CONSUMER_SECRET). Used for fetching order history by customer ID.
Deferred (this session)¶
/login+/accountUI pages — paused pending UX/design direction.- Yoast metadata passthrough — depends on whether Yoast WooCommerce SEO is active on A2.
- Plugin deploy — SEO changes are PWA-only; plugin JWT additions stay on disk until explicit go-ahead.
2026-04-24 (later) — Phase 1 Week 5 server wiring¶
Backend (fruitplug-api)¶
- 🎉 Authoritative box templates on the server. New
Fruitplug\BoxTemplatesclass mirrorsapps/web/lib/box-templates.ts. Any endpoint that prices or validates a box now routes throughBoxTemplates::validate_composition(), so the server, cart hook, and PWA agree on what's legal and what it costs. - 🎉
GET /fruitplug/v1/box/templates— public endpoint returning the full template list. Will become the single source of truth once the TS side switches to fetching it. - 🎉
POST /fruitplug/v1/box/pricetightened. Body is now{ template_slug, items }(was justitems). Server validates eligibility, section caps, credit budget, and returns the canonical anchor price. Illegal compositions return HTTP 422 with a descriptive code. - 🎉
POST /fruitplug/v1/box/savevalidated. Saved-box payload stores{ template_slug, price_gbp, lines }so future loads are replayable even if templates change. - 🎉
CustomBoxPricingbug fixed. The cart hook previously summed fruit component prices and set the line price to that sum — contradicting the anchor-price model. It now pins the line to the template's anchor price, so a tampered client can't under-pay. - 🎉 Fruit Costs admin page.
wp-admin → WooCommerce → Fruit Costslists every published product with an inline editable_fruitplug_cost_gbpmeta. Margin is computed and shown per-row. Replaces the third-party Cost of Goods plugin removed in Phase 0.
Frontend (Next.js PWA)¶
- 🎉
lib/fruitplug-api.ts— server-side proxy helper for the plugin (forwards WP login cookies to endpoints that need them). - 🎉
/api/box/price+/api/box/saveroutes — Next.js proxies so the browser never talks to WP directly. - 🎉 Box builder live validation. Every composition change debounces a
/api/box/pricecall; the server-returned price drives the CTA, and validation errors are surfaced inline. - 🎉 "Save this box" wired to
/api/box/save. Login-gated: shows "Sign in to save boxes — coming soon" on 401 (expected until JWT auth ships in Week 4), otherwise reportsSaved · /b/{slug}.
Deferred¶
- A2 plugin deploy — held back pending explicit auth (no production push without a green-light).
- Cart-item composition metadata via Woo Store API extension — simpler path is deferred until Stripe checkout lands in Week 3.5.
2026-04-24 — Phase 0 complete · Phase 1 ~60%¶
Backend (A2 Hosting)¶
- 🎉 PHP 7.4.33 → 8.2.30. Enabled
mysqli,sodium,imagickin cPanel → Select PHP Version → Extensions. - 🎉 Plugin cleanup shipped. Removed 18 frontend-only plugins in 5 verified batches; 38 → 21 active.
- 🎉 Revisions cleaned. 578
wp_postsrevision rows deleted. - 🎉 Categories rebalanced. Uncategorised: 6 → 0. New
Rare & Specialcategory (5 products). Signature Box duplicate removed from Fruits. - 🎉 fruitplug-api installed + activated. 7
wp_fruitplug_*tables created, seasonal calendar seeded. - 🎉 Full DB + plugin + theme backup in
backups/a2-20260424-205535/locally. Uploads tarball kept server-side. - ⏳ HPOS still needs a one-click toggle in wp-admin (deferred — Woo sync-first migration).
Frontend (Next.js PWA at dev.fruitplug.co.uk)¶
- 🎉 Home — hero, wordmark, tagline, social proof, feature cards.
- 🎉 Shop — all 46 products rendering from Woo Store API.
- 🎉 Category pages — Fruits, Boxes, Subscription, Merchandise, Rare & Special.
- 🎉 Product detail pages — variation selector, rating, related, Add to Cart.
- 🎉 Cart — full Woo cart with Cart-Token + Nonce session, quantity updates, remove, totals.
- 🎉 Custom Box Builder —
/build-your-box+/build-your-box/[slug]. 3-tier section model (Premium 20cr / Tropical 12cr / Everyday 8cr), sticky credit budget bar, section min/max enforcement, adds to Woo cart. - 🎉 PWA install banner — device-aware (iOS Share-sheet instructions vs Android/Chrome native), 14-day cool-off on dismiss.
- 🎉 Animated splash screen — dragon-fruit mascot with magenta glow + bounce-in, session-scoped,
?splash=1override for preview. - 🎉 PWA icons regenerated — full wordmark on magenta for launchers, glyph-only for 32px favicon, maskable variants with 20% safe zone.
- 🎉 Clean mascot PNG — flood-fill chroma-key on white background produces a truly transparent glyph. Saved as
public/brand/mascot.png.
Brand & typography¶
- 🎉 Display font: Caveat Brush (bolder, marker-pen feel). Closest free match to the Butcher theme's Hugtophia and the user-nominated "Grit".
- 🎉 Grit hot-swap — layout auto-detects
public/fonts/grit.woff2and uses it over Caveat Brush when present. - 🎉 Brand tokens —
--fp-magenta #be006a,--fp-black,--fp-pith,--fp-leaf,--fp-stickerexposed as CSS vars AND Tailwind utilities. - 🎉 Lucide outline icons throughout — 1.75 stroke weight, no emoji in UI.
Infrastructure¶
- 🎉 Caddy site blocks for
dev.fruitplug.co.ukanddocs.fruitplug.co.uk. Auto Let's Encrypt certs. - 🎉 Docker dev container with webpack polling for reliable HMR through WSL2 bind mounts.
- 🎉 MkDocs Material docs site with hot reload and brand theming.
- 🎉 A2 SSH tooling at
infra/a2/— inventory, backup, plugin cleanup, plugin rsync. - 🎉 Paramiko-based automation — encrypted-key support, SFTP, wp-cli pass-through.
Deferred / planned¶
See the Roadmap — it supersedes this list going forward.
2026-04-23 — Scaffold + plan¶
- 📝 Strategic plan written at
.claude/plans/hazy-soaring-hedgehog.mdand user-approved. - 🎉 Next.js 16.2.4 + React 19.2.4 monorepo scaffolded with pnpm workspaces.
- 🎉 Multi-stage Dockerfile +
output: 'standalone'for slim production images. - 🎉 CI workflow (GitHub Actions, not yet pushed) — build → GHCR → SSH deploy → smoke test.
- 🎉 WP custom plugin skeleton at
wp-plugin/fruitplug-api/with 11 PHP files covering 6 REST controllers + DB migrations + cron skeletons. - 🎉 Site audit baseline — 13 screenshots, 46 products exported, 38 plugins audited, 151M IG plays cataloged.
- 🎉 Instagram asset library — 349 videos (2 GB), 53 images, thumbnails, manifest with captions + engagement.