feat: add waitlist signup count
This commit is contained in:
@ -1,11 +1,14 @@
|
|||||||
import { Hero } from "@/components/landing/hero";
|
import { Hero } from "@/components/landing/hero";
|
||||||
import { Header } from "@/components/header";
|
import { Header } from "@/components/header";
|
||||||
|
import { getWaitlistCount } from "@/lib/waitlist";
|
||||||
|
|
||||||
|
export default async function Home() {
|
||||||
|
const signupCount = await getWaitlistCount();
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header />
|
<Header />
|
||||||
<Hero />
|
<Hero signupCount={signupCount} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,25 @@ import { Input } from "../ui/input";
|
|||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
|
||||||
export function Hero() {
|
interface HeroProps {
|
||||||
|
signupCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Hero({ signupCount }: HeroProps) {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!email.trim()) {
|
if (!email.trim()) {
|
||||||
toast.error("Email required", {
|
toast({
|
||||||
|
title: "Email required",
|
||||||
description: "Please enter your email address.",
|
description: "Please enter your email address.",
|
||||||
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -36,16 +43,23 @@ export function Hero() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success("Welcome to the waitlist! 🎉");
|
toast({
|
||||||
|
title: "Welcome to the waitlist! 🎉",
|
||||||
|
description: "You'll be notified when we launch.",
|
||||||
|
});
|
||||||
setEmail("");
|
setEmail("");
|
||||||
} else {
|
} else {
|
||||||
toast.error("Oops!", {
|
toast({
|
||||||
|
title: "Oops!",
|
||||||
description: data.error || "Something went wrong. Please try again.",
|
description: data.error || "Something went wrong. Please try again.",
|
||||||
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Network error", {
|
toast({
|
||||||
|
title: "Network error",
|
||||||
description: "Please check your connection and try again.",
|
description: "Please check your connection and try again.",
|
||||||
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
@ -113,6 +127,18 @@ export function Hero() {
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{signupCount > 0 && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.8, duration: 0.6 }}
|
||||||
|
className="mt-6 inline-flex items-center gap-2 bg-muted/30 px-4 py-2 rounded-full text-sm text-muted-foreground"
|
||||||
|
>
|
||||||
|
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
||||||
|
<span>{signupCount.toLocaleString()} people already joined</span>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
|
15
apps/web/src/lib/waitlist.ts
Normal file
15
apps/web/src/lib/waitlist.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { db } from "@/lib/db";
|
||||||
|
import { waitlist } from "@/lib/db/schema";
|
||||||
|
import { sql } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function getWaitlistCount() {
|
||||||
|
try {
|
||||||
|
const result = await db
|
||||||
|
.select({ count: sql<number>`count(*)` })
|
||||||
|
.from(waitlist);
|
||||||
|
return result[0]?.count || 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch waitlist count:", error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user