feat: PostHog analytics + waitlist with Resend#100
Merged
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, types Fixes 8 issues from spec review: - Migration 00009 → 00031 (correct sequence) - Server action moved to src/app/(auth)/signup/actions.ts (colocated) - CSP update for PostHog host documented - Beta gate clarified: allowed emails can still reach signup form - WaitlistForm colocated with signup page - shouldSendEmail() exclusion documented - Server-side PostHog client (analytics-server.ts) specified - DbWaitlist type addition to database.ts documented Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewed and fixed: reordered analytics before PostHog provider, CSP reads env var instead of hardcoding, removed dead code, added test env cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also patches vitest setup and config to handle Node 25's broken built-in localStorage (requires --localstorage-file) with an in-memory fallback, and sets jsdom URL to http://localhost for correct environment init. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… audienceId Resend migrated from audiences to segments. Use contacts.create with segments array instead of the deprecated audienceId parameter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split signup page into SignupPage (coordinator) + SignupForm (form UI) to stay under the 150-line function limit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…llision Two files shared the 00027 prefix causing a unique constraint violation on schema_migrations_pkey during supabase db reset in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI requires numeric-only prefixes — the 00027b rename was skipped with "file name must match pattern". Merged the unique active checkin index into 00027_rate_limits.sql and removed the duplicate file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase takes ~5.5min to start all containers in CI. The previous timeout of 90×3s=270s was insufficient now that migrations run successfully (the duplicate 00027 collision was masking this). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLI 2.67.1 sends migrations as a batch transaction, causing "relation does not exist" when a later migration references a table created by an earlier one in the same batch. 2.78.1 applies migrations sequentially. Cache key updated to pull fresh Docker images. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI applies migrations with search_path that may not include "public". Unqualified table names like "notes" fail with "relation does not exist". Fixed all trigger DDL in 00028 to use public.* prefix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
supabase start batches all migrations into a single transaction, causing "relation does not exist" errors when later migrations reference tables from earlier ones. Use --ignore-health-check for start, then db reset to apply migrations one-by-one in separate transactions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
supabase start batches migrations in a single transaction. If that fails (cross-migration table references), fall back to db reset which applies them sequentially. Removed --ignore-health-check to keep services running properly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI sends all migrations as a single batch transaction. Cross-file DDL dependencies (e.g., CREATE TABLE in 00004 + CREATE TRIGGER ON that table in 00028) fail because Postgres can't resolve forward references within a batch. Squashing into one file eliminates inter-file dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI parses all migration statements before executing any. Cross-file DDL (CREATE TRIGGER/POLICY/INDEX on tables from earlier migrations) fails at parse time with "relation does not exist". Fixed 14 migration files by wrapping cross-file DDL in DO blocks with EXECUTE to defer table name resolution to execution time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI sends the seed file as a batch and pre-parses all statements. The explicit BEGIN/COMMIT wrapper may conflict with the batch transaction model and cause table resolution failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI batch-parses the seed.sql and fails resolving table names before execution. Disable seed in config.toml and apply it directly via psql after supabase start, where statements execute sequentially. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supabase CLI sends all migration files as a single batch, pre-parsing all SQL before executing any. This fails when migrations reference tables created in earlier files (e.g., CREATE TRIGGER ON a table from another migration). Fix: disable seed in config.toml, restore original migration files, and apply both migrations and seed sequentially via psql in CI where each file executes independently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
supabase start successfully applies migrations (they work in batch mode since tables + policies are in the same file). Only the seed needs psql because it references tables from migration files and fails in batch parse. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion The enforce_couple_resource_limit() function uses SET search_path = '' for security but then queries TG_TABLE_NAME without schema qualification. This fails because "notes" is not found without "public." prefix when search_path is empty. Use TG_TABLE_SCHEMA.TG_TABLE_NAME instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ALLOWED_EMAILS— non-allowed emails see a waitlist form instead of signupwaitlisttable) + synced to Resend segmentNew files (13)
src/lib/cookie-consent.ts+ test — consent state with localStorage + CustomEventsrc/lib/analytics.ts+ test — 8 client-side tracking functionssrc/lib/analytics-server.ts+ test — server-side PostHog via posthog-nodesrc/components/PostHogProvider.tsx— consent-based PostHog initsrc/components/PostHogWrapper.tsx— SSR-disabled dynamic wrappersrc/components/ui/CookieConsent.tsx— cookie consent bannersrc/app/(auth)/signup/actions.ts—joinWaitlistserver actionsrc/app/(auth)/signup/WaitlistForm.tsx— waitlist form componentsrc/lib/email/templates/waitlist-confirmation.tsx— email templatesupabase/migrations/00031_add_waitlist.sql— waitlist table + RLSModified files (7)
src/app/providers.tsx— PostHogWrapper + CookieConsentsrc/app/(auth)/signup/page.tsx— beta gate logicsrc/app/landing-page.tsx— landing analyticssrc/components/Landing/Hero.tsx— CTA click trackingsrc/lib/supabase/middleware-utils.ts— CSP for PostHogsrc/lib/email/send.ts—sendWaitlistConfirmation()src/types/database.ts—DbWaitlistinterfaceNew env vars (set in Vercel)
NEXT_PUBLIC_POSTHOG_KEYNEXT_PUBLIC_POSTHOG_HOSTRESEND_WAITLIST_AUDIENCE_IDTest plan
🤖 Generated with Claude Code