From 99804656d7699f99bf4f2d1d2d052c318f6733f6 Mon Sep 17 00:00:00 2001 From: colinislit Date: Sat, 15 Nov 2025 23:59:38 +0100 Subject: [PATCH] RLS policies implementeren, Demo auth flow --- .../components/comparison-table.tsx | 104 +++++ app/(marketing)/components/experiment-cta.tsx | 56 +++ app/(marketing)/components/hero-quote.tsx | 59 +++ app/(marketing)/components/insight-box.tsx | 32 ++ .../components/manifesto-content.tsx | 95 ++++ .../components/marketing-shader.tsx | 83 ++++ app/(marketing)/components/minimal-nav.tsx | 147 +++++++ .../components/reading-progress.tsx | 69 +++ .../components/statement-section.tsx | 51 +++ .../components/structured-data.tsx | 52 +++ app/(marketing)/contact/contact-form.tsx | 326 ++++++++++++++ app/(marketing)/contact/page.tsx | 146 ++++++ app/(marketing)/epd/credentials-box.tsx | 88 ++++ app/(marketing)/epd/page.tsx | 243 ++++++++++ app/(marketing)/layout.tsx | 31 +- app/(marketing)/page.tsx | 119 ++++- app/api/leads/route.ts | 116 +++++ app/auth/callback/route.ts | 56 +++ app/auth/logout/route.ts | 31 ++ app/globals.css | 35 +- app/layout.tsx | 36 +- app/login/page.tsx | 276 ++++++++++++ app/robots.ts | 36 ++ app/sitemap.ts | 38 ++ content/nl/contact.json | 123 ++++++ content/nl/epd.json | 80 ++++ content/nl/navigation.json | 8 +- content/schemas/manifesto.ts | 139 ++++++ docs/AUTH_SETUP.md | 416 ++++++++++++++++++ docs/RLS_SECURITY.md | 304 +++++++++++++ docs/manifesto.md | 13 + ...uwplan-ai-speedrun-marketing-first-v1.1.md | 182 ++++++-- docs/specs/design-specs-manifesto-website.md | 147 +++++-- lib/auth/client.ts | 162 +++++++ lib/auth/server.ts | 154 +++++++ lib/content/loader.ts | 27 ++ lib/content/markdown-parser.ts | 62 +++ lib/database.types.ts | 365 +++++++++++++++ middleware.ts | 97 ++++ next.config.ts | 48 +- package.json | 7 +- pnpm-lock.yaml | 332 +++++++++++++- scripts/check-demo-users.ts | 63 +++ scripts/run-migration.ts | 82 ++++ scripts/seed-demo-users.ts | 158 +++++++ .../20241115000001_create_leads_table.sql | 69 +++ .../20241115000002_create_epd_core_tables.sql | 230 ++++++++++ .../20241115000003_test_rls_policies.sql | 180 ++++++++ .../20241115000004_create_demo_users.sql | 191 ++++++++ 49 files changed, 5841 insertions(+), 123 deletions(-) create mode 100644 app/(marketing)/components/comparison-table.tsx create mode 100644 app/(marketing)/components/experiment-cta.tsx create mode 100644 app/(marketing)/components/hero-quote.tsx create mode 100644 app/(marketing)/components/insight-box.tsx create mode 100644 app/(marketing)/components/manifesto-content.tsx create mode 100644 app/(marketing)/components/marketing-shader.tsx create mode 100644 app/(marketing)/components/minimal-nav.tsx create mode 100644 app/(marketing)/components/reading-progress.tsx create mode 100644 app/(marketing)/components/statement-section.tsx create mode 100644 app/(marketing)/components/structured-data.tsx create mode 100644 app/(marketing)/contact/contact-form.tsx create mode 100644 app/(marketing)/contact/page.tsx create mode 100644 app/(marketing)/epd/credentials-box.tsx create mode 100644 app/(marketing)/epd/page.tsx create mode 100644 app/api/leads/route.ts create mode 100644 app/auth/callback/route.ts create mode 100644 app/auth/logout/route.ts create mode 100644 app/login/page.tsx create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 content/nl/contact.json create mode 100644 content/nl/epd.json create mode 100644 docs/AUTH_SETUP.md create mode 100644 docs/RLS_SECURITY.md create mode 100644 lib/auth/client.ts create mode 100644 lib/auth/server.ts create mode 100644 lib/content/markdown-parser.ts create mode 100644 lib/database.types.ts create mode 100644 middleware.ts create mode 100644 scripts/check-demo-users.ts create mode 100644 scripts/run-migration.ts create mode 100644 scripts/seed-demo-users.ts create mode 100644 supabase/migrations/20241115000001_create_leads_table.sql create mode 100644 supabase/migrations/20241115000002_create_epd_core_tables.sql create mode 100644 supabase/migrations/20241115000003_test_rls_policies.sql create mode 100644 supabase/migrations/20241115000004_create_demo_users.sql diff --git a/app/(marketing)/components/comparison-table.tsx b/app/(marketing)/components/comparison-table.tsx new file mode 100644 index 0000000..3ca2278 --- /dev/null +++ b/app/(marketing)/components/comparison-table.tsx @@ -0,0 +1,104 @@ +/** + * Comparison Table Component + * + * Visual comparison between Traditional and AI Speedrun approaches. + * Responsive: table on desktop, stacked cards on mobile. + */ + +import type { ComparisonContent } from '@/content/schemas/manifesto' + +interface ComparisonTableProps { + content: ComparisonContent +} + +export function ComparisonTable({ content }: ComparisonTableProps) { + return ( +
+

+ {content.heading} +

+ + {/* Desktop: Table view */} +
+ + + + + + + + + + {content.items.map((item, index) => ( + + + + + + ))} + +
+ Aspect + + Traditioneel + + AI Speedrun +
+ {item.label} + + {item.traditional} + + {item.aiSpeedrun} +
+
+ + {/* Mobile: Stacked cards */} +
+ {content.items.map((item) => ( +
+

+ {item.label} +

+
+
+
Traditioneel:
+
+ {item.traditional} +
+
+
+
+ AI Speedrun: +
+
+ {item.aiSpeedrun} +
+
+
+
+ ))} +
+
+ ) +} + diff --git a/app/(marketing)/components/experiment-cta.tsx b/app/(marketing)/components/experiment-cta.tsx new file mode 100644 index 0000000..e86ffd6 --- /dev/null +++ b/app/(marketing)/components/experiment-cta.tsx @@ -0,0 +1,56 @@ +/** + * Experiment CTA Component + * + * Call-to-action section at the end of the manifesto. + * Subtle buttons, no aggressive colors or fake urgency. + * Follows design specs: large typography, centered, minimal styling. + */ + +import Link from 'next/link' +import type { CTAContent } from '@/content/schemas/manifesto' + +interface ExperimentCTAProps { + content: CTAContent +} + +export function ExperimentCTA({ content }: ExperimentCTAProps) { + return ( +
+
+

+ {content.heading} +

+ + {content.subheading && ( +

+ {content.subheading} +

+ )} + +
+ {/* Primary Button - Subtle dark background */} + + {content.primaryButton.text} + + + {/* Secondary Button - Subtle outline */} + + {content.secondaryButton.text} + +
+
+
+ ) +} + diff --git a/app/(marketing)/components/hero-quote.tsx b/app/(marketing)/components/hero-quote.tsx new file mode 100644 index 0000000..45a913c --- /dev/null +++ b/app/(marketing)/components/hero-quote.tsx @@ -0,0 +1,59 @@ +/** + * Hero Quote Component + * + * Full-viewport hero section with Jensen Huang quote. + * Displays quote, attribution, and subtitle from content. + */ + +import type { HeroContent } from '@/content/schemas/manifesto' +import { MarketingShader } from './marketing-shader' + +interface HeroQuoteProps { + content: HeroContent +} + +export function HeroQuote({ content }: HeroQuoteProps) { + return ( +
+ {/* Shader Background - zeer subtiel (opacity 0.02) */} + {/* Op mobile: donkere fallback, op desktop: subtiele shader */} + + + {/* Content Overlay */} +
+
+ "{content.quote}" +
+ +
+ + {content.attribution} + + {content.attributionContext && ( + + )} +
+ +

+ {content.subtitle} +

+
+
+ ) +} + diff --git a/app/(marketing)/components/insight-box.tsx b/app/(marketing)/components/insight-box.tsx new file mode 100644 index 0000000..4a58ceb --- /dev/null +++ b/app/(marketing)/components/insight-box.tsx @@ -0,0 +1,32 @@ +/** + * Insight Box Component + * + * Visual anchor for key takeaways with yellow left border. + * Used to highlight important insights in the manifesto content. + */ + +interface InsightBoxProps { + children: React.ReactNode + variant?: 'default' | 'highlight' +} + +export function InsightBox({ children, variant = 'default' }: InsightBoxProps) { + const borderColor = variant === 'highlight' ? '#D97706' : '#F59E0B' + + return ( +
+

+ {children} +

+
+ ) +} + diff --git a/app/(marketing)/components/manifesto-content.tsx b/app/(marketing)/components/manifesto-content.tsx new file mode 100644 index 0000000..46fe9af --- /dev/null +++ b/app/(marketing)/components/manifesto-content.tsx @@ -0,0 +1,95 @@ +/** + * Manifesto Content Component + * + * Long-form reading experience with manifesto text. + * Renders paragraphs with proper typography for optimal reading. + */ + +import type { ManifestoSection } from '@/content/schemas/manifesto' +import { InsightBox } from './insight-box' +import { StatementSection } from './statement-section' + +interface ManifestoContentProps { + sections: ManifestoSection[] +} + +export function ManifestoContent({ sections }: ManifestoContentProps) { + // Group consecutive non-statement sections into article blocks + const blocks: Array<{ start: number; end: number }> = [] + let blockStart = 0 + + sections.forEach((section, index) => { + if (section.type === 'statement') { + if (blockStart < index) { + blocks.push({ start: blockStart, end: index - 1 }) + } + blockStart = index + 1 + } + }) + + // Add final block if needed + if (blockStart < sections.length) { + blocks.push({ start: blockStart, end: sections.length - 1 }) + } + + let blockIndex = 0 + + return ( + <> + {sections.map((section, index) => { + // Statement sections are full-width + if (section.type === 'statement') { + return + } + + // Check if this is the start of a new article block + const currentBlock = blocks[blockIndex] + const isBlockStart = currentBlock && index === currentBlock.start + + if (isBlockStart) { + const blockSections = sections.slice(currentBlock.start, currentBlock.end + 1) + blockIndex++ + + return ( +
+
+ {blockSections.map((blockSection) => { + if (blockSection.type === 'paragraph') { + return ( +

+ {blockSection.content} +

+ ) + } + + if (blockSection.type === 'insight') { + return ( + + {blockSection.content} + + ) + } + + return null + })} +
+
+ ) + } + + return null + })} + + ) +} + diff --git a/app/(marketing)/components/marketing-shader.tsx b/app/(marketing)/components/marketing-shader.tsx new file mode 100644 index 0000000..66c74fa --- /dev/null +++ b/app/(marketing)/components/marketing-shader.tsx @@ -0,0 +1,83 @@ +'use client' + +/** + * Marketing Shader Component + * + * Wrapper around DotScreenShader for marketing pages. + * Very subtle opacity (0.02) for hero sections. + * Includes mobile fallback and reduced motion support. + */ + +import dynamic from 'next/dynamic' +import { useEffect, useState } from 'react' + +// Lazy load the shader component (heavy, only load when needed) +const DotScreenShader = dynamic( + () => import('@/components/ui/dot-shader-background').then((mod) => ({ default: mod.DotScreenShader })), + { + ssr: false, + loading: () => null + } +) + +interface MarketingShaderProps { + variant?: 'hero' | 'section' + className?: string +} + +export function MarketingShader({ variant = 'hero', className = '' }: MarketingShaderProps) { + const [mounted, setMounted] = useState(false) + const [reducedMotion, setReducedMotion] = useState(false) + const [isMobile, setIsMobile] = useState(false) + + useEffect(() => { + setMounted(true) + + // Check for reduced motion preference + const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)') + setReducedMotion(mediaQuery.matches) + + const handleChange = (e: MediaQueryListEvent) => setReducedMotion(e.matches) + mediaQuery.addEventListener('change', handleChange) + + // Check for mobile (simple check) + const checkMobile = () => { + setIsMobile(window.innerWidth < 768) + } + checkMobile() + window.addEventListener('resize', checkMobile) + + return () => { + mediaQuery.removeEventListener('change', handleChange) + window.removeEventListener('resize', checkMobile) + } + }, []) + + // Fallback voor mobile of reduced motion + // Voor hero variant: donkere achtergrond, voor section variant: lichte achtergrond + if (!mounted || reducedMotion || isMobile) { + const fallbackBg = variant === 'hero' + ? 'bg-slate-900' // Donkere achtergrond voor hero + : 'bg-gradient-to-br from-slate-50 to-slate-100' // Lichte achtergrond voor sections + + return ( +