├── 0 ├── .npmrc ├── src ├── app │ ├── favicon.ico │ ├── api │ │ ├── auth │ │ │ ├── [...nextauth] │ │ │ │ └── route.ts │ │ │ └── save-user │ │ │ │ └── route.ts │ │ ├── users │ │ │ ├── stats │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── usage │ │ │ └── route.ts │ │ ├── credits │ │ │ └── route.ts │ │ ├── admin │ │ │ ├── analytics │ │ │ │ └── route.ts │ │ │ ├── credits │ │ │ │ └── route.ts │ │ │ ├── stats │ │ │ │ └── route.ts │ │ │ ├── users │ │ │ │ ├── route.ts │ │ │ │ └── [id] │ │ │ │ │ ├── credits │ │ │ │ │ └── route.ts │ │ │ │ │ └── route.ts │ │ │ ├── test-analytics │ │ │ │ └── route.ts │ │ │ ├── upgrade-user │ │ │ │ └── route.ts │ │ │ └── webhook-debug │ │ │ │ └── route.ts │ │ ├── stripe │ │ │ ├── test │ │ │ │ └── route.ts │ │ │ └── checkout │ │ │ │ └── route.ts │ │ ├── emails │ │ │ ├── test │ │ │ │ └── route.ts │ │ │ ├── welcome │ │ │ │ └── route.ts │ │ │ ├── contact │ │ │ │ └── route.ts │ │ │ └── subscription │ │ │ │ └── route.ts │ │ ├── test-ai │ │ │ └── route.ts │ │ ├── discounts │ │ │ └── validate │ │ │ │ └── route.ts │ │ └── chat │ │ │ ├── stream │ │ │ └── route.ts │ │ │ └── route.ts │ ├── admin │ │ ├── layout.tsx │ │ ├── users │ │ │ └── page.tsx │ │ └── discounts │ │ │ └── page.tsx │ ├── page.tsx │ ├── dashboard │ │ ├── layout.tsx │ │ └── profile │ │ │ └── [[...rest]] │ │ │ └── page.tsx │ ├── auth │ │ └── signin │ │ │ └── page.tsx │ ├── layout.tsx │ ├── globals.css │ └── demo │ │ └── page.tsx ├── lib │ ├── utils.ts │ ├── auth-actions.ts │ ├── auth-utils.ts │ ├── stripe-client.ts │ ├── admin-config.ts │ ├── auth.ts │ ├── user-actions.ts │ ├── admin-auth.ts │ ├── simple-analytics.ts │ ├── usage-tracking.ts │ ├── stripe.ts │ └── openrouter.ts ├── components │ ├── landing │ │ ├── navigation.tsx │ │ ├── pricing-client.tsx │ │ └── navigation-client.tsx │ ├── auth │ │ ├── signin-button.tsx │ │ ├── signout-button.tsx │ │ ├── user-button.tsx │ │ └── user-button-client.tsx │ ├── ui │ │ ├── label.tsx │ │ ├── textarea.tsx │ │ ├── smooth-scroll.tsx │ │ ├── separator.tsx │ │ ├── progress.tsx │ │ ├── input.tsx │ │ ├── switch.tsx │ │ ├── badge.tsx │ │ ├── loading.tsx │ │ ├── popover.tsx │ │ ├── scroll-to-top.tsx │ │ ├── avatar.tsx │ │ ├── theme-toggle.tsx │ │ ├── card.tsx │ │ ├── button.tsx │ │ ├── optimized-image.tsx │ │ ├── calendar.tsx │ │ ├── table.tsx │ │ ├── dialog.tsx │ │ └── alert-dialog.tsx │ ├── theme-script.tsx │ ├── credits │ │ └── credits-display.tsx │ ├── theme-provider.tsx │ ├── chat │ │ ├── chat-input.tsx │ │ └── chat-message.tsx │ ├── billing │ │ └── billing-client.tsx │ ├── dashboard │ │ └── dashboard-client.tsx │ └── contact │ │ └── contact-form.tsx ├── types │ └── next-auth.d.ts ├── middleware.ts └── hooks │ └── use-toast.ts ├── public ├── best saas kit.png ├── vercel.svg ├── window.svg ├── file.svg ├── globe.svg └── next.svg ├── postcss.config.mjs ├── next.config.ts ├── tsconfig.json ├── .gitignore ├── package.json ├── sql-queries ├── 01-create-users-table.sql ├── README.md ├── 06-cleanup-sample-data.sql ├── 02-create-indexes.sql ├── 04-insert-sample-data.sql └── 05-verify-setup.sql └── .env.example /0: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zainulabedeen123/Best-Saas-Kit--V2/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /public/best saas kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zainulabedeen123/Best-Saas-Kit--V2/HEAD/public/best saas kit.png -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { handlers } from "@/lib/auth" 2 | 3 | export const { GET, POST } = handlers 4 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /src/components/landing/navigation.tsx: -------------------------------------------------------------------------------- 1 | import { auth } from "@/lib/auth" 2 | import { NavigationClient } from "./navigation-client" 3 | 4 | const Navigation = async () => { 5 | const session = await auth() 6 | 7 | return 8 | } 9 | 10 | export default Navigation -------------------------------------------------------------------------------- /src/lib/auth-actions.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { signIn as nextAuthSignIn, signOut as nextAuthSignOut } from "@/lib/auth" 4 | 5 | export async function signInAction() { 6 | await nextAuthSignIn("google", { redirectTo: "/dashboard" }) 7 | } 8 | 9 | export async function signOutAction() { 10 | await nextAuthSignOut({ redirectTo: "/" }) 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/auth-utils.ts: -------------------------------------------------------------------------------- 1 | import { auth } from "./auth" 2 | 3 | export async function getCurrentUser() { 4 | const session = await auth() 5 | return session?.user 6 | } 7 | 8 | export async function requireAuth() { 9 | const session = await auth() 10 | if (!session?.user) { 11 | throw new Error("Authentication required") 12 | } 13 | return session.user 14 | } 15 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/types/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth" 2 | 3 | declare module "next-auth" { 4 | interface Session { 5 | accessToken?: string 6 | user: { 7 | id: string 8 | name?: string | null 9 | email?: string | null 10 | image?: string | null 11 | } 12 | } 13 | 14 | interface JWT { 15 | accessToken?: string 16 | id?: string 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/admin/layout.tsx: -------------------------------------------------------------------------------- 1 | import { requireAdminAccess } from "@/lib/admin-auth"; 2 | import { AdminClient } from "@/components/admin/admin-client"; 3 | 4 | export const runtime = 'nodejs'; 5 | 6 | export default async function AdminLayout({ 7 | children, 8 | }: { 9 | children: React.ReactNode; 10 | }) { 11 | // This will redirect if user is not admin 12 | const adminUser = await requireAdminAccess(); 13 | 14 | return {children}; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/auth/signin-button.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { signInAction } from "@/lib/auth-actions" 4 | import { Button } from "@/components/ui/button" 5 | 6 | interface SignInButtonProps { 7 | children?: React.ReactNode 8 | className?: string 9 | } 10 | 11 | export function SignInButton({ children, className }: SignInButtonProps) { 12 | return ( 13 |
14 | 17 |
18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/stripe-client.ts: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { loadStripe } from '@stripe/stripe-js'; 4 | 5 | export const stripePromise = loadStripe( 6 | process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY! 7 | ); 8 | 9 | export const redirectToCheckout = async (sessionId: string) => { 10 | const stripe = await stripePromise; 11 | 12 | if (!stripe) { 13 | throw new Error('Stripe failed to load'); 14 | } 15 | 16 | const { error } = await stripe.redirectToCheckout({ 17 | sessionId, 18 | }); 19 | 20 | if (error) { 21 | throw error; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/auth/signout-button.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { signOutAction } from "@/lib/auth-actions" 4 | import { Button } from "@/components/ui/button" 5 | 6 | interface SignOutButtonProps { 7 | children?: React.ReactNode 8 | className?: string 9 | } 10 | 11 | export function SignOutButton({ children, className }: SignOutButtonProps) { 12 | return ( 13 |
14 | 21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Navigation from "@/components/landing/navigation" 2 | import Hero from "@/components/landing/hero" 3 | import Features from "@/components/landing/features" 4 | import Pricing from "@/components/landing/pricing" 5 | import Testimonials from "@/components/landing/testimonials" 6 | import Footer from "@/components/landing/footer" 7 | import { SmoothScroll } from "@/components/ui/smooth-scroll" 8 | import { ScrollToTop } from "@/components/ui/scroll-to-top" 9 | 10 | export default function Home() { 11 | return ( 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /src/components/theme-script.tsx: -------------------------------------------------------------------------------- 1 | export function ThemeScript() { 2 | const script = ` 3 | (function() { 4 | function getThemePreference() { 5 | if (typeof localStorage !== 'undefined' && localStorage.getItem('ui-theme')) { 6 | return localStorage.getItem('ui-theme'); 7 | } 8 | return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; 9 | } 10 | 11 | const theme = getThemePreference(); 12 | 13 | if (theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)) { 14 | document.documentElement.classList.add('dark'); 15 | } else { 16 | document.documentElement.classList.remove('dark'); 17 | } 18 | })(); 19 | ` 20 | 21 | return