├── public └── hero.png ├── types ├── index.ts └── server-action-types.ts ├── db ├── schema │ ├── index.ts │ └── profiles-schema.ts └── db.ts ├── .husky └── pre-commit ├── components ├── ui │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── collapsible.tsx │ ├── textarea.tsx │ ├── label.tsx │ ├── input.tsx │ ├── separator.tsx │ ├── progress.tsx │ ├── toaster.tsx │ ├── sonner.tsx │ ├── checkbox.tsx │ ├── slider.tsx │ ├── switch.tsx │ ├── badge.tsx │ ├── tooltip.tsx │ ├── hover-card.tsx │ ├── popover.tsx │ ├── avatar.tsx │ ├── radio-group.tsx │ ├── toggle.tsx │ ├── alert.tsx │ ├── scroll-area.tsx │ ├── resizable.tsx │ ├── toggle-group.tsx │ ├── tabs.tsx │ ├── button.tsx │ ├── card.tsx │ ├── accordion.tsx │ ├── input-otp.tsx │ ├── calendar.tsx │ ├── breadcrumb.tsx │ ├── pagination.tsx │ ├── table.tsx │ ├── drawer.tsx │ ├── dialog.tsx │ ├── use-toast.ts │ ├── sheet.tsx │ ├── form.tsx │ ├── alert-dialog.tsx │ ├── toast.tsx │ ├── command.tsx │ ├── navigation-menu.tsx │ ├── select.tsx │ └── carousel.tsx ├── utilities │ ├── providers.tsx │ ├── tailwind-indicator.tsx │ └── theme-switcher.tsx ├── magicui │ ├── animated-gradient-text.tsx │ └── hero-video-dialog.tsx └── landing │ ├── hero.tsx │ ├── footer.tsx │ └── header.tsx ├── postcss.config.mjs ├── .cursor └── rules │ ├── payments.mdc │ ├── auth.mdc │ ├── env.mdc │ ├── types.mdc │ ├── general.mdc │ ├── frontend.mdc │ ├── storage.mdc │ └── backend.mdc ├── next.config.mjs ├── lib ├── utils.ts ├── stripe.ts └── hooks │ ├── use-mobile.tsx │ ├── use-copy-to-clipboard.tsx │ └── use-toast.ts ├── app ├── (marketing) │ ├── page.tsx │ ├── layout.tsx │ ├── contact │ │ ├── page.tsx │ │ └── _components │ │ │ └── contact-form.tsx │ ├── about │ │ └── page.tsx │ ├── features │ │ └── page.tsx │ └── pricing │ │ └── page.tsx ├── (auth) │ ├── layout.tsx │ ├── login │ │ └── [[...login]] │ │ │ └── page.tsx │ └── signup │ │ └── [[...signup]] │ │ └── page.tsx ├── layout.tsx ├── globals.css └── api │ └── stripe │ └── webhooks │ └── route.ts ├── drizzle.config.ts ├── .env.example ├── components.json ├── .gitignore ├── .repo_ignore ├── tsconfig.json ├── middleware.ts ├── .eslintrc.json ├── prettier.config.cjs ├── license ├── tailwind.config.ts ├── actions ├── stripe-actions.ts └── db │ └── profiles-actions.ts ├── package.json └── README.md /public/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckaywrigley/o1-pro-template-system/HEAD/public/hero.png -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Exports the types for the app. 3 | */ 4 | 5 | export * from "./server-action-types" 6 | -------------------------------------------------------------------------------- /db/schema/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Exports the database schema for the app. 3 | */ 4 | 5 | export * from "./profiles-schema" 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | . "$(dirname -- "$0")/_/husky.sh" 4 | 5 | npm run lint:fix && npm run format:write && git add . -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | Configures PostCSS for the app. 3 | */ 4 | 5 | /** @type {import('postcss-load-config').Config} */ 6 | const config = { plugins: { tailwindcss: {} } } 7 | 8 | export default config 9 | -------------------------------------------------------------------------------- /.cursor/rules/payments.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Follow these rules when working on payments. 3 | globs: 4 | --- 5 | # Payments Rules 6 | 7 | Follow these rules when working on payments. 8 | 9 | It uses Stripe for payments. -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | Configures Next.js for the app. 3 | */ 4 | 5 | /** @type {import('next').NextConfig} */ 6 | const nextConfig = { images: { remotePatterns: [{ hostname: "localhost" }] } } 7 | 8 | export default nextConfig 9 | -------------------------------------------------------------------------------- /types/server-action-types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Contains the general server action types. 3 | */ 4 | 5 | export type ActionState = 6 | | { isSuccess: true; message: string; data: T } 7 | | { isSuccess: false; message: string; data?: never } 8 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Contains the utility functions for the app. 3 | */ 4 | 5 | import { type ClassValue, clsx } from "clsx" 6 | import { twMerge } from "tailwind-merge" 7 | 8 | export function cn(...inputs: ClassValue[]) { 9 | return twMerge(clsx(inputs)) 10 | } 11 | -------------------------------------------------------------------------------- /lib/stripe.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Contains the Stripe configuration for the app. 3 | */ 4 | 5 | import Stripe from "stripe" 6 | 7 | export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { 8 | apiVersion: "2025-01-27.acacia", 9 | appInfo: { name: "Receipt AI", version: "0.1.0" } 10 | }) 11 | -------------------------------------------------------------------------------- /app/(marketing)/page.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This server page is the marketing homepage. 3 | */ 4 | 5 | "use server" 6 | 7 | import { HeroSection } from "@/components/landing/hero" 8 | 9 | export default async function HomePage() { 10 | return ( 11 |
12 | 13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /.cursor/rules/auth.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Follow these rules when working on auth. 3 | globs: 4 | --- 5 | # Auth Rules 6 | 7 | Follow these rules when working on auth. 8 | 9 | It uses Clerk for authentication. 10 | 11 | ## General Rules 12 | 13 | - Import the auth helper with `import { auth } from "@clerk/nextjs/server"` in server components 14 | - await the auth helper in server action -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This server layout provides a centered layout for (auth) pages. 3 | */ 4 | 5 | "use server" 6 | 7 | interface AuthLayoutProps { 8 | children: React.ReactNode 9 | } 10 | 11 | export default async function AuthLayout({ children }: AuthLayoutProps) { 12 | return ( 13 |
{children}
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Configures Drizzle for the app. 3 | */ 4 | 5 | import { config } from "dotenv" 6 | import { defineConfig } from "drizzle-kit" 7 | 8 | config({ path: ".env.local" }) 9 | 10 | export default defineConfig({ 11 | schema: "./db/schema/index.ts", 12 | out: "./db/migrations", 13 | dialect: "postgresql", 14 | dbCredentials: { url: process.env.DATABASE_URL! } 15 | }) 16 | -------------------------------------------------------------------------------- /components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /db/db.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Initializes the database connection and schema for the app. 3 | */ 4 | 5 | import { profilesTable } from "@/db/schema" 6 | import { config } from "dotenv" 7 | import { drizzle } from "drizzle-orm/postgres-js" 8 | import postgres from "postgres" 9 | 10 | config({ path: ".env.local" }) 11 | 12 | const schema = { profiles: profilesTable } 13 | 14 | const client = postgres(process.env.DATABASE_URL!) 15 | 16 | export const db = drizzle(client, { schema }) 17 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # DB 2 | DATABASE_URL= 3 | 4 | # Supabase 5 | SUPABASE_URL= 6 | SUPABASE_SERVICE_ROLE_KEY= 7 | SUPABASE_BUCKET_RECEIPTS= 8 | 9 | # Auth 10 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= 11 | CLERK_SECRET_KEY= 12 | NEXT_PUBLIC_CLERK_SIGN_IN_URL=/login 13 | NEXT_PUBLIC_CLERK_SIGN_UP_URL=/signup 14 | 15 | # Payments 16 | STRIPE_SECRET_KEY= 17 | STRIPE_WEBHOOK_SECRET= 18 | NEXT_PUBLIC_STRIPE_PAYMENT_LINK_YEARLY= 19 | NEXT_PUBLIC_STRIPE_PAYMENT_LINK_MONTHLY= 20 | 21 | # AI 22 | OPENAI_API_KEY= -------------------------------------------------------------------------------- /app/(auth)/login/[[...login]]/page.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This client page provides the login form from Clerk. 3 | */ 4 | 5 | "use client" 6 | 7 | import { SignIn } from "@clerk/nextjs" 8 | import { dark } from "@clerk/themes" 9 | import { useTheme } from "next-themes" 10 | 11 | export default function LoginPage() { 12 | const { theme } = useTheme() 13 | 14 | return ( 15 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /app/(auth)/signup/[[...signup]]/page.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This client page provides the signup form from Clerk. 3 | */ 4 | 5 | "use client" 6 | 7 | import { SignUp } from "@clerk/nextjs" 8 | import { dark } from "@clerk/themes" 9 | import { useTheme } from "next-themes" 10 | 11 | export default function SignUpPage() { 12 | const { theme } = useTheme() 13 | 14 | return ( 15 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/lib/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.cursor/rules/env.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Follow these rules when working with environment variables. 3 | globs: 4 | --- 5 | # Env Rules 6 | 7 | - If you update environment variables, update the `.env.example` file 8 | - All environment variables should go in `.env.local` 9 | - Do not expose environment variables to the frontend 10 | - Use `NEXT_PUBLIC_` prefix for environment variables that need to be accessed from the frontend 11 | - You may import environment variables in server actions and components by using `process.env.VARIABLE_NAME` -------------------------------------------------------------------------------- /components/utilities/providers.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This client component provides the providers for the app. 3 | */ 4 | 5 | "use client" 6 | 7 | import { TooltipProvider } from "@/components/ui/tooltip" 8 | import { 9 | ThemeProvider as NextThemesProvider, 10 | ThemeProviderProps 11 | } from "next-themes" 12 | 13 | export const Providers = ({ children, ...props }: ThemeProviderProps) => { 14 | return ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /app/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | This server layout provides a shared header and basic structure for (marketing) routes. 3 | */ 4 | 5 | "use server" 6 | 7 | import { Footer } from "@/components/landing/footer" 8 | import Header from "@/components/landing/header" 9 | 10 | export default async function MarketingLayout({ 11 | children 12 | }: { 13 | children: React.ReactNode 14 | }) { 15 | return ( 16 |
17 |
18 |
{children}
19 |
20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # clerk configuration (can include secrets) 39 | /.clerk/ 40 | -------------------------------------------------------------------------------- /.repo_ignore: -------------------------------------------------------------------------------- 1 | # Package manager caches 2 | **/node_modules/ 3 | **/.npm/ 4 | **/__pycache__/ 5 | **/.pytest_cache/ 6 | **/.mypy_cache/ 7 | 8 | # Build caches 9 | **/.gradle/ 10 | **/.nuget/ 11 | **/.cargo/ 12 | **/.stack-work/ 13 | **/.ccache/ 14 | 15 | # IDE and Editor caches 16 | **/.idea/ 17 | **/.vscode/ 18 | **/*.swp 19 | **/*~ 20 | 21 | # Temp files 22 | **/*.tmp 23 | **/*.temp 24 | **/*.bak 25 | 26 | **/*.meta 27 | **/package-lock.json 28 | 29 | # AI Specific 30 | .repo_ignore 31 | .cursorrules 32 | /.cursor 33 | 34 | # Project Specific 35 | **/.github 36 | **/.husky 37 | **/migrations 38 | **/public 39 | **/.next 40 | README.md -------------------------------------------------------------------------------- /lib/hooks/use-mobile.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | Hook to check if the user is on a mobile device. 3 | */ 4 | 5 | import * as React from "react" 6 | 7 | const MOBILE_BREAKPOINT = 768 8 | 9 | export function useIsMobile() { 10 | const [isMobile, setIsMobile] = React.useState(undefined) 11 | 12 | React.useEffect(() => { 13 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 14 | const onChange = () => { 15 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 16 | } 17 | mql.addEventListener("change", onChange) 18 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 19 | return () => mql.removeEventListener("change", onChange) 20 | }, []) 21 | 22 | return !!isMobile 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* 2 | Configures the TypeScript compiler options for the app. 3 | */ 4 | 5 | { 6 | "compilerOptions": { 7 | "lib": ["dom", "dom.iterable", "esnext"], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "bundler", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "incremental": true, 19 | "plugins": [{ "name": "next" }], 20 | "paths": { "@/*": ["./*"] }, 21 | "target": "ES2017" 22 | }, 23 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /.cursor/rules/types.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Follow these rules when working with types. 3 | globs: 4 | --- 5 | # Type Rules 6 | 7 | - When importing types, use `@/types` 8 | - Name files like `example-types.ts` 9 | - All types should go in `types` 10 | - Make sure to export the types in `types/index.ts` 11 | - Prefer interfaces over type aliases 12 | - If referring to db types, use `@/db/schema` such as `SelectTodo` from `todos-schema.ts` 13 | 14 | An example of a type: 15 | 16 | `types/actions-types.ts` 17 | 18 | ```ts 19 | export type ActionState = 20 | | { isSuccess: true; message: string; data: T } 21 | | { isSuccess: false; message: string; data?: never } 22 | ``` 23 | 24 | And exporting it: 25 | 26 | `types/index.ts` 27 | 28 | ```ts 29 | export * from "./actions-types" 30 | ``` -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |