feat: complete launch & marketing layer

- Add marketing pages: blog, changelog, roadmap, waitlist, security, maintenance
- Add GDPR cookie consent banner with preference modal
- Add referral system with API routes for code generation/tracking
- Add waitlist API with referral support
- Add PWA support: manifest, service worker, install prompt
- Add onboarding flow with 6-step guided tour
- Add in-app notification center with bell dropdown
- Add admin launch checklist (32 items across 8 categories)
- Update landing page with trust badges
- Add Open Graph image and favicon
- Configure ESLint for PWA install patterns
- Add LAUNCH_CHECKLIST.md with go-to-market guide

Supabase migrations for:
- blog_posts and blog_categories tables
- waitlist_signups table
- roadmap_items table
- launch_checklist_items table
This commit is contained in:
2026-06-02 06:19:56 +00:00
parent f6d2dc4e6c
commit 7dfaba6e3d
46 changed files with 4065 additions and 2188 deletions
+4 -150
View File
@@ -1,156 +1,10 @@
// Clerk authentication components for the UI
// Use Show, UserButton, SignInButton, SignUpButton from @clerk/nextjs
// Auth Components for Clerk
import { UserButton } from "@clerk/nextjs";
"use client";
import {
SignInButton,
SignUpButton,
UserButton,
useAuth,
useUser
} from "@clerk/nextjs";
import Link from "next/link";
// Show component for conditional rendering based on auth state
interface ShowProps {
when: "signed-in" | "signed-out";
children: React.ReactNode;
}
export function Show({ when, children }: ShowProps) {
const { isSignedIn } = useAuth();
if (when === "signed-in" && isSignedIn) {
return <>{children}</>;
}
if (when === "signed-out" && !isSignedIn) {
return <>{children}</>;
}
return null;
}
// User profile button with dropdown menu
export function ProfileButton() {
const { user } = useUser();
return (
<div className="flex items-center gap-2">
<UserButton
afterSignOutUrl="/"
appearance={{
elements: {
avatarBox: "w-8 h-8",
},
}}
/>
{user && (
<span className="hidden md:block text-sm text-gray-700">
{user.firstName || user.emailAddresses[0]?.emailAddress}
</span>
)}
</div>
);
}
// Sign in button for use in nav/header
export function SignInLink({ className }: { className?: string }) {
return (
<SignInButton mode="modal">
<button className={className || "px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90"}>
Sign In
</button>
</SignInButton>
);
}
// Sign up button for use in nav/header
export function SignUpLink({ className }: { className?: string }) {
return (
<SignUpButton mode="modal">
<button className={className || "px-4 py-2 border border-primary text-primary rounded-lg hover:bg-primary/5"}>
Sign Up
</button>
</SignUpButton>
);
}
// Combined auth buttons for header
export function AuthButtons({ className }: { className?: string }) {
return (
<div className={`flex items-center gap-3 ${className || ""}`}>
<Show when="signed-out">
<>
<SignInLink />
<SignUpLink />
</>
</Show>
<Show when="signed-in">
<ProfileButton />
</Show>
</div>
);
}
// Admin navigation with auth check
export function AdminNav() {
const { isSignedIn, userId } = useAuth();
const { user } = useUser();
if (!isSignedIn) {
return (
<div className="flex items-center gap-4">
<SignInLink />
<SignUpLink />
</div>
);
}
export default function ClerkComponents() {
return (
<div className="flex items-center gap-4">
<Link href="/admin" className="text-sm text-gray-600 hover:text-primary">
Dashboard
</Link>
<Link href="/admin/orders" className="text-sm text-gray-600 hover:text-primary">
Orders
</Link>
<Link href="/admin/products" className="text-sm text-gray-600 hover:text-primary">
Products
</Link>
<ProfileButton />
</div>
);
}
// Wholesale customer navigation
export function WholesaleNav() {
const { isSignedIn } = useAuth();
return (
<div className="flex items-center gap-4">
<Show when="signed-out">
<SignInLink />
</Show>
<Show when="signed-in">
<>
<Link href="/wholesale/portal" className="text-sm text-gray-600 hover:text-primary">
Portal
</Link>
<ProfileButton />
</>
</Show>
</div>
);
}
// Loading state component
export function AuthLoading() {
return (
<div className="flex items-center gap-2">
<div className="w-8 h-8 border-2 border-gray-300 border-t-primary rounded-full animate-spin" />
<span className="text-sm text-gray-500">Loading...</span>
<UserButton />
</div>
);
}