From 4b86aa2ac8150a142e13998eabdae4bdf3602720 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 10:15:27 +0000 Subject: [PATCH 1/2] feat: add waitlist modal with source tracking - Add WaitlistModal component with Tally.so form integration - Implement source parameter tracking for analytics (hero, nav, pricing, etc.) - Add loading spinner to prevent white flash while iframe loads - Update header, hero, pricing, and call-to-action components - Enterprise "Contact Sales" now uses mailto:david@replimap.com - Install shadcn/ui dialog component --- package-lock.json | 6 +- package.json | 2 +- src/components/call-to-action.tsx | 9 +- src/components/header.tsx | 26 +++--- src/components/hero.tsx | 9 +- src/components/pricing.tsx | 31 ++++--- src/components/ui/dialog.tsx | 143 ++++++++++++++++++++++++++++++ src/components/waitlist-modal.tsx | 57 ++++++++++++ 8 files changed, 247 insertions(+), 36 deletions(-) create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/waitlist-modal.tsx diff --git a/package-lock.json b/package-lock.json index af046a2..a0a93b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "my-v0-project", + "name": "replimap-frontend", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "my-v0-project", + "name": "replimap-frontend", "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -16,7 +16,7 @@ "@radix-ui/react-checkbox": "1.1.3", "@radix-ui/react-collapsible": "1.1.2", "@radix-ui/react-context-menu": "2.2.4", - "@radix-ui/react-dialog": "1.1.4", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "2.1.4", "@radix-ui/react-hover-card": "1.1.4", "@radix-ui/react-label": "2.1.1", diff --git a/package.json b/package.json index 8482cf8..399e8b8 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@radix-ui/react-checkbox": "1.1.3", "@radix-ui/react-collapsible": "1.1.2", "@radix-ui/react-context-menu": "2.2.4", - "@radix-ui/react-dialog": "1.1.4", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "2.1.4", "@radix-ui/react-hover-card": "1.1.4", "@radix-ui/react-label": "2.1.1", diff --git a/src/components/call-to-action.tsx b/src/components/call-to-action.tsx index a637926..33d6b61 100644 --- a/src/components/call-to-action.tsx +++ b/src/components/call-to-action.tsx @@ -3,6 +3,7 @@ import Link from "next/link" import { Button } from "@/components/ui/button" import { ArrowRight, BookOpen, Copy, Check } from "lucide-react" +import { WaitlistModal } from "@/components/waitlist-modal" import { useState } from "react" export function CallToAction() { @@ -25,12 +26,12 @@ export function CallToAction() {

- + + + {/* Primary CTA */} + + + {/* Mobile Menu */} @@ -96,13 +99,14 @@ export function Header() { View on GitHub - + + +
diff --git a/src/components/hero.tsx b/src/components/hero.tsx index 9c04f84..30177e9 100644 --- a/src/components/hero.tsx +++ b/src/components/hero.tsx @@ -5,6 +5,7 @@ import Link from "next/link" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { ArrowRight, Github, Terminal } from "lucide-react" +import { WaitlistModal } from "@/components/waitlist-modal" export function Hero() { // Typewriter animation state @@ -91,12 +92,12 @@ export function Hero() { {/* CTA Buttons */}
- + + + + + {/* Features List */}
-
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..a6f1cfb --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,143 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/src/components/waitlist-modal.tsx b/src/components/waitlist-modal.tsx new file mode 100644 index 0000000..4839a22 --- /dev/null +++ b/src/components/waitlist-modal.tsx @@ -0,0 +1,57 @@ +"use client" + +import { useState } from "react" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" + +const BASE_TALLY_URL = "https://tally.so/r/2EaYae?transparentBackground=1" + +interface WaitlistModalProps { + children: React.ReactNode + source?: string // Track where the user clicked (for analytics) +} + +export function WaitlistModal({ children, source = "generic" }: WaitlistModalProps) { + const [open, setOpen] = useState(false) + const [isLoading, setIsLoading] = useState(true) + + // Dynamic URL with source tracking + const formUrl = `${BASE_TALLY_URL}&source=${encodeURIComponent(source)}` + + return ( + + {children} + + + Join the Waitlist + + {/* Dark background prevents white flash while iframe loads */} +
+ {/* Loading spinner */} + {isLoading && ( +
+
+
+ )} +