Files
Epicure/VITRINE_PLAN.md
T
Arnaud 634f8245e1 docs: add vitrine (marketing site) plan
Page list (home/features/pricing/changelog/about/privacy/terms/contact),
same-app route-group approach reusing design system/i18n/deploy, and
the one integration detail that actually gates visibility: proxy.ts's
PUBLIC_PATHS currently sends every anonymous visitor of "/" straight
to /login before page.tsx's redirect-to-/recipes ever runs — the
marketing site is invisible until that changes, plus a startsWith-vs-
exact-match gotcha for adding "/" itself to that list. Also flags
missing sitemap.ts/robots.ts and legal-content review as open gaps.
2026-07-18 00:52:36 +02:00

77 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vitrine (Marketing Site) Plan
## Status: planning only — nothing in this doc is implemented yet
**Current state, confirmed by reading the actual code, not assumed:**
- `apps/web/app/page.tsx``/` unconditionally `redirect("/recipes")`, no marketing content exists.
- `apps/web/proxy.ts` (the app's middleware, `export default proxy` + `config.matcher`) gates every path **not** in `PUBLIC_PATHS` behind a session cookie, redirecting straight to `/login`. `/` is not in that list — so today, an anonymous visitor hitting `/` never even reaches `page.tsx`'s redirect; they're bounced to `/login` first. **This is the one integration point that actually matters** — the vitrine is invisible to anonymous visitors until `PUBLIC_PATHS` changes.
- No `/privacy`, `/terms`, `/about`, `/contact`, `/features`, `/pricing`, or public `/changelog` route exists anywhere.
- `apps/web/lib/changelog.ts` already has a `CHANGELOG` array — currently only rendered at `/admin/changelog` (admin-only). Directly reusable for a public changelog page, no new data source needed.
- No `sitemap.ts`/`robots.ts` — Next.js supports both as file-based special routes (same convention already used for `app/manifest.ts`, seen in that file) — currently missing entirely, worth adding as basic SEO hygiene regardless of how much content the vitrine ends up with.
---
## 1. Page list
| Route | Purpose |
|---|---|
| `/` | Home — hero, value prop, 3-4 feature highlights with screenshots, primary CTA → `/signup` |
| `/features` | Deeper tour: AI recipe generation, meal planning, pantry tracking, social (follows/ratings/comments), the cooking assistant + its tool-calling (create recipe / add to shopping list from chat) |
| `/pricing` | Free/Pro/Family comparison table, CTA into Checkout once Stripe ships (`STRIPE_PLAN.md` §5/§7), or straight to `/signup` before it does |
| `/changelog` | Public version of the existing `CHANGELOG` data — social proof of active development, good for conversion and for existing users checking what's new |
| `/about` | Story/mission — why this exists, who's building it |
| `/privacy` | Privacy policy — needed once collecting emails regardless of Stripe timing (GDPR, since you're in France/EU per `STRIPE_PLAN.md` §10) |
| `/terms` | Terms of service |
| `/contact` | Support email / simple contact form |
Not in v1, deferred: a blog (`/blog`) — real SEO value but ongoing content-production cost that's a separate commitment from building the site itself; add later if organic-search growth becomes a priority.
---
## 2. Where it lives — same app, new route group
`apps/web/app/(marketing)/` — a new route group alongside the existing `(auth)`/`(app)` ones, same conventions (shared `layout.tsx` for the marketing nav/footer, page-per-route). This reuses:
- the design system (`components/ui/*`, Tailwind config, dark/light theming already wired app-wide),
- `next-intl` i18n (`lib/i18n/*`, `messages/en.json`/`fr.json`) — marketing copy gets the same `en`/`fr` treatment as the rest of the app, new keys under a `marketing` namespace,
- the existing deploy pipeline (`DEPLOY.md`, Docker/Traefik) — no second build/deploy to maintain.
**Root page change**: `apps/web/app/page.tsx` currently always redirects to `/recipes`. Change to: authenticated session → redirect to `/recipes` (unchanged behavior for existing users); no session → render the `(marketing)` home page instead of redirecting. Concretely, move the marketing home content into `app/(marketing)/page.tsx` (or a shared component) and have root `page.tsx` branch on `auth.api.getSession(...)` the same way every other server component in this app already checks session (see `app/(app)/recipes/page.tsx` for the exact pattern to mirror).
**Middleware change (the one that actually gates visibility)** — add the marketing routes to `PUBLIC_PATHS` in `apps/web/proxy.ts`:
```ts
const PUBLIC_PATHS = [
"/login", "/signup", /* ...existing... */,
"/", "/features", "/pricing", "/changelog", "/about", "/privacy", "/terms", "/contact",
];
```
Note `/` needs an **exact** match, not `startsWith` — the current check is `pathname.startsWith(p)`, which is fine for `/features` etc. (nothing else starts with those prefixes) but `"/"` as a prefix would make `PUBLIC_PATHS.some(p => pathname.startsWith(p))` true for literally every path, since every path starts with `/`. This needs a small tweak to the matching logic, not just an array addition — e.g. check `pathname === "/"` as its own condition alongside the `startsWith` list, don't add bare `"/"` into the array.
**Nav**: the existing authenticated `Nav` component (`apps/web/components/layout/nav.tsx`) doesn't apply here — the marketing route group gets its own simple header/footer (logo, nav links to the pages above, Login/Signup buttons) inside `(marketing)/layout.tsx`, not reused from the app shell.
---
## 3. SEO basics (currently entirely missing)
- `app/sitemap.ts` — list the 8 marketing routes (file-based Next.js route, same pattern as the existing `app/manifest.ts`).
- `app/robots.ts` — allow marketing routes, disallow `/api`, `/admin`, `(app)` authenticated routes, `/r/`/`/s/`/`/u/` if those shouldn't be indexed (check current intent — they're public-but-unlisted-ish today, decide explicitly rather than defaulting).
- Per-page `generateMetadata` (title/description/OpenGraph image) — every existing app page already sets `export const metadata: Metadata = {}` as a placeholder pattern; marketing pages should actually fill these in, they're the ones search engines/social shares see.
- One shared OG image (or per-page) — doesn't need to be fancy, just present; a page with no OG image looks broken when shared on socials, more damaging than a plain one.
---
## 4. Content sourcing
- **Screenshots** — real app screenshots (recipe detail page, meal planner, the cooking-assistant proposal card) sell the product better than illustrations for a tool like this. Needs light art-direction (consistent browser-frame chrome, consistent sample data — a demo/seed account with good-looking recipes, not someone's messy real account) but no dedicated design tooling.
- **Copy** — feature descriptions can mostly be extracted from what already exists in `messages/en.json` (feature names, help text) as a starting draft, then punched up for marketing tone rather than in-app instructional tone — those two registers are legitimately different and shouldn't just be copy-pasted.
- **Privacy/Terms** — templated legal content exists (e.g. via a generator, or your accountant/lawyer per `STRIPE_PLAN.md` §10 can point you to a template suited to a French micro-entreprise) — not something to draft from scratch or have generated without review, this is exactly the kind of content where a wrong clause has real consequences. Flag as **needs non-engineering review** before publishing, same spirit as the Stripe plan's tax-advice caveat.
---
## 5. Rollout order
1. `sitemap.ts`/`robots.ts` + the `PUBLIC_PATHS`/root-`page.tsx` change (§23) — plumbing first, still no real content, safe to ship and verify the routing/auth-gating logic works before writing copy.
2. `/` (home) + `/features` — the two pages that actually sell the product; ship these before pricing exists if Stripe isn't live yet (CTA → signup, not checkout).
3. `/changelog` — cheapest page to build (data already exists), good early win.
4. `/privacy` + `/terms` + `/about` + `/contact` — required-but-not-persuasive pages, batch together.
5. `/pricing` — once `STRIPE_PLAN.md` pricing (§1c) is confirmed and ideally once Checkout exists, so the CTA goes somewhere real instead of just to signup.