diff --git a/ROADMAP.md b/ROADMAP.md index a315e29..5f36304 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -60,15 +60,28 @@ ships with the next promotion — the items below are what's left after that. (portwing's landed), so a Playwright canary's silence proves nothing either way. Verify with a real browser or by reading the deployed chunk for the property names. -- [ ] Acquisition data: decided org-wide in `CodesWhat/ops` +- [x] Acquisition data: decided org-wide in `CodesWhat/ops` `standards/analytics.md` ("Acquisition data and consent", as of - `c161ebc`) — that file is the authority. Shape: `save_campaign_params: - true` with `gclid`/`fbclid`/`msclkid` excluded; `save_referrer: true` - with the sanitizer forwarding `$referring_domain` only (drop it unless - it's a bare hostname, never copy `$referrer`); geo stays off (cookieless - strips the IP upstream, PostHog #48660). No banner needed. Implement - AFTER the `$pathname`/`$pageleave` promotion is verified in production, - so the two changes are separately attributable. + `c161ebc`) — that file is the authority. Landed: `save_campaign_params: + true` with `mask_personal_data_properties: true` (masks + `gclid`/`fbclid`/`msclkid` and the rest of the SDK's per-click ids); + `save_referrer: true` with the sanitizer forwarding `$referring_domain` + only (bare hostname, never `$referrer`). The sanitizer forwards only + `utm_source` (github, x, bluesky, linkedin, reddit, hackernews, + newsletter, producthunt), `utm_medium` (social, email, referral, + community) and `utm_campaign` (`/^[a-z0-9-]{1,40}$/`); everything else is + dropped. The vocabulary is pending Scott's approval. Geo stays off + (cookieless strips the IP upstream, PostHog #48660). No banner needed. +- [x] `$web_vitals` carries `$pathname` from the sanitized path +- [x] `cta activated` for the project cards: `project_` ids, placement + `projects`. `cta activated` had zero rows in 120 days on codeswhat.com; + reproduced in a browser and the capture, sanitizer and send path all work, + so it was low traffic (76 pageviews) rather than a bug. Check it fires after + deploy. +- [x] `newsletter submitted` (`result`, plus `error_class` from + invalid_email / rate_limited / unavailable / network on error) +- [x] `client error` (`error_kind`, allowlisted `error_name`, sanitized path; + max 5 per page load; `capture_exceptions` stays off) ## Telemetry and badge audit (2026-08-14) diff --git a/frontend/README-VERCEL.md b/frontend/README-VERCEL.md index 17e647c..5472b18 100644 --- a/frontend/README-VERCEL.md +++ b/frontend/README-VERCEL.md @@ -9,8 +9,12 @@ a local CLI deployment is not the routine production path. After promotion, confirm the production deployment references the intended commit. Open the canonical domain and verify the theme toggle changes theme -and the client scene mounts. Build success and analytics events alone do not -establish that client initialization worked. +and persists after reload, the specimen plates render, and `/support` shows its +buy links. Build success and analytics events alone do not establish that client +initialization worked. + +`npm run og` regenerates the share cards in `public/og*` with playwright-core +and the locally installed Google Chrome; it isn't part of the build. ## Rendering and security headers @@ -55,6 +59,23 @@ unset in Preview and Development so those deployments emit no analytics: | `NEXT_PUBLIC_POSTHOG_HOST` | `https://e.codeswhat.com` | | `NEXT_PUBLIC_POSTHOG_UI_HOST` | `https://us.posthog.com` | +### Support page + +`/support` reads `CODESWHAT_SUPPORT_LINKS`, a server-side JSON object mapping tier +id to a Stripe Payment Link. The six ids are `monthly-5`, `monthly-10`, +`monthly-25`, `monthly-100`, `once-10` and `once-50`: + +``` +CODESWHAT_SUPPORT_LINKS={"monthly-5":"https://buy.stripe.com/...","monthly-10":"https://buy.stripe.com/...","monthly-25":"https://buy.stripe.com/...","monthly-100":"https://buy.stripe.com/...","once-10":"https://buy.stripe.com/...","once-50":"https://buy.stripe.com/..."} +``` + +Only `https://buy.stripe.com/...` and `https://donate.stripe.com/...` links with a +path and no credentials are accepted. Unknown ids and invalid links are dropped +with a server warning, and any tier without a link renders as "Opening soon". +The page is rendered per request, so changing the variable needs a redeploy but +no rebuild of page code. Costs marked PLACEHOLDER in `lib/support.ts` are for the +maintainer to fill in. + ## Domain Configuration 1. Go to your Vercel project settings diff --git a/frontend/app/(site)/layout.tsx b/frontend/app/(site)/layout.tsx new file mode 100644 index 0000000..75048ef --- /dev/null +++ b/frontend/app/(site)/layout.tsx @@ -0,0 +1,13 @@ +import { Footer } from "@/components/catalogue/Footer"; +import { Shell } from "@/components/catalogue/Shell"; + +// The Catalogue shell wraps every public route. The dev-only /studio capture pages sit +// outside this group so their transparent canvases are not painted over by the paper. +export default function SiteLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} +