Production upgrade: Clerk auth, Stripe billing, analytics, PWA support

Backend & Auth:
- Add @clerk/nextjs for production authentication
- Create src/proxy.ts with clerkMiddleware() for route protection
- Implement multi-tenant auth with role-based access control
- Add Clerk components (Show, UserButton, SignInButton, SignUpButton)

Billing & Payments:
- Full Stripe integration (subscriptions, add-ons, customer portal)
- Plan tiers: Starter 9/mo, Farm 49/mo, Enterprise 99/mo
- Webhook handling for subscription events
- createSubscription(), createAddonSubscription(), createCustomerPortalSession()

API & Security:
- Rate limiting with @upstash/ratelimit (100 req/min API, 20 req/min checkout)
- Zod validation schemas for all endpoints (orders, products, campaigns, etc.)
- Security headers (CSP, HSTS, X-Frame-Options)
- API routes: /api/v1/ with validated, rate-limited endpoints

Monitoring:
- Sentry error tracking with performance monitoring
- PostHog analytics for feature usage, funnels, cohorts
- User activity logging and breadcrumb tracking

Admin Features:
- Analytics dashboard with revenue charts, customer growth, conversion funnel
- Onboarding flow with 6-step interactive tour
- Referral system with share tracking and reward redemption
- Changelog feed with in-app notifications

PWA & SEO:
- Web app manifest with icons and shortcuts
- Service worker for offline support and caching
- Full SEO metadata, OpenGraph, Twitter cards
- Structured data (JSON-LD) for organization and products

Database:
- Add referral_codes, changelogs, onboarding_progress tables
- Add user_activity_logs, api_keys, notification_preferences
- Comprehensive RLS policies for all new tables
- Seed data for demo brands and products
This commit is contained in:
2026-06-02 05:33:42 +00:00
parent b845d69aba
commit 6ab52a2499
32 changed files with 5816 additions and 501 deletions
+156
View File
@@ -0,0 +1,156 @@
// Clerk authentication components for the UI
// Use Show, UserButton, SignInButton, SignUpButton 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>
);
}
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>
</div>
);
}