[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\",\n  \"rules\": {\n    \"no-unused-vars\": [\"warn\"]\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n.env"
  },
  {
    "path": "README.md",
    "content": "# Full Stack AI Fianace Platform with Next JS, Supabase, Tailwind, Prisma, Inngest, ArcJet, Shadcn UI Tutorial 🔥🔥\n## https://youtu.be/egS6fnZAdzk\n\n<img width=\"1470\" alt=\"Screenshot 2024-12-10 at 9 45 45 AM\" src=\"https://github.com/user-attachments/assets/1bc50b85-b421-4122-8ba4-ae68b2b61432\">\n\n### Make sure to create a `.env` file with following variables -\n\n```\nDATABASE_URL=\nDIRECT_URL=\n\nNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=\nCLERK_SECRET_KEY=\nNEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in\nNEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up\nNEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/onboarding\nNEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding\n\nGEMINI_API_KEY=\n\nRESEND_API_KEY=\n\nARCJET_KEY=\n```\n"
  },
  {
    "path": "actions/account.js",
    "content": "\"use server\";\n\nimport { db } from \"@/lib/prisma\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { revalidatePath } from \"next/cache\";\n\nconst serializeDecimal = (obj) => {\n  const serialized = { ...obj };\n  if (obj.balance) {\n    serialized.balance = obj.balance.toNumber();\n  }\n  if (obj.amount) {\n    serialized.amount = obj.amount.toNumber();\n  }\n  return serialized;\n};\n\nexport async function getAccountWithTransactions(accountId) {\n  const { userId } = await auth();\n  if (!userId) throw new Error(\"Unauthorized\");\n\n  const user = await db.user.findUnique({\n    where: { clerkUserId: userId },\n  });\n\n  if (!user) throw new Error(\"User not found\");\n\n  const account = await db.account.findUnique({\n    where: {\n      id: accountId,\n      userId: user.id,\n    },\n    include: {\n      transactions: {\n        orderBy: { date: \"desc\" },\n      },\n      _count: {\n        select: { transactions: true },\n      },\n    },\n  });\n\n  if (!account) return null;\n\n  return {\n    ...serializeDecimal(account),\n    transactions: account.transactions.map(serializeDecimal),\n  };\n}\n\nexport async function bulkDeleteTransactions(transactionIds) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) throw new Error(\"User not found\");\n\n    // Get transactions to calculate balance changes\n    const transactions = await db.transaction.findMany({\n      where: {\n        id: { in: transactionIds },\n        userId: user.id,\n      },\n    });\n\n    // Group transactions by account to update balances\n    const accountBalanceChanges = transactions.reduce((acc, transaction) => {\n      const change =\n        transaction.type === \"EXPENSE\"\n          ? transaction.amount\n          : -transaction.amount;\n      acc[transaction.accountId] = (acc[transaction.accountId] || 0) + change;\n      return acc;\n    }, {});\n\n    // Delete transactions and update account balances in a transaction\n    await db.$transaction(async (tx) => {\n      // Delete transactions\n      await tx.transaction.deleteMany({\n        where: {\n          id: { in: transactionIds },\n          userId: user.id,\n        },\n      });\n\n      // Update account balances\n      for (const [accountId, balanceChange] of Object.entries(\n        accountBalanceChanges\n      )) {\n        await tx.account.update({\n          where: { id: accountId },\n          data: {\n            balance: {\n              increment: balanceChange,\n            },\n          },\n        });\n      }\n    });\n\n    revalidatePath(\"/dashboard\");\n    revalidatePath(\"/account/[id]\");\n\n    return { success: true };\n  } catch (error) {\n    return { success: false, error: error.message };\n  }\n}\n\nexport async function updateDefaultAccount(accountId) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) {\n      throw new Error(\"User not found\");\n    }\n\n    // First, unset any existing default account\n    await db.account.updateMany({\n      where: {\n        userId: user.id,\n        isDefault: true,\n      },\n      data: { isDefault: false },\n    });\n\n    // Then set the new default account\n    const account = await db.account.update({\n      where: {\n        id: accountId,\n        userId: user.id,\n      },\n      data: { isDefault: true },\n    });\n\n    revalidatePath(\"/dashboard\");\n    return { success: true, data: serializeTransaction(account) };\n  } catch (error) {\n    return { success: false, error: error.message };\n  }\n}\n"
  },
  {
    "path": "actions/budget.js",
    "content": "\"use server\";\n\nimport { db } from \"@/lib/prisma\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { revalidatePath } from \"next/cache\";\n\nexport async function getCurrentBudget(accountId) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) {\n      throw new Error(\"User not found\");\n    }\n\n    const budget = await db.budget.findFirst({\n      where: {\n        userId: user.id,\n      },\n    });\n\n    // Get current month's expenses\n    const currentDate = new Date();\n    const startOfMonth = new Date(\n      currentDate.getFullYear(),\n      currentDate.getMonth(),\n      1\n    );\n    const endOfMonth = new Date(\n      currentDate.getFullYear(),\n      currentDate.getMonth() + 1,\n      0\n    );\n\n    const expenses = await db.transaction.aggregate({\n      where: {\n        userId: user.id,\n        type: \"EXPENSE\",\n        date: {\n          gte: startOfMonth,\n          lte: endOfMonth,\n        },\n        accountId,\n      },\n      _sum: {\n        amount: true,\n      },\n    });\n\n    return {\n      budget: budget ? { ...budget, amount: budget.amount.toNumber() } : null,\n      currentExpenses: expenses._sum.amount\n        ? expenses._sum.amount.toNumber()\n        : 0,\n    };\n  } catch (error) {\n    console.error(\"Error fetching budget:\", error);\n    throw error;\n  }\n}\n\nexport async function updateBudget(amount) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) throw new Error(\"User not found\");\n\n    // Update or create budget\n    const budget = await db.budget.upsert({\n      where: {\n        userId: user.id,\n      },\n      update: {\n        amount,\n      },\n      create: {\n        userId: user.id,\n        amount,\n      },\n    });\n\n    revalidatePath(\"/dashboard\");\n    return {\n      success: true,\n      data: { ...budget, amount: budget.amount.toNumber() },\n    };\n  } catch (error) {\n    console.error(\"Error updating budget:\", error);\n    return { success: false, error: error.message };\n  }\n}\n"
  },
  {
    "path": "actions/dashboard.js",
    "content": "\"use server\";\n\nimport aj from \"@/lib/arcjet\";\nimport { db } from \"@/lib/prisma\";\nimport { request } from \"@arcjet/next\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { revalidatePath } from \"next/cache\";\n\nconst serializeTransaction = (obj) => {\n  const serialized = { ...obj };\n  if (obj.balance) {\n    serialized.balance = obj.balance.toNumber();\n  }\n  if (obj.amount) {\n    serialized.amount = obj.amount.toNumber();\n  }\n  return serialized;\n};\n\nexport async function getUserAccounts() {\n  const { userId } = await auth();\n  if (!userId) throw new Error(\"Unauthorized\");\n\n  const user = await db.user.findUnique({\n    where: { clerkUserId: userId },\n  });\n\n  if (!user) {\n    throw new Error(\"User not found\");\n  }\n\n  try {\n    const accounts = await db.account.findMany({\n      where: { userId: user.id },\n      orderBy: { createdAt: \"desc\" },\n      include: {\n        _count: {\n          select: {\n            transactions: true,\n          },\n        },\n      },\n    });\n\n    // Serialize accounts before sending to client\n    const serializedAccounts = accounts.map(serializeTransaction);\n\n    return serializedAccounts;\n  } catch (error) {\n    console.error(error.message);\n  }\n}\n\nexport async function createAccount(data) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    // Get request data for ArcJet\n    const req = await request();\n\n    // Check rate limit\n    const decision = await aj.protect(req, {\n      userId,\n      requested: 1, // Specify how many tokens to consume\n    });\n\n    if (decision.isDenied()) {\n      if (decision.reason.isRateLimit()) {\n        const { remaining, reset } = decision.reason;\n        console.error({\n          code: \"RATE_LIMIT_EXCEEDED\",\n          details: {\n            remaining,\n            resetInSeconds: reset,\n          },\n        });\n\n        throw new Error(\"Too many requests. Please try again later.\");\n      }\n\n      throw new Error(\"Request blocked\");\n    }\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) {\n      throw new Error(\"User not found\");\n    }\n\n    // Convert balance to float before saving\n    const balanceFloat = parseFloat(data.balance);\n    if (isNaN(balanceFloat)) {\n      throw new Error(\"Invalid balance amount\");\n    }\n\n    // Check if this is the user's first account\n    const existingAccounts = await db.account.findMany({\n      where: { userId: user.id },\n    });\n\n    // If it's the first account, make it default regardless of user input\n    // If not, use the user's preference\n    const shouldBeDefault =\n      existingAccounts.length === 0 ? true : data.isDefault;\n\n    // If this account should be default, unset other default accounts\n    if (shouldBeDefault) {\n      await db.account.updateMany({\n        where: { userId: user.id, isDefault: true },\n        data: { isDefault: false },\n      });\n    }\n\n    // Create new account\n    const account = await db.account.create({\n      data: {\n        ...data,\n        balance: balanceFloat,\n        userId: user.id,\n        isDefault: shouldBeDefault, // Override the isDefault based on our logic\n      },\n    });\n\n    // Serialize the account before returning\n    const serializedAccount = serializeTransaction(account);\n\n    revalidatePath(\"/dashboard\");\n    return { success: true, data: serializedAccount };\n  } catch (error) {\n    throw new Error(error.message);\n  }\n}\n\nexport async function getDashboardData() {\n  const { userId } = await auth();\n  if (!userId) throw new Error(\"Unauthorized\");\n\n  const user = await db.user.findUnique({\n    where: { clerkUserId: userId },\n  });\n\n  if (!user) {\n    throw new Error(\"User not found\");\n  }\n\n  // Get all user transactions\n  const transactions = await db.transaction.findMany({\n    where: { userId: user.id },\n    orderBy: { date: \"desc\" },\n  });\n\n  return transactions.map(serializeTransaction);\n}\n"
  },
  {
    "path": "actions/seed.js",
    "content": "\"use server\";\n\nimport { db } from \"@/lib/prisma\";\nimport { subDays } from \"date-fns\";\n\nconst ACCOUNT_ID = \"account-id\";\nconst USER_ID = \"user-id\";\n\n// Categories with their typical amount ranges\nconst CATEGORIES = {\n  INCOME: [\n    { name: \"salary\", range: [5000, 8000] },\n    { name: \"freelance\", range: [1000, 3000] },\n    { name: \"investments\", range: [500, 2000] },\n    { name: \"other-income\", range: [100, 1000] },\n  ],\n  EXPENSE: [\n    { name: \"housing\", range: [1000, 2000] },\n    { name: \"transportation\", range: [100, 500] },\n    { name: \"groceries\", range: [200, 600] },\n    { name: \"utilities\", range: [100, 300] },\n    { name: \"entertainment\", range: [50, 200] },\n    { name: \"food\", range: [50, 150] },\n    { name: \"shopping\", range: [100, 500] },\n    { name: \"healthcare\", range: [100, 1000] },\n    { name: \"education\", range: [200, 1000] },\n    { name: \"travel\", range: [500, 2000] },\n  ],\n};\n\n// Helper to generate random amount within a range\nfunction getRandomAmount(min, max) {\n  return Number((Math.random() * (max - min) + min).toFixed(2));\n}\n\n// Helper to get random category with amount\nfunction getRandomCategory(type) {\n  const categories = CATEGORIES[type];\n  const category = categories[Math.floor(Math.random() * categories.length)];\n  const amount = getRandomAmount(category.range[0], category.range[1]);\n  return { category: category.name, amount };\n}\n\nexport async function seedTransactions() {\n  try {\n    // Generate 90 days of transactions\n    const transactions = [];\n    let totalBalance = 0;\n\n    for (let i = 90; i >= 0; i--) {\n      const date = subDays(new Date(), i);\n\n      // Generate 1-3 transactions per day\n      const transactionsPerDay = Math.floor(Math.random() * 3) + 1;\n\n      for (let j = 0; j < transactionsPerDay; j++) {\n        // 40% chance of income, 60% chance of expense\n        const type = Math.random() < 0.4 ? \"INCOME\" : \"EXPENSE\";\n        const { category, amount } = getRandomCategory(type);\n\n        const transaction = {\n          id: crypto.randomUUID(),\n          type,\n          amount,\n          description: `${\n            type === \"INCOME\" ? \"Received\" : \"Paid for\"\n          } ${category}`,\n          date,\n          category,\n          status: \"COMPLETED\",\n          userId: USER_ID,\n          accountId: ACCOUNT_ID,\n          createdAt: date,\n          updatedAt: date,\n        };\n\n        totalBalance += type === \"INCOME\" ? amount : -amount;\n        transactions.push(transaction);\n      }\n    }\n\n    // Insert transactions in batches and update account balance\n    await db.$transaction(async (tx) => {\n      // Clear existing transactions\n      await tx.transaction.deleteMany({\n        where: { accountId: ACCOUNT_ID },\n      });\n\n      // Insert new transactions\n      await tx.transaction.createMany({\n        data: transactions,\n      });\n\n      // Update account balance\n      await tx.account.update({\n        where: { id: ACCOUNT_ID },\n        data: { balance: totalBalance },\n      });\n    });\n\n    return {\n      success: true,\n      message: `Created ${transactions.length} transactions`,\n    };\n  } catch (error) {\n    console.error(\"Error seeding transactions:\", error);\n    return { success: false, error: error.message };\n  }\n}\n"
  },
  {
    "path": "actions/send-email.js",
    "content": "\"use server\";\n\nimport { Resend } from \"resend\";\n\nexport async function sendEmail({ to, subject, react }) {\n  const resend = new Resend(process.env.RESEND_API_KEY || \"\");\n\n  try {\n    const data = await resend.emails.send({\n      from: \"Finance App <onboarding@resend.dev>\",\n      to,\n      subject,\n      react,\n    });\n\n    return { success: true, data };\n  } catch (error) {\n    console.error(\"Failed to send email:\", error);\n    return { success: false, error };\n  }\n}\n"
  },
  {
    "path": "actions/transaction.js",
    "content": "\"use server\";\n\nimport { auth } from \"@clerk/nextjs/server\";\nimport { db } from \"@/lib/prisma\";\nimport { revalidatePath } from \"next/cache\";\nimport { GoogleGenerativeAI } from \"@google/generative-ai\";\nimport aj from \"@/lib/arcjet\";\nimport { request } from \"@arcjet/next\";\n\nconst genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);\n\nconst serializeAmount = (obj) => ({\n  ...obj,\n  amount: obj.amount.toNumber(),\n});\n\n// Create Transaction\nexport async function createTransaction(data) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    // Get request data for ArcJet\n    const req = await request();\n\n    // Check rate limit\n    const decision = await aj.protect(req, {\n      userId,\n      requested: 1, // Specify how many tokens to consume\n    });\n\n    if (decision.isDenied()) {\n      if (decision.reason.isRateLimit()) {\n        const { remaining, reset } = decision.reason;\n        console.error({\n          code: \"RATE_LIMIT_EXCEEDED\",\n          details: {\n            remaining,\n            resetInSeconds: reset,\n          },\n        });\n\n        throw new Error(\"Too many requests. Please try again later.\");\n      }\n\n      throw new Error(\"Request blocked\");\n    }\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) {\n      throw new Error(\"User not found\");\n    }\n\n    const account = await db.account.findUnique({\n      where: {\n        id: data.accountId,\n        userId: user.id,\n      },\n    });\n\n    if (!account) {\n      throw new Error(\"Account not found\");\n    }\n\n    // Calculate new balance\n    const balanceChange = data.type === \"EXPENSE\" ? -data.amount : data.amount;\n    const newBalance = account.balance.toNumber() + balanceChange;\n\n    // Create transaction and update account balance\n    const transaction = await db.$transaction(async (tx) => {\n      const newTransaction = await tx.transaction.create({\n        data: {\n          ...data,\n          userId: user.id,\n          nextRecurringDate:\n            data.isRecurring && data.recurringInterval\n              ? calculateNextRecurringDate(data.date, data.recurringInterval)\n              : null,\n        },\n      });\n\n      await tx.account.update({\n        where: { id: data.accountId },\n        data: { balance: newBalance },\n      });\n\n      return newTransaction;\n    });\n\n    revalidatePath(\"/dashboard\");\n    revalidatePath(`/account/${transaction.accountId}`);\n\n    return { success: true, data: serializeAmount(transaction) };\n  } catch (error) {\n    throw new Error(error.message);\n  }\n}\n\nexport async function getTransaction(id) {\n  const { userId } = await auth();\n  if (!userId) throw new Error(\"Unauthorized\");\n\n  const user = await db.user.findUnique({\n    where: { clerkUserId: userId },\n  });\n\n  if (!user) throw new Error(\"User not found\");\n\n  const transaction = await db.transaction.findUnique({\n    where: {\n      id,\n      userId: user.id,\n    },\n  });\n\n  if (!transaction) throw new Error(\"Transaction not found\");\n\n  return serializeAmount(transaction);\n}\n\nexport async function updateTransaction(id, data) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) throw new Error(\"User not found\");\n\n    // Get original transaction to calculate balance change\n    const originalTransaction = await db.transaction.findUnique({\n      where: {\n        id,\n        userId: user.id,\n      },\n      include: {\n        account: true,\n      },\n    });\n\n    if (!originalTransaction) throw new Error(\"Transaction not found\");\n\n    // Calculate balance changes\n    const oldBalanceChange =\n      originalTransaction.type === \"EXPENSE\"\n        ? -originalTransaction.amount.toNumber()\n        : originalTransaction.amount.toNumber();\n\n    const newBalanceChange =\n      data.type === \"EXPENSE\" ? -data.amount : data.amount;\n\n    const netBalanceChange = newBalanceChange - oldBalanceChange;\n\n    // Update transaction and account balance in a transaction\n    const transaction = await db.$transaction(async (tx) => {\n      const updated = await tx.transaction.update({\n        where: {\n          id,\n          userId: user.id,\n        },\n        data: {\n          ...data,\n          nextRecurringDate:\n            data.isRecurring && data.recurringInterval\n              ? calculateNextRecurringDate(data.date, data.recurringInterval)\n              : null,\n        },\n      });\n\n      // Update account balance\n      await tx.account.update({\n        where: { id: data.accountId },\n        data: {\n          balance: {\n            increment: netBalanceChange,\n          },\n        },\n      });\n\n      return updated;\n    });\n\n    revalidatePath(\"/dashboard\");\n    revalidatePath(`/account/${data.accountId}`);\n\n    return { success: true, data: serializeAmount(transaction) };\n  } catch (error) {\n    throw new Error(error.message);\n  }\n}\n\n// Get User Transactions\nexport async function getUserTransactions(query = {}) {\n  try {\n    const { userId } = await auth();\n    if (!userId) throw new Error(\"Unauthorized\");\n\n    const user = await db.user.findUnique({\n      where: { clerkUserId: userId },\n    });\n\n    if (!user) {\n      throw new Error(\"User not found\");\n    }\n\n    const transactions = await db.transaction.findMany({\n      where: {\n        userId: user.id,\n        ...query,\n      },\n      include: {\n        account: true,\n      },\n      orderBy: {\n        date: \"desc\",\n      },\n    });\n\n    return { success: true, data: transactions };\n  } catch (error) {\n    throw new Error(error.message);\n  }\n}\n\n// Scan Receipt\nexport async function scanReceipt(file) {\n  try {\n    const model = genAI.getGenerativeModel({ model: \"gemini-1.5-flash\" });\n\n    // Convert File to ArrayBuffer\n    const arrayBuffer = await file.arrayBuffer();\n    // Convert ArrayBuffer to Base64\n    const base64String = Buffer.from(arrayBuffer).toString(\"base64\");\n\n    const prompt = `\n      Analyze this receipt image and extract the following information in JSON format:\n      - Total amount (just the number)\n      - Date (in ISO format)\n      - Description or items purchased (brief summary)\n      - Merchant/store name\n      - Suggested category (one of: housing,transportation,groceries,utilities,entertainment,food,shopping,healthcare,education,personal,travel,insurance,gifts,bills,other-expense )\n      \n      Only respond with valid JSON in this exact format:\n      {\n        \"amount\": number,\n        \"date\": \"ISO date string\",\n        \"description\": \"string\",\n        \"merchantName\": \"string\",\n        \"category\": \"string\"\n      }\n\n      If its not a recipt, return an empty object\n    `;\n\n    const result = await model.generateContent([\n      {\n        inlineData: {\n          data: base64String,\n          mimeType: file.type,\n        },\n      },\n      prompt,\n    ]);\n\n    const response = await result.response;\n    const text = response.text();\n    const cleanedText = text.replace(/```(?:json)?\\n?/g, \"\").trim();\n\n    try {\n      const data = JSON.parse(cleanedText);\n      return {\n        amount: parseFloat(data.amount),\n        date: new Date(data.date),\n        description: data.description,\n        category: data.category,\n        merchantName: data.merchantName,\n      };\n    } catch (parseError) {\n      console.error(\"Error parsing JSON response:\", parseError);\n      throw new Error(\"Invalid response format from Gemini\");\n    }\n  } catch (error) {\n    console.error(\"Error scanning receipt:\", error);\n    throw new Error(\"Failed to scan receipt\");\n  }\n}\n\n// Helper function to calculate next recurring date\nfunction calculateNextRecurringDate(startDate, interval) {\n  const date = new Date(startDate);\n\n  switch (interval) {\n    case \"DAILY\":\n      date.setDate(date.getDate() + 1);\n      break;\n    case \"WEEKLY\":\n      date.setDate(date.getDate() + 7);\n      break;\n    case \"MONTHLY\":\n      date.setMonth(date.getMonth() + 1);\n      break;\n    case \"YEARLY\":\n      date.setFullYear(date.getFullYear() + 1);\n      break;\n  }\n\n  return date;\n}\n"
  },
  {
    "path": "app/(auth)/layout.js",
    "content": "const AuthLayout = ({ children }) => {\n  return <div className=\"flex justify-center pt-40\">{children}</div>;\n};\n\nexport default AuthLayout;\n"
  },
  {
    "path": "app/(auth)/sign-in/[[...sign-in]]/page.jsx",
    "content": "import { SignIn } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignIn />;\n}\n"
  },
  {
    "path": "app/(auth)/sign-up/[[...sign-up]]/page.jsx",
    "content": "import { SignUp } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignUp />;\n}\n"
  },
  {
    "path": "app/(main)/account/[id]/page.jsx",
    "content": "import { Suspense } from \"react\";\nimport { getAccountWithTransactions } from \"@/actions/account\";\nimport { BarLoader } from \"react-spinners\";\nimport { TransactionTable } from \"../_components/transaction-table\";\nimport { notFound } from \"next/navigation\";\nimport { AccountChart } from \"../_components/account-chart\";\n\nexport default async function AccountPage({ params }) {\n  const accountData = await getAccountWithTransactions(params.id);\n\n  if (!accountData) {\n    notFound();\n  }\n\n  const { transactions, ...account } = accountData;\n\n  return (\n    <div className=\"space-y-8 px-5\">\n      <div className=\"flex gap-4 items-end justify-between\">\n        <div>\n          <h1 className=\"text-5xl sm:text-6xl font-bold tracking-tight gradient-title capitalize\">\n            {account.name}\n          </h1>\n          <p className=\"text-muted-foreground\">\n            {account.type.charAt(0) + account.type.slice(1).toLowerCase()}{\" \"}\n            Account\n          </p>\n        </div>\n\n        <div className=\"text-right pb-2\">\n          <div className=\"text-xl sm:text-2xl font-bold\">\n            ${parseFloat(account.balance).toFixed(2)}\n          </div>\n          <p className=\"text-sm text-muted-foreground\">\n            {account._count.transactions} Transactions\n          </p>\n        </div>\n      </div>\n\n      {/* Chart Section */}\n      <Suspense\n        fallback={<BarLoader className=\"mt-4\" width={\"100%\"} color=\"#9333ea\" />}\n      >\n        <AccountChart transactions={transactions} />\n      </Suspense>\n\n      {/* Transactions Table */}\n      <Suspense\n        fallback={<BarLoader className=\"mt-4\" width={\"100%\"} color=\"#9333ea\" />}\n      >\n        <TransactionTable transactions={transactions} />\n      </Suspense>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/account/_components/account-chart.jsx",
    "content": "\"use client\";\n\nimport { useState, useMemo } from \"react\";\nimport {\n  BarChart,\n  Bar,\n  XAxis,\n  YAxis,\n  CartesianGrid,\n  Tooltip,\n  Legend,\n  ResponsiveContainer,\n} from \"recharts\";\nimport { format, subDays, startOfDay, endOfDay } from \"date-fns\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\n\nconst DATE_RANGES = {\n  \"7D\": { label: \"Last 7 Days\", days: 7 },\n  \"1M\": { label: \"Last Month\", days: 30 },\n  \"3M\": { label: \"Last 3 Months\", days: 90 },\n  \"6M\": { label: \"Last 6 Months\", days: 180 },\n  ALL: { label: \"All Time\", days: null },\n};\n\nexport function AccountChart({ transactions }) {\n  const [dateRange, setDateRange] = useState(\"1M\");\n\n  const filteredData = useMemo(() => {\n    const range = DATE_RANGES[dateRange];\n    const now = new Date();\n    const startDate = range.days\n      ? startOfDay(subDays(now, range.days))\n      : startOfDay(new Date(0));\n\n    // Filter transactions within date range\n    const filtered = transactions.filter(\n      (t) => new Date(t.date) >= startDate && new Date(t.date) <= endOfDay(now)\n    );\n\n    // Group transactions by date\n    const grouped = filtered.reduce((acc, transaction) => {\n      const date = format(new Date(transaction.date), \"MMM dd\");\n      if (!acc[date]) {\n        acc[date] = { date, income: 0, expense: 0 };\n      }\n      if (transaction.type === \"INCOME\") {\n        acc[date].income += transaction.amount;\n      } else {\n        acc[date].expense += transaction.amount;\n      }\n      return acc;\n    }, {});\n\n    // Convert to array and sort by date\n    return Object.values(grouped).sort(\n      (a, b) => new Date(a.date) - new Date(b.date)\n    );\n  }, [transactions, dateRange]);\n\n  // Calculate totals for the selected period\n  const totals = useMemo(() => {\n    return filteredData.reduce(\n      (acc, day) => ({\n        income: acc.income + day.income,\n        expense: acc.expense + day.expense,\n      }),\n      { income: 0, expense: 0 }\n    );\n  }, [filteredData]);\n\n  return (\n    <Card>\n      <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-7\">\n        <CardTitle className=\"text-base font-normal\">\n          Transaction Overview\n        </CardTitle>\n        <Select defaultValue={dateRange} onValueChange={setDateRange}>\n          <SelectTrigger className=\"w-[140px]\">\n            <SelectValue placeholder=\"Select range\" />\n          </SelectTrigger>\n          <SelectContent>\n            {Object.entries(DATE_RANGES).map(([key, { label }]) => (\n              <SelectItem key={key} value={key}>\n                {label}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex justify-around mb-6 text-sm\">\n          <div className=\"text-center\">\n            <p className=\"text-muted-foreground\">Total Income</p>\n            <p className=\"text-lg font-bold text-green-500\">\n              ${totals.income.toFixed(2)}\n            </p>\n          </div>\n          <div className=\"text-center\">\n            <p className=\"text-muted-foreground\">Total Expenses</p>\n            <p className=\"text-lg font-bold text-red-500\">\n              ${totals.expense.toFixed(2)}\n            </p>\n          </div>\n          <div className=\"text-center\">\n            <p className=\"text-muted-foreground\">Net</p>\n            <p\n              className={`text-lg font-bold ${\n                totals.income - totals.expense >= 0\n                  ? \"text-green-500\"\n                  : \"text-red-500\"\n              }`}\n            >\n              ${(totals.income - totals.expense).toFixed(2)}\n            </p>\n          </div>\n        </div>\n        <div className=\"h-[300px]\">\n          <ResponsiveContainer width=\"100%\" height=\"100%\">\n            <BarChart\n              data={filteredData}\n              margin={{ top: 10, right: 10, left: 10, bottom: 0 }}\n            >\n              <CartesianGrid strokeDasharray=\"3 3\" vertical={false} />\n              <XAxis\n                dataKey=\"date\"\n                fontSize={12}\n                tickLine={false}\n                axisLine={false}\n              />\n              <YAxis\n                fontSize={12}\n                tickLine={false}\n                axisLine={false}\n                tickFormatter={(value) => `$${value}`}\n              />\n              <Tooltip\n                formatter={(value) => [`$${value}`, undefined]}\n                contentStyle={{\n                  backgroundColor: \"hsl(var(--popover))\",\n                  border: \"1px solid hsl(var(--border))\",\n                  borderRadius: \"var(--radius)\",\n                }}\n              />\n              <Legend />\n              <Bar\n                dataKey=\"income\"\n                name=\"Income\"\n                fill=\"#22c55e\"\n                radius={[4, 4, 0, 0]}\n              />\n              <Bar\n                dataKey=\"expense\"\n                name=\"Expense\"\n                fill=\"#ef4444\"\n                radius={[4, 4, 0, 0]}\n              />\n            </BarChart>\n          </ResponsiveContainer>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n"
  },
  {
    "path": "app/(main)/account/_components/no-pagination-transaction-table.jsx",
    "content": "\"use client\";\n\nimport { useState, useEffect, useMemo } from \"react\";\nimport {\n  ChevronDown,\n  ChevronUp,\n  MoreHorizontal,\n  Trash,\n  Search,\n  X,\n  RefreshCw,\n  Clock,\n} from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport { toast } from \"sonner\";\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n  DropdownMenuSeparator,\n} from \"@/components/ui/dropdown-menu\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport { categoryColors } from \"@/data/categories\";\nimport { bulkDeleteTransactions } from \"@/actions/account\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { BarLoader } from \"react-spinners\";\nimport { useRouter } from \"next/navigation\";\n\nconst RECURRING_INTERVALS = {\n  DAILY: \"Daily\",\n  WEEKLY: \"Weekly\",\n  MONTHLY: \"Monthly\",\n  YEARLY: \"Yearly\",\n};\n\nexport function NoPaginationTransactionTable({ transactions }) {\n  const [selectedIds, setSelectedIds] = useState([]);\n  const [sortConfig, setSortConfig] = useState({\n    field: \"date\",\n    direction: \"desc\",\n  });\n  const [searchTerm, setSearchTerm] = useState(\"\");\n  const [typeFilter, setTypeFilter] = useState(\"\");\n  const [recurringFilter, setRecurringFilter] = useState(\"\");\n  const router = useRouter();\n\n  // Memoized filtered and sorted transactions\n  const filteredAndSortedTransactions = useMemo(() => {\n    let result = [...transactions];\n\n    // Apply search filter\n    if (searchTerm) {\n      const searchLower = searchTerm.toLowerCase();\n      result = result.filter((transaction) =>\n        transaction.description?.toLowerCase().includes(searchLower)\n      );\n    }\n\n    // Apply type filter\n    if (typeFilter) {\n      result = result.filter((transaction) => transaction.type === typeFilter);\n    }\n\n    // Apply recurring filter\n    if (recurringFilter) {\n      result = result.filter((transaction) => {\n        if (recurringFilter === \"recurring\") return transaction.isRecurring;\n        return !transaction.isRecurring;\n      });\n    }\n\n    // Apply sorting\n    result.sort((a, b) => {\n      let comparison = 0;\n\n      switch (sortConfig.field) {\n        case \"date\":\n          comparison = new Date(a.date) - new Date(b.date);\n          break;\n        case \"amount\":\n          comparison = a.amount - b.amount;\n          break;\n        case \"category\":\n          comparison = a.category.localeCompare(b.category);\n          break;\n        default:\n          comparison = 0;\n      }\n\n      return sortConfig.direction === \"asc\" ? comparison : -comparison;\n    });\n\n    return result;\n  }, [transactions, searchTerm, typeFilter, recurringFilter, sortConfig]);\n\n  const handleSort = (field) => {\n    setSortConfig((current) => ({\n      field,\n      direction:\n        current.field === field && current.direction === \"asc\" ? \"desc\" : \"asc\",\n    }));\n  };\n\n  const handleSelect = (id) => {\n    setSelectedIds((current) =>\n      current.includes(id)\n        ? current.filter((item) => item !== id)\n        : [...current, id]\n    );\n  };\n\n  const handleSelectAll = () => {\n    setSelectedIds((current) =>\n      current.length === filteredAndSortedTransactions.length\n        ? []\n        : filteredAndSortedTransactions.map((t) => t.id)\n    );\n  };\n\n  const {\n    loading: deleteLoading,\n    fn: deleteFn,\n    data: deleted,\n  } = useFetch(bulkDeleteTransactions);\n\n  const handleBulkDelete = async () => {\n    if (\n      !window.confirm(\n        `Are you sure you want to delete ${selectedIds.length} transactions?`\n      )\n    )\n      return;\n\n    deleteFn(selectedIds);\n  };\n\n  useEffect(() => {\n    if (deleted && !deleteLoading) {\n      toast.error(\"Transactions deleted successfully\");\n    }\n  }, [deleted, deleteLoading]);\n\n  const handleClearFilters = () => {\n    setSearchTerm(\"\");\n    setTypeFilter(\"\");\n    setRecurringFilter(\"\");\n    setSelectedIds([]);\n  };\n\n  return (\n    <div className=\"space-y-4\">\n      {deleteLoading && (\n        <BarLoader className=\"mt-4\" width={\"100%\"} color=\"#9333ea\" />\n      )}\n      {/* Filters */}\n      <div className=\"flex flex-col sm:flex-row gap-4\">\n        <div className=\"relative flex-1\">\n          <Search className=\"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n          <Input\n            placeholder=\"Search transactions...\"\n            value={searchTerm}\n            onChange={(e) => setSearchTerm(e.target.value)}\n            className=\"pl-8\"\n          />\n        </div>\n        <div className=\"flex gap-2\">\n          <Select value={typeFilter} onValueChange={setTypeFilter}>\n            <SelectTrigger>\n              <SelectValue placeholder=\"All Types\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"INCOME\">Income</SelectItem>\n              <SelectItem value=\"EXPENSE\">Expense</SelectItem>\n            </SelectContent>\n          </Select>\n\n          <Select\n            value={recurringFilter}\n            onValueChange={(value) => {\n              setRecurringFilter(value);\n            }}\n          >\n            <SelectTrigger className=\"w-[130px]\">\n              <SelectValue placeholder=\"All Transactions\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"recurring\">Recurring Only</SelectItem>\n              <SelectItem value=\"non-recurring\">Non-recurring Only</SelectItem>\n            </SelectContent>\n          </Select>\n\n          {/* Bulk Actions */}\n          {selectedIds.length > 0 && (\n            <div className=\"flex items-center gap-2\">\n              <Button\n                variant=\"destructive\"\n                size=\"sm\"\n                onClick={handleBulkDelete}\n              >\n                <Trash className=\"h-4 w-4 mr-2\" />\n                Delete Selected ({selectedIds.length})\n              </Button>\n            </div>\n          )}\n\n          {(searchTerm || typeFilter || recurringFilter) && (\n            <Button\n              variant=\"outline\"\n              size=\"icon\"\n              onClick={handleClearFilters}\n              title=\"Clear filters\"\n            >\n              <X className=\"h-4 w-5\" />\n            </Button>\n          )}\n        </div>\n      </div>\n\n      {/* Transactions Table */}\n      <div className=\"rounded-md border\">\n        <Table>\n          <TableHeader>\n            <TableRow>\n              <TableHead className=\"w-[50px]\">\n                <Checkbox\n                  checked={\n                    selectedIds.length ===\n                      filteredAndSortedTransactions.length &&\n                    filteredAndSortedTransactions.length > 0\n                  }\n                  onCheckedChange={handleSelectAll}\n                />\n              </TableHead>\n              <TableHead\n                className=\"cursor-pointer\"\n                onClick={() => handleSort(\"date\")}\n              >\n                <div className=\"flex items-center\">\n                  Date\n                  {sortConfig.field === \"date\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead>Description</TableHead>\n              <TableHead\n                className=\"cursor-pointer\"\n                onClick={() => handleSort(\"category\")}\n              >\n                <div className=\"flex items-center\">\n                  Category\n                  {sortConfig.field === \"category\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead\n                className=\"cursor-pointer text-right\"\n                onClick={() => handleSort(\"amount\")}\n              >\n                <div className=\"flex items-center justify-end\">\n                  Amount\n                  {sortConfig.field === \"amount\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead>Recurring</TableHead>\n              <TableHead className=\"w-[50px]\" />\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {filteredAndSortedTransactions.length === 0 ? (\n              <TableRow>\n                <TableCell\n                  colSpan={7}\n                  className=\"text-center text-muted-foreground\"\n                >\n                  No transactions found\n                </TableCell>\n              </TableRow>\n            ) : (\n              filteredAndSortedTransactions.map((transaction) => (\n                <TableRow key={transaction.id}>\n                  <TableCell>\n                    <Checkbox\n                      checked={selectedIds.includes(transaction.id)}\n                      onCheckedChange={() => handleSelect(transaction.id)}\n                    />\n                  </TableCell>\n                  <TableCell>\n                    {format(new Date(transaction.date), \"PP\")}\n                  </TableCell>\n                  <TableCell>{transaction.description}</TableCell>\n                  <TableCell className=\"capitalize\">\n                    <span\n                      style={{\n                        background: categoryColors[transaction.category],\n                      }}\n                      className=\"px-2 py-1 rounded text-white text-sm\"\n                    >\n                      {transaction.category}\n                    </span>\n                  </TableCell>\n                  <TableCell\n                    className={cn(\n                      \"text-right font-medium\",\n                      transaction.type === \"EXPENSE\"\n                        ? \"text-red-500\"\n                        : \"text-green-500\"\n                    )}\n                  >\n                    {transaction.type === \"EXPENSE\" ? \"-\" : \"+\"}$\n                    {transaction.amount.toFixed(2)}\n                  </TableCell>\n                  <TableCell>\n                    {transaction.isRecurring ? (\n                      <TooltipProvider>\n                        <Tooltip>\n                          <TooltipTrigger>\n                            <Badge\n                              variant=\"secondary\"\n                              className=\"gap-1 bg-purple-100 text-purple-700 hover:bg-purple-200\"\n                            >\n                              <RefreshCw className=\"h-3 w-3\" />\n                              {\n                                RECURRING_INTERVALS[\n                                  transaction.recurringInterval\n                                ]\n                              }\n                            </Badge>\n                          </TooltipTrigger>\n                          <TooltipContent>\n                            <div className=\"text-sm\">\n                              <div className=\"font-medium\">Next Date:</div>\n                              <div>\n                                {format(\n                                  new Date(transaction.nextRecurringDate),\n                                  \"PPP\"\n                                )}\n                              </div>\n                            </div>\n                          </TooltipContent>\n                        </Tooltip>\n                      </TooltipProvider>\n                    ) : (\n                      <Badge variant=\"outline\" className=\"gap-1\">\n                        <Clock className=\"h-3 w-3\" />\n                        One-time\n                      </Badge>\n                    )}\n                  </TableCell>\n                  <TableCell>\n                    <DropdownMenu>\n                      <DropdownMenuTrigger asChild>\n                        <Button variant=\"ghost\" className=\"h-8 w-8 p-0\">\n                          <MoreHorizontal className=\"h-4 w-4\" />\n                        </Button>\n                      </DropdownMenuTrigger>\n                      <DropdownMenuContent align=\"end\">\n                        <DropdownMenuItem\n                          onClick={() =>\n                            router.push(\n                              `/transaction/create?edit=${transaction.id}`\n                            )\n                          }\n                        >\n                          Edit\n                        </DropdownMenuItem>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem\n                          className=\"text-destructive\"\n                          onClick={() => deleteFn([transaction.id])}\n                        >\n                          Delete\n                        </DropdownMenuItem>\n                      </DropdownMenuContent>\n                    </DropdownMenu>\n                  </TableCell>\n                </TableRow>\n              ))\n            )}\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/account/_components/transaction-table.jsx",
    "content": "\"use client\";\n\nimport { useState, useEffect, useMemo } from \"react\";\nimport {\n  ChevronDown,\n  ChevronUp,\n  MoreHorizontal,\n  Trash,\n  Search,\n  X,\n  ChevronLeft,\n  ChevronRight,\n  RefreshCw,\n  Clock,\n} from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport { toast } from \"sonner\";\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n  DropdownMenuSeparator,\n} from \"@/components/ui/dropdown-menu\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport { categoryColors } from \"@/data/categories\";\nimport { bulkDeleteTransactions } from \"@/actions/account\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { BarLoader } from \"react-spinners\";\nimport { useRouter } from \"next/navigation\";\n\nconst ITEMS_PER_PAGE = 10;\n\nconst RECURRING_INTERVALS = {\n  DAILY: \"Daily\",\n  WEEKLY: \"Weekly\",\n  MONTHLY: \"Monthly\",\n  YEARLY: \"Yearly\",\n};\n\nexport function TransactionTable({ transactions }) {\n  const [selectedIds, setSelectedIds] = useState([]);\n  const [sortConfig, setSortConfig] = useState({\n    field: \"date\",\n    direction: \"desc\",\n  });\n  const [searchTerm, setSearchTerm] = useState(\"\");\n  const [typeFilter, setTypeFilter] = useState(\"\");\n  const [recurringFilter, setRecurringFilter] = useState(\"\");\n  const [currentPage, setCurrentPage] = useState(1);\n  const router = useRouter();\n\n  // Memoized filtered and sorted transactions\n  const filteredAndSortedTransactions = useMemo(() => {\n    let result = [...transactions];\n\n    // Apply search filter\n    if (searchTerm) {\n      const searchLower = searchTerm.toLowerCase();\n      result = result.filter((transaction) =>\n        transaction.description?.toLowerCase().includes(searchLower)\n      );\n    }\n\n    // Apply type filter\n    if (typeFilter) {\n      result = result.filter((transaction) => transaction.type === typeFilter);\n    }\n\n    // Apply recurring filter\n    if (recurringFilter) {\n      result = result.filter((transaction) => {\n        if (recurringFilter === \"recurring\") return transaction.isRecurring;\n        return !transaction.isRecurring;\n      });\n    }\n\n    // Apply sorting\n    result.sort((a, b) => {\n      let comparison = 0;\n\n      switch (sortConfig.field) {\n        case \"date\":\n          comparison = new Date(a.date) - new Date(b.date);\n          break;\n        case \"amount\":\n          comparison = a.amount - b.amount;\n          break;\n        case \"category\":\n          comparison = a.category.localeCompare(b.category);\n          break;\n        default:\n          comparison = 0;\n      }\n\n      return sortConfig.direction === \"asc\" ? comparison : -comparison;\n    });\n\n    return result;\n  }, [transactions, searchTerm, typeFilter, recurringFilter, sortConfig]);\n\n  // Pagination calculations\n  const totalPages = Math.ceil(\n    filteredAndSortedTransactions.length / ITEMS_PER_PAGE\n  );\n  const paginatedTransactions = useMemo(() => {\n    const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;\n    return filteredAndSortedTransactions.slice(\n      startIndex,\n      startIndex + ITEMS_PER_PAGE\n    );\n  }, [filteredAndSortedTransactions, currentPage]);\n\n  const handleSort = (field) => {\n    setSortConfig((current) => ({\n      field,\n      direction:\n        current.field === field && current.direction === \"asc\" ? \"desc\" : \"asc\",\n    }));\n  };\n\n  const handleSelect = (id) => {\n    setSelectedIds((current) =>\n      current.includes(id)\n        ? current.filter((item) => item !== id)\n        : [...current, id]\n    );\n  };\n\n  const handleSelectAll = () => {\n    setSelectedIds((current) =>\n      current.length === paginatedTransactions.length\n        ? []\n        : paginatedTransactions.map((t) => t.id)\n    );\n  };\n\n  const {\n    loading: deleteLoading,\n    fn: deleteFn,\n    data: deleted,\n  } = useFetch(bulkDeleteTransactions);\n\n  const handleBulkDelete = async () => {\n    if (\n      !window.confirm(\n        `Are you sure you want to delete ${selectedIds.length} transactions?`\n      )\n    )\n      return;\n\n    deleteFn(selectedIds);\n  };\n\n  useEffect(() => {\n    if (deleted && !deleteLoading) {\n      toast.error(\"Transactions deleted successfully\");\n    }\n  }, [deleted, deleteLoading]);\n\n  const handleClearFilters = () => {\n    setSearchTerm(\"\");\n    setTypeFilter(\"\");\n    setRecurringFilter(\"\");\n    setCurrentPage(1);\n  };\n\n  const handlePageChange = (newPage) => {\n    setCurrentPage(newPage);\n    setSelectedIds([]); // Clear selections on page change\n  };\n\n  return (\n    <div className=\"space-y-4\">\n      {deleteLoading && (\n        <BarLoader className=\"mt-4\" width={\"100%\"} color=\"#9333ea\" />\n      )}\n      {/* Filters */}\n      <div className=\"flex flex-col sm:flex-row gap-4\">\n        <div className=\"relative flex-1\">\n          <Search className=\"absolute left-2 top-2.5 h-4 w-4 text-muted-foreground\" />\n          <Input\n            placeholder=\"Search transactions...\"\n            value={searchTerm}\n            onChange={(e) => {\n              setSearchTerm(e.target.value);\n              setCurrentPage(1);\n            }}\n            className=\"pl-8\"\n          />\n        </div>\n        <div className=\"flex gap-2\">\n          <Select\n            value={typeFilter}\n            onValueChange={(value) => {\n              setTypeFilter(value);\n              setCurrentPage(1);\n            }}\n          >\n            <SelectTrigger className=\"w-[130px]\">\n              <SelectValue placeholder=\"All Types\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"INCOME\">Income</SelectItem>\n              <SelectItem value=\"EXPENSE\">Expense</SelectItem>\n            </SelectContent>\n          </Select>\n\n          <Select\n            value={recurringFilter}\n            onValueChange={(value) => {\n              setRecurringFilter(value);\n              setCurrentPage(1);\n            }}\n          >\n            <SelectTrigger className=\"w-[130px]\">\n              <SelectValue placeholder=\"All Transactions\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"recurring\">Recurring Only</SelectItem>\n              <SelectItem value=\"non-recurring\">Non-recurring Only</SelectItem>\n            </SelectContent>\n          </Select>\n\n          {/* Bulk Actions */}\n          {selectedIds.length > 0 && (\n            <div className=\"flex items-center gap-2\">\n              <Button\n                variant=\"destructive\"\n                size=\"sm\"\n                onClick={handleBulkDelete}\n              >\n                <Trash className=\"h-4 w-4 mr-2\" />\n                Delete Selected ({selectedIds.length})\n              </Button>\n            </div>\n          )}\n\n          {(searchTerm || typeFilter || recurringFilter) && (\n            <Button\n              variant=\"outline\"\n              size=\"icon\"\n              onClick={handleClearFilters}\n              title=\"Clear filters\"\n            >\n              <X className=\"h-4 w-5\" />\n            </Button>\n          )}\n        </div>\n      </div>\n\n      {/* Transactions Table */}\n      <div className=\"rounded-md border\">\n        <Table>\n          <TableHeader>\n            <TableRow>\n              <TableHead className=\"w-[50px]\">\n                <Checkbox\n                  checked={\n                    selectedIds.length === paginatedTransactions.length &&\n                    paginatedTransactions.length > 0\n                  }\n                  onCheckedChange={handleSelectAll}\n                />\n              </TableHead>\n              <TableHead\n                className=\"cursor-pointer\"\n                onClick={() => handleSort(\"date\")}\n              >\n                <div className=\"flex items-center\">\n                  Date\n                  {sortConfig.field === \"date\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead>Description</TableHead>\n              <TableHead\n                className=\"cursor-pointer\"\n                onClick={() => handleSort(\"category\")}\n              >\n                <div className=\"flex items-center\">\n                  Category\n                  {sortConfig.field === \"category\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead\n                className=\"cursor-pointer text-right\"\n                onClick={() => handleSort(\"amount\")}\n              >\n                <div className=\"flex items-center justify-end\">\n                  Amount\n                  {sortConfig.field === \"amount\" &&\n                    (sortConfig.direction === \"asc\" ? (\n                      <ChevronUp className=\"ml-1 h-4 w-4\" />\n                    ) : (\n                      <ChevronDown className=\"ml-1 h-4 w-4\" />\n                    ))}\n                </div>\n              </TableHead>\n              <TableHead>Recurring</TableHead>\n              <TableHead className=\"w-[50px]\" />\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {paginatedTransactions.length === 0 ? (\n              <TableRow>\n                <TableCell\n                  colSpan={7}\n                  className=\"text-center text-muted-foreground\"\n                >\n                  No transactions found\n                </TableCell>\n              </TableRow>\n            ) : (\n              paginatedTransactions.map((transaction) => (\n                <TableRow key={transaction.id}>\n                  <TableCell>\n                    <Checkbox\n                      checked={selectedIds.includes(transaction.id)}\n                      onCheckedChange={() => handleSelect(transaction.id)}\n                    />\n                  </TableCell>\n                  <TableCell>\n                    {format(new Date(transaction.date), \"PP\")}\n                  </TableCell>\n                  <TableCell>{transaction.description}</TableCell>\n                  <TableCell className=\"capitalize\">\n                    <span\n                      style={{\n                        background: categoryColors[transaction.category],\n                      }}\n                      className=\"px-2 py-1 rounded text-white text-sm\"\n                    >\n                      {transaction.category}\n                    </span>\n                  </TableCell>\n                  <TableCell\n                    className={cn(\n                      \"text-right font-medium\",\n                      transaction.type === \"EXPENSE\"\n                        ? \"text-red-500\"\n                        : \"text-green-500\"\n                    )}\n                  >\n                    {transaction.type === \"EXPENSE\" ? \"-\" : \"+\"}$\n                    {transaction.amount.toFixed(2)}\n                  </TableCell>\n                  <TableCell>\n                    {transaction.isRecurring ? (\n                      <TooltipProvider>\n                        <Tooltip>\n                          <TooltipTrigger>\n                            <Badge\n                              variant=\"secondary\"\n                              className=\"gap-1 bg-purple-100 text-purple-700 hover:bg-purple-200\"\n                            >\n                              <RefreshCw className=\"h-3 w-3\" />\n                              {\n                                RECURRING_INTERVALS[\n                                  transaction.recurringInterval\n                                ]\n                              }\n                            </Badge>\n                          </TooltipTrigger>\n                          <TooltipContent>\n                            <div className=\"text-sm\">\n                              <div className=\"font-medium\">Next Date:</div>\n                              <div>\n                                {format(\n                                  new Date(transaction.nextRecurringDate),\n                                  \"PPP\"\n                                )}\n                              </div>\n                            </div>\n                          </TooltipContent>\n                        </Tooltip>\n                      </TooltipProvider>\n                    ) : (\n                      <Badge variant=\"outline\" className=\"gap-1\">\n                        <Clock className=\"h-3 w-3\" />\n                        One-time\n                      </Badge>\n                    )}\n                  </TableCell>\n                  <TableCell>\n                    <DropdownMenu>\n                      <DropdownMenuTrigger asChild>\n                        <Button variant=\"ghost\" className=\"h-8 w-8 p-0\">\n                          <MoreHorizontal className=\"h-4 w-4\" />\n                        </Button>\n                      </DropdownMenuTrigger>\n                      <DropdownMenuContent align=\"end\">\n                        <DropdownMenuItem\n                          onClick={() =>\n                            router.push(\n                              `/transaction/create?edit=${transaction.id}`\n                            )\n                          }\n                        >\n                          Edit\n                        </DropdownMenuItem>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem\n                          className=\"text-destructive\"\n                          onClick={() => deleteFn([transaction.id])}\n                        >\n                          Delete\n                        </DropdownMenuItem>\n                      </DropdownMenuContent>\n                    </DropdownMenu>\n                  </TableCell>\n                </TableRow>\n              ))\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      {/* Pagination */}\n      {totalPages > 1 && (\n        <div className=\"flex items-center justify-center gap-2\">\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            onClick={() => handlePageChange(currentPage - 1)}\n            disabled={currentPage === 1}\n          >\n            <ChevronLeft className=\"h-4 w-4\" />\n          </Button>\n          <span className=\"text-sm\">\n            Page {currentPage} of {totalPages}\n          </span>\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            onClick={() => handlePageChange(currentPage + 1)}\n            disabled={currentPage === totalPages}\n          >\n            <ChevronRight className=\"h-4 w-4\" />\n          </Button>\n        </div>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/dashboard/_components/account-card.jsx",
    "content": "\"use client\";\n\nimport { ArrowUpRight, ArrowDownRight, CreditCard } from \"lucide-react\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { useEffect } from \"react\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport {\n  Card,\n  CardContent,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\";\nimport Link from \"next/link\";\nimport { updateDefaultAccount } from \"@/actions/account\";\nimport { toast } from \"sonner\";\n\nexport function AccountCard({ account }) {\n  const { name, type, balance, id, isDefault } = account;\n\n  const {\n    loading: updateDefaultLoading,\n    fn: updateDefaultFn,\n    data: updatedAccount,\n    error,\n  } = useFetch(updateDefaultAccount);\n\n  const handleDefaultChange = async (event) => {\n    event.preventDefault(); // Prevent navigation\n\n    if (isDefault) {\n      toast.warning(\"You need atleast 1 default account\");\n      return; // Don't allow toggling off the default account\n    }\n\n    await updateDefaultFn(id);\n  };\n\n  useEffect(() => {\n    if (updatedAccount?.success) {\n      toast.success(\"Default account updated successfully\");\n    }\n  }, [updatedAccount]);\n\n  useEffect(() => {\n    if (error) {\n      toast.error(error.message || \"Failed to update default account\");\n    }\n  }, [error]);\n\n  return (\n    <Card className=\"hover:shadow-md transition-shadow group relative\">\n      <Link href={`/account/${id}`}>\n        <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n          <CardTitle className=\"text-sm font-medium capitalize\">\n            {name}\n          </CardTitle>\n          <Switch\n            checked={isDefault}\n            onClick={handleDefaultChange}\n            disabled={updateDefaultLoading}\n          />\n        </CardHeader>\n        <CardContent>\n          <div className=\"text-2xl font-bold\">\n            ${parseFloat(balance).toFixed(2)}\n          </div>\n          <p className=\"text-xs text-muted-foreground\">\n            {type.charAt(0) + type.slice(1).toLowerCase()} Account\n          </p>\n        </CardContent>\n        <CardFooter className=\"flex justify-between text-sm text-muted-foreground\">\n          <div className=\"flex items-center\">\n            <ArrowUpRight className=\"mr-1 h-4 w-4 text-green-500\" />\n            Income\n          </div>\n          <div className=\"flex items-center\">\n            <ArrowDownRight className=\"mr-1 h-4 w-4 text-red-500\" />\n            Expense\n          </div>\n        </CardFooter>\n      </Link>\n    </Card>\n  );\n}\n"
  },
  {
    "path": "app/(main)/dashboard/_components/budget-progress.jsx",
    "content": "\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { Pencil, Check, X } from \"lucide-react\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { toast } from \"sonner\";\n\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\";\nimport { Progress } from \"@/components/ui/progress\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { updateBudget } from \"@/actions/budget\";\n\nexport function BudgetProgress({ initialBudget, currentExpenses }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const [newBudget, setNewBudget] = useState(\n    initialBudget?.amount?.toString() || \"\"\n  );\n\n  const {\n    loading: isLoading,\n    fn: updateBudgetFn,\n    data: updatedBudget,\n    error,\n  } = useFetch(updateBudget);\n\n  const percentUsed = initialBudget\n    ? (currentExpenses / initialBudget.amount) * 100\n    : 0;\n\n  const handleUpdateBudget = async () => {\n    const amount = parseFloat(newBudget);\n\n    if (isNaN(amount) || amount <= 0) {\n      toast.error(\"Please enter a valid amount\");\n      return;\n    }\n\n    await updateBudgetFn(amount);\n  };\n\n  const handleCancel = () => {\n    setNewBudget(initialBudget?.amount?.toString() || \"\");\n    setIsEditing(false);\n  };\n\n  useEffect(() => {\n    if (updatedBudget?.success) {\n      setIsEditing(false);\n      toast.success(\"Budget updated successfully\");\n    }\n  }, [updatedBudget]);\n\n  useEffect(() => {\n    if (error) {\n      toast.error(error.message || \"Failed to update budget\");\n    }\n  }, [error]);\n\n  return (\n    <Card>\n      <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <div className=\"flex-1\">\n          <CardTitle className=\"text-sm font-medium\">\n            Monthly Budget (Default Account)\n          </CardTitle>\n          <div className=\"flex items-center gap-2 mt-1\">\n            {isEditing ? (\n              <div className=\"flex items-center gap-2\">\n                <Input\n                  type=\"number\"\n                  value={newBudget}\n                  onChange={(e) => setNewBudget(e.target.value)}\n                  className=\"w-32\"\n                  placeholder=\"Enter amount\"\n                  autoFocus\n                  disabled={isLoading}\n                />\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  onClick={handleUpdateBudget}\n                  disabled={isLoading}\n                >\n                  <Check className=\"h-4 w-4 text-green-500\" />\n                </Button>\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  onClick={handleCancel}\n                  disabled={isLoading}\n                >\n                  <X className=\"h-4 w-4 text-red-500\" />\n                </Button>\n              </div>\n            ) : (\n              <>\n                <CardDescription>\n                  {initialBudget\n                    ? `$${currentExpenses.toFixed(\n                        2\n                      )} of $${initialBudget.amount.toFixed(2)} spent`\n                    : \"No budget set\"}\n                </CardDescription>\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  onClick={() => setIsEditing(true)}\n                  className=\"h-6 w-6\"\n                >\n                  <Pencil className=\"h-3 w-3\" />\n                </Button>\n              </>\n            )}\n          </div>\n        </div>\n      </CardHeader>\n      <CardContent>\n        {initialBudget && (\n          <div className=\"space-y-2\">\n            <Progress\n              value={percentUsed}\n              extraStyles={`${\n                // add to Progress component\n                percentUsed >= 90\n                  ? \"bg-red-500\"\n                  : percentUsed >= 75\n                    ? \"bg-yellow-500\"\n                    : \"bg-green-500\"\n              }`}\n            />\n            <p className=\"text-xs text-muted-foreground text-right\">\n              {percentUsed.toFixed(1)}% used\n            </p>\n          </div>\n        )}\n      </CardContent>\n    </Card>\n  );\n}\n"
  },
  {
    "path": "app/(main)/dashboard/_components/transaction-overview.jsx",
    "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport {\n  PieChart,\n  Pie,\n  Cell,\n  ResponsiveContainer,\n  Tooltip,\n  Legend,\n} from \"recharts\";\nimport { format } from \"date-fns\";\nimport { ArrowUpRight, ArrowDownRight } from \"lucide-react\";\n\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\n\nconst COLORS = [\n  \"#FF6B6B\",\n  \"#4ECDC4\",\n  \"#45B7D1\",\n  \"#96CEB4\",\n  \"#FFEEAD\",\n  \"#D4A5A5\",\n  \"#9FA8DA\",\n];\n\nexport function DashboardOverview({ accounts, transactions }) {\n  const [selectedAccountId, setSelectedAccountId] = useState(\n    accounts.find((a) => a.isDefault)?.id || accounts[0]?.id\n  );\n\n  // Filter transactions for selected account\n  const accountTransactions = transactions.filter(\n    (t) => t.accountId === selectedAccountId\n  );\n\n  // Get recent transactions (last 5)\n  const recentTransactions = accountTransactions\n    .sort((a, b) => new Date(b.date) - new Date(a.date))\n    .slice(0, 5);\n\n  // Calculate expense breakdown for current month\n  const currentDate = new Date();\n  const currentMonthExpenses = accountTransactions.filter((t) => {\n    const transactionDate = new Date(t.date);\n    return (\n      t.type === \"EXPENSE\" &&\n      transactionDate.getMonth() === currentDate.getMonth() &&\n      transactionDate.getFullYear() === currentDate.getFullYear()\n    );\n  });\n\n  // Group expenses by category\n  const expensesByCategory = currentMonthExpenses.reduce((acc, transaction) => {\n    const category = transaction.category;\n    if (!acc[category]) {\n      acc[category] = 0;\n    }\n    acc[category] += transaction.amount;\n    return acc;\n  }, {});\n\n  // Format data for pie chart\n  const pieChartData = Object.entries(expensesByCategory).map(\n    ([category, amount]) => ({\n      name: category,\n      value: amount,\n    })\n  );\n\n  return (\n    <div className=\"grid gap-4 md:grid-cols-2\">\n      {/* Recent Transactions Card */}\n      <Card>\n        <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-4\">\n          <CardTitle className=\"text-base font-normal\">\n            Recent Transactions\n          </CardTitle>\n          <Select\n            value={selectedAccountId}\n            onValueChange={setSelectedAccountId}\n          >\n            <SelectTrigger className=\"w-[140px]\">\n              <SelectValue placeholder=\"Select account\" />\n            </SelectTrigger>\n            <SelectContent>\n              {accounts.map((account) => (\n                <SelectItem key={account.id} value={account.id}>\n                  {account.name}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n        </CardHeader>\n        <CardContent>\n          <div className=\"space-y-4\">\n            {recentTransactions.length === 0 ? (\n              <p className=\"text-center text-muted-foreground py-4\">\n                No recent transactions\n              </p>\n            ) : (\n              recentTransactions.map((transaction) => (\n                <div\n                  key={transaction.id}\n                  className=\"flex items-center justify-between\"\n                >\n                  <div className=\"space-y-1\">\n                    <p className=\"text-sm font-medium leading-none\">\n                      {transaction.description || \"Untitled Transaction\"}\n                    </p>\n                    <p className=\"text-sm text-muted-foreground\">\n                      {format(new Date(transaction.date), \"PP\")}\n                    </p>\n                  </div>\n                  <div className=\"flex items-center gap-2\">\n                    <div\n                      className={cn(\n                        \"flex items-center\",\n                        transaction.type === \"EXPENSE\"\n                          ? \"text-red-500\"\n                          : \"text-green-500\"\n                      )}\n                    >\n                      {transaction.type === \"EXPENSE\" ? (\n                        <ArrowDownRight className=\"mr-1 h-4 w-4\" />\n                      ) : (\n                        <ArrowUpRight className=\"mr-1 h-4 w-4\" />\n                      )}\n                      ${transaction.amount.toFixed(2)}\n                    </div>\n                  </div>\n                </div>\n              ))\n            )}\n          </div>\n        </CardContent>\n      </Card>\n\n      {/* Expense Breakdown Card */}\n      <Card>\n        <CardHeader>\n          <CardTitle className=\"text-base font-normal\">\n            Monthly Expense Breakdown\n          </CardTitle>\n        </CardHeader>\n        <CardContent className=\"p-0 pb-5\">\n          {pieChartData.length === 0 ? (\n            <p className=\"text-center text-muted-foreground py-4\">\n              No expenses this month\n            </p>\n          ) : (\n            <div className=\"h-[300px]\">\n              <ResponsiveContainer width=\"100%\" height=\"100%\">\n                <PieChart>\n                  <Pie\n                    data={pieChartData}\n                    cx=\"50%\"\n                    cy=\"50%\"\n                    outerRadius={80}\n                    fill=\"#8884d8\"\n                    dataKey=\"value\"\n                    label={({ name, value }) => `${name}: $${value.toFixed(2)}`}\n                  >\n                    {pieChartData.map((entry, index) => (\n                      <Cell\n                        key={`cell-${index}`}\n                        fill={COLORS[index % COLORS.length]}\n                      />\n                    ))}\n                  </Pie>\n                  <Tooltip\n                    formatter={(value) => `$${value.toFixed(2)}`}\n                    contentStyle={{\n                      backgroundColor: \"hsl(var(--popover))\",\n                      border: \"1px solid hsl(var(--border))\",\n                      borderRadius: \"var(--radius)\",\n                    }}\n                  />\n                  <Legend />\n                </PieChart>\n              </ResponsiveContainer>\n            </div>\n          )}\n        </CardContent>\n      </Card>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/dashboard/layout.js",
    "content": "import DashboardPage from \"./page\";\nimport { BarLoader } from \"react-spinners\";\nimport { Suspense } from \"react\";\n\nexport default function Layout() {\n  return (\n    <div className=\"px-5\">\n      <div className=\"flex items-center justify-between mb-5\">\n        <h1 className=\"text-6xl font-bold tracking-tight gradient-title\">\n          Dashboard\n        </h1>\n      </div>\n      <Suspense\n        fallback={<BarLoader className=\"mt-4\" width={\"100%\"} color=\"#9333ea\" />}\n      >\n        <DashboardPage />\n      </Suspense>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/dashboard/page.jsx",
    "content": "import { Suspense } from \"react\";\nimport { getUserAccounts } from \"@/actions/dashboard\";\nimport { getDashboardData } from \"@/actions/dashboard\";\nimport { getCurrentBudget } from \"@/actions/budget\";\nimport { AccountCard } from \"./_components/account-card\";\nimport { CreateAccountDrawer } from \"@/components/create-account-drawer\";\nimport { BudgetProgress } from \"./_components/budget-progress\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Plus } from \"lucide-react\";\nimport { DashboardOverview } from \"./_components/transaction-overview\";\n\nexport default async function DashboardPage() {\n  const [accounts, transactions] = await Promise.all([\n    getUserAccounts(),\n    getDashboardData(),\n  ]);\n\n  const defaultAccount = accounts?.find((account) => account.isDefault);\n\n  // Get budget for default account\n  let budgetData = null;\n  if (defaultAccount) {\n    budgetData = await getCurrentBudget(defaultAccount.id);\n  }\n\n  return (\n    <div className=\"space-y-8\">\n      {/* Budget Progress */}\n      <BudgetProgress\n        initialBudget={budgetData?.budget}\n        currentExpenses={budgetData?.currentExpenses || 0}\n      />\n\n      {/* Dashboard Overview */}\n      <DashboardOverview\n        accounts={accounts}\n        transactions={transactions || []}\n      />\n\n      {/* Accounts Grid */}\n      <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n        <CreateAccountDrawer>\n          <Card className=\"hover:shadow-md transition-shadow cursor-pointer border-dashed\">\n            <CardContent className=\"flex flex-col items-center justify-center text-muted-foreground h-full pt-5\">\n              <Plus className=\"h-10 w-10 mb-2\" />\n              <p className=\"text-sm font-medium\">Add New Account</p>\n            </CardContent>\n          </Card>\n        </CreateAccountDrawer>\n        {accounts.length > 0 &&\n          accounts?.map((account) => (\n            <AccountCard key={account.id} account={account} />\n          ))}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/layout.js",
    "content": "import React from \"react\";\n\nconst MainLayout = ({ children }) => {\n  return <div className=\"container mx-auto my-32\">{children}</div>;\n};\n\nexport default MainLayout;\n"
  },
  {
    "path": "app/(main)/transaction/_components/recipt-scanner.jsx",
    "content": "\"use client\";\n\nimport { useRef, useEffect } from \"react\";\nimport { Camera, Loader2 } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { toast } from \"sonner\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { scanReceipt } from \"@/actions/transaction\";\n\nexport function ReceiptScanner({ onScanComplete }) {\n  const fileInputRef = useRef(null);\n\n  const {\n    loading: scanReceiptLoading,\n    fn: scanReceiptFn,\n    data: scannedData,\n  } = useFetch(scanReceipt);\n\n  const handleReceiptScan = async (file) => {\n    if (file.size > 5 * 1024 * 1024) {\n      toast.error(\"File size should be less than 5MB\");\n      return;\n    }\n\n    await scanReceiptFn(file);\n  };\n\n  useEffect(() => {\n    if (scannedData && !scanReceiptLoading) {\n      onScanComplete(scannedData);\n      toast.success(\"Receipt scanned successfully\");\n    }\n  }, [scanReceiptLoading, scannedData]);\n\n  return (\n    <div className=\"flex items-center gap-4\">\n      <input\n        type=\"file\"\n        ref={fileInputRef}\n        className=\"hidden\"\n        accept=\"image/*\"\n        capture=\"environment\"\n        onChange={(e) => {\n          const file = e.target.files?.[0];\n          if (file) handleReceiptScan(file);\n        }}\n      />\n      <Button\n        type=\"button\"\n        variant=\"outline\"\n        className=\"w-full h-10 bg-gradient-to-br from-orange-500 via-pink-500 to-purple-500 animate-gradient hover:opacity-90 transition-opacity text-white hover:text-white\"\n        onClick={() => fileInputRef.current?.click()}\n        disabled={scanReceiptLoading}\n      >\n        {scanReceiptLoading ? (\n          <>\n            <Loader2 className=\"mr-2 animate-spin\" />\n            <span>Scanning Receipt...</span>\n          </>\n        ) : (\n          <>\n            <Camera className=\"mr-2\" />\n            <span>Scan Receipt with AI</span>\n          </>\n        )}\n      </Button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(main)/transaction/_components/transaction-form.jsx",
    "content": "\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { CalendarIcon, Loader2 } from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { toast } from \"sonner\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Switch } from \"@/components/ui/switch\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Calendar } from \"@/components/ui/calendar\";\nimport { CreateAccountDrawer } from \"@/components/create-account-drawer\";\nimport { cn } from \"@/lib/utils\";\nimport { createTransaction, updateTransaction } from \"@/actions/transaction\";\nimport { transactionSchema } from \"@/app/lib/schema\";\nimport { ReceiptScanner } from \"./recipt-scanner\";\n\nexport function AddTransactionForm({\n  accounts,\n  categories,\n  editMode = false,\n  initialData = null,\n}) {\n  const router = useRouter();\n  const searchParams = useSearchParams();\n  const editId = searchParams.get(\"edit\");\n\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n    watch,\n    setValue,\n    getValues,\n    reset,\n  } = useForm({\n    resolver: zodResolver(transactionSchema),\n    defaultValues:\n      editMode && initialData\n        ? {\n            type: initialData.type,\n            amount: initialData.amount.toString(),\n            description: initialData.description,\n            accountId: initialData.accountId,\n            category: initialData.category,\n            date: new Date(initialData.date),\n            isRecurring: initialData.isRecurring,\n            ...(initialData.recurringInterval && {\n              recurringInterval: initialData.recurringInterval,\n            }),\n          }\n        : {\n            type: \"EXPENSE\",\n            amount: \"\",\n            description: \"\",\n            accountId: accounts.find((ac) => ac.isDefault)?.id,\n            date: new Date(),\n            isRecurring: false,\n          },\n  });\n\n  const {\n    loading: transactionLoading,\n    fn: transactionFn,\n    data: transactionResult,\n  } = useFetch(editMode ? updateTransaction : createTransaction);\n\n  const onSubmit = (data) => {\n    const formData = {\n      ...data,\n      amount: parseFloat(data.amount),\n    };\n\n    if (editMode) {\n      transactionFn(editId, formData);\n    } else {\n      transactionFn(formData);\n    }\n  };\n\n  const handleScanComplete = (scannedData) => {\n    if (scannedData) {\n      setValue(\"amount\", scannedData.amount.toString());\n      setValue(\"date\", new Date(scannedData.date));\n      if (scannedData.description) {\n        setValue(\"description\", scannedData.description);\n      }\n      if (scannedData.category) {\n        setValue(\"category\", scannedData.category);\n      }\n      toast.success(\"Receipt scanned successfully\");\n    }\n  };\n\n  useEffect(() => {\n    if (transactionResult?.success && !transactionLoading) {\n      toast.success(\n        editMode\n          ? \"Transaction updated successfully\"\n          : \"Transaction created successfully\"\n      );\n      reset();\n      router.push(`/account/${transactionResult.data.accountId}`);\n    }\n  }, [transactionResult, transactionLoading, editMode]);\n\n  const type = watch(\"type\");\n  const isRecurring = watch(\"isRecurring\");\n  const date = watch(\"date\");\n\n  const filteredCategories = categories.filter(\n    (category) => category.type === type\n  );\n\n  return (\n    <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-6\">\n      {/* Receipt Scanner - Only show in create mode */}\n      {!editMode && <ReceiptScanner onScanComplete={handleScanComplete} />}\n\n      {/* Type */}\n      <div className=\"space-y-2\">\n        <label className=\"text-sm font-medium\">Type</label>\n        <Select\n          onValueChange={(value) => setValue(\"type\", value)}\n          defaultValue={type}\n        >\n          <SelectTrigger>\n            <SelectValue placeholder=\"Select type\" />\n          </SelectTrigger>\n          <SelectContent>\n            <SelectItem value=\"EXPENSE\">Expense</SelectItem>\n            <SelectItem value=\"INCOME\">Income</SelectItem>\n          </SelectContent>\n        </Select>\n        {errors.type && (\n          <p className=\"text-sm text-red-500\">{errors.type.message}</p>\n        )}\n      </div>\n\n      {/* Amount and Account */}\n      <div className=\"grid gap-6 md:grid-cols-2\">\n        <div className=\"space-y-2\">\n          <label className=\"text-sm font-medium\">Amount</label>\n          <Input\n            type=\"number\"\n            step=\"0.01\"\n            placeholder=\"0.00\"\n            {...register(\"amount\")}\n          />\n          {errors.amount && (\n            <p className=\"text-sm text-red-500\">{errors.amount.message}</p>\n          )}\n        </div>\n\n        <div className=\"space-y-2\">\n          <label className=\"text-sm font-medium\">Account</label>\n          <Select\n            onValueChange={(value) => setValue(\"accountId\", value)}\n            defaultValue={getValues(\"accountId\")}\n          >\n            <SelectTrigger>\n              <SelectValue placeholder=\"Select account\" />\n            </SelectTrigger>\n            <SelectContent>\n              {accounts.map((account) => (\n                <SelectItem key={account.id} value={account.id}>\n                  {account.name} (${parseFloat(account.balance).toFixed(2)})\n                </SelectItem>\n              ))}\n              <CreateAccountDrawer>\n                <Button\n                  variant=\"ghost\"\n                  className=\"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground\"\n                >\n                  Create Account\n                </Button>\n              </CreateAccountDrawer>\n            </SelectContent>\n          </Select>\n          {errors.accountId && (\n            <p className=\"text-sm text-red-500\">{errors.accountId.message}</p>\n          )}\n        </div>\n      </div>\n\n      {/* Category */}\n      <div className=\"space-y-2\">\n        <label className=\"text-sm font-medium\">Category</label>\n        <Select\n          onValueChange={(value) => setValue(\"category\", value)}\n          defaultValue={getValues(\"category\")}\n        >\n          <SelectTrigger>\n            <SelectValue placeholder=\"Select category\" />\n          </SelectTrigger>\n          <SelectContent>\n            {filteredCategories.map((category) => (\n              <SelectItem key={category.id} value={category.id}>\n                {category.name}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n        {errors.category && (\n          <p className=\"text-sm text-red-500\">{errors.category.message}</p>\n        )}\n      </div>\n\n      {/* Date */}\n      <div className=\"space-y-2\">\n        <label className=\"text-sm font-medium\">Date</label>\n        <Popover>\n          <PopoverTrigger asChild>\n            <Button\n              variant=\"outline\"\n              className={cn(\n                \"w-full pl-3 text-left font-normal\",\n                !date && \"text-muted-foreground\"\n              )}\n            >\n              {date ? format(date, \"PPP\") : <span>Pick a date</span>}\n              <CalendarIcon className=\"ml-auto h-4 w-4 opacity-50\" />\n            </Button>\n          </PopoverTrigger>\n          <PopoverContent className=\"w-auto p-0\" align=\"start\">\n            <Calendar\n              mode=\"single\"\n              selected={date}\n              onSelect={(date) => setValue(\"date\", date)}\n              disabled={(date) =>\n                date > new Date() || date < new Date(\"1900-01-01\")\n              }\n              initialFocus\n            />\n          </PopoverContent>\n        </Popover>\n        {errors.date && (\n          <p className=\"text-sm text-red-500\">{errors.date.message}</p>\n        )}\n      </div>\n\n      {/* Description */}\n      <div className=\"space-y-2\">\n        <label className=\"text-sm font-medium\">Description</label>\n        <Input placeholder=\"Enter description\" {...register(\"description\")} />\n        {errors.description && (\n          <p className=\"text-sm text-red-500\">{errors.description.message}</p>\n        )}\n      </div>\n\n      {/* Recurring Toggle */}\n      <div className=\"flex flex-row items-center justify-between rounded-lg border p-4\">\n        <div className=\"space-y-0.5\">\n          <label className=\"text-base font-medium\">Recurring Transaction</label>\n          <div className=\"text-sm text-muted-foreground\">\n            Set up a recurring schedule for this transaction\n          </div>\n        </div>\n        <Switch\n          checked={isRecurring}\n          onCheckedChange={(checked) => setValue(\"isRecurring\", checked)}\n        />\n      </div>\n\n      {/* Recurring Interval */}\n      {isRecurring && (\n        <div className=\"space-y-2\">\n          <label className=\"text-sm font-medium\">Recurring Interval</label>\n          <Select\n            onValueChange={(value) => setValue(\"recurringInterval\", value)}\n            defaultValue={getValues(\"recurringInterval\")}\n          >\n            <SelectTrigger>\n              <SelectValue placeholder=\"Select interval\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"DAILY\">Daily</SelectItem>\n              <SelectItem value=\"WEEKLY\">Weekly</SelectItem>\n              <SelectItem value=\"MONTHLY\">Monthly</SelectItem>\n              <SelectItem value=\"YEARLY\">Yearly</SelectItem>\n            </SelectContent>\n          </Select>\n          {errors.recurringInterval && (\n            <p className=\"text-sm text-red-500\">\n              {errors.recurringInterval.message}\n            </p>\n          )}\n        </div>\n      )}\n\n      {/* Actions */}\n      <div className=\"flex gap-4\">\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          className=\"w-full\"\n          onClick={() => router.back()}\n        >\n          Cancel\n        </Button>\n        <Button type=\"submit\" className=\"w-full\" disabled={transactionLoading}>\n          {transactionLoading ? (\n            <>\n              <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n              {editMode ? \"Updating...\" : \"Creating...\"}\n            </>\n          ) : editMode ? (\n            \"Update Transaction\"\n          ) : (\n            \"Create Transaction\"\n          )}\n        </Button>\n      </div>\n    </form>\n  );\n}\n"
  },
  {
    "path": "app/(main)/transaction/create/page.jsx",
    "content": "import { getUserAccounts } from \"@/actions/dashboard\";\nimport { defaultCategories } from \"@/data/categories\";\nimport { AddTransactionForm } from \"../_components/transaction-form\";\nimport { getTransaction } from \"@/actions/transaction\";\n\nexport default async function AddTransactionPage({ searchParams }) {\n  const accounts = await getUserAccounts();\n  const editId = searchParams?.edit;\n\n  let initialData = null;\n  if (editId) {\n    const transaction = await getTransaction(editId);\n    initialData = transaction;\n  }\n\n  return (\n    <div className=\"max-w-3xl mx-auto px-5\">\n      <div className=\"flex justify-center md:justify-normal mb-8\">\n        <h1 className=\"text-5xl gradient-title \">Add Transaction</h1>\n      </div>\n      <AddTransactionForm\n        accounts={accounts}\n        categories={defaultCategories}\n        editMode={!!editId}\n        initialData={initialData}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/api/inngest/route.js",
    "content": "import { serve } from \"inngest/next\";\n\nimport { inngest } from \"@/lib/inngest/client\";\nimport {\n  checkBudgetAlerts,\n  generateMonthlyReports,\n  processRecurringTransaction,\n  triggerRecurringTransactions,\n} from \"@/lib/inngest/function\";\n\nexport const { GET, POST, PUT } = serve({\n  client: inngest,\n  functions: [\n    processRecurringTransaction,\n    triggerRecurringTransactions,\n    generateMonthlyReports,\n    checkBudgetAlerts,\n  ],\n});\n"
  },
  {
    "path": "app/api/seed/route.js",
    "content": "import { seedTransactions } from \"@/actions/seed\";\n\nexport async function GET() {\n  const result = await seedTransactions();\n  return Response.json(result);\n}\n"
  },
  {
    "path": "app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n  font-family: Arial, Helvetica, sans-serif;\n}\n\nhtml {\n  scroll-behavior: smooth;\n}\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 0 0% 3.9%;\n    --card: 0 0% 100%;\n    --card-foreground: 0 0% 3.9%;\n    --popover: 0 0% 100%;\n    --popover-foreground: 0 0% 3.9%;\n    --primary: 0 0% 9%;\n    --primary-foreground: 0 0% 98%;\n    --secondary: 0 0% 96.1%;\n    --secondary-foreground: 0 0% 9%;\n    --muted: 0 0% 96.1%;\n    --muted-foreground: 0 0% 45.1%;\n    --accent: 0 0% 96.1%;\n    --accent-foreground: 0 0% 9%;\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 0 0% 98%;\n    --border: 0 0% 89.8%;\n    --input: 0 0% 89.8%;\n    --ring: 0 0% 3.9%;\n    --chart-1: 12 76% 61%;\n    --chart-2: 173 58% 39%;\n    --chart-3: 197 37% 24%;\n    --chart-4: 43 74% 66%;\n    --chart-5: 27 87% 67%;\n    --radius: 0.5rem;\n  }\n  .dark {\n    --background: 0 0% 3.9%;\n    --foreground: 0 0% 98%;\n    --card: 0 0% 3.9%;\n    --card-foreground: 0 0% 98%;\n    --popover: 0 0% 3.9%;\n    --popover-foreground: 0 0% 98%;\n    --primary: 0 0% 98%;\n    --primary-foreground: 0 0% 9%;\n    --secondary: 0 0% 14.9%;\n    --secondary-foreground: 0 0% 98%;\n    --muted: 0 0% 14.9%;\n    --muted-foreground: 0 0% 63.9%;\n    --accent: 0 0% 14.9%;\n    --accent-foreground: 0 0% 98%;\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 0 0% 98%;\n    --border: 0 0% 14.9%;\n    --input: 0 0% 14.9%;\n    --ring: 0 0% 83.1%;\n    --chart-1: 220 70% 50%;\n    --chart-2: 160 60% 45%;\n    --chart-3: 30 80% 55%;\n    --chart-4: 280 65% 60%;\n    --chart-5: 340 75% 55%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n@layer utilities {\n  .gradient {\n    @apply bg-gradient-to-br from-blue-600 to-purple-600;\n  }\n  .gradient-title {\n    @apply gradient font-extrabold tracking-tighter pr-2 pb-2 text-transparent bg-clip-text;\n  }\n}\n\n.hero-image-wrapper {\n  perspective: 1000px;\n}\n\n.hero-image {\n  /* transform: rotateX(20deg) scale(0.9) translateY(-50); */\n  transform: rotateX(15deg) scale(1);\n  transition: transform 0.5s ease-out;\n  will-change: transform;\n}\n\n.hero-image.scrolled {\n  transform: rotateX(0deg) scale(1) translateY(40px);\n}\n\n@keyframes gradientMove {\n  0% {\n    background-position: 0% 50%;\n  }\n  50% {\n    background-position: 100% 50%;\n  }\n  100% {\n    background-position: 0% 50%;\n  }\n}\n\n/* Add this class */\n.animate-gradient {\n  background-size: 200% 200%;\n  animation: gradientMove 3s ease infinite;\n}\n"
  },
  {
    "path": "app/layout.js",
    "content": "import { Inter } from \"next/font/google\";\nimport \"./globals.css\";\nimport Header from \"@/components/header\";\nimport { ClerkProvider } from \"@clerk/nextjs\";\nimport { Toaster } from \"sonner\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata = {\n  title: \"Welth\",\n  description: \"One stop Finance Platform\",\n};\n\nexport default function RootLayout({ children }) {\n  return (\n    <ClerkProvider>\n      <html lang=\"en\">\n        <head>\n          <link rel=\"icon\" href=\"/logo-sm.png\" sizes=\"any\" />\n        </head>\n        <body className={`${inter.className}`}>\n          <Header />\n          <main className=\"min-h-screen\">{children}</main>\n          <Toaster richColors />\n\n          <footer className=\"bg-blue-50 py-12\">\n            <div className=\"container mx-auto px-4 text-center text-gray-600\">\n              <p>Made with 💗 by RoadsideCoder</p>\n            </div>\n          </footer>\n        </body>\n      </html>\n    </ClerkProvider>\n  );\n}\n"
  },
  {
    "path": "app/lib/schema.js",
    "content": "import { z } from \"zod\";\n\nexport const accountSchema = z.object({\n  name: z.string().min(1, \"Name is required\"),\n  type: z.enum([\"CURRENT\", \"SAVINGS\"]),\n  balance: z.string().min(1, \"Initial balance is required\"),\n  isDefault: z.boolean().default(false),\n});\n\nexport const transactionSchema = z\n  .object({\n    type: z.enum([\"INCOME\", \"EXPENSE\"]),\n    amount: z.string().min(1, \"Amount is required\"),\n    description: z.string().optional(),\n    date: z.date({ required_error: \"Date is required\" }),\n    accountId: z.string().min(1, \"Account is required\"),\n    category: z.string().min(1, \"Category is required\"),\n    isRecurring: z.boolean().default(false),\n    recurringInterval: z\n      .enum([\"DAILY\", \"WEEKLY\", \"MONTHLY\", \"YEARLY\"])\n      .optional(),\n  })\n  .superRefine((data, ctx) => {\n    if (data.isRecurring && !data.recurringInterval) {\n      ctx.addIssue({\n        code: z.ZodIssueCode.custom,\n        message: \"Recurring interval is required for recurring transactions\",\n        path: [\"recurringInterval\"],\n      });\n    }\n  });\n"
  },
  {
    "path": "app/not-found.jsx",
    "content": "import Link from \"next/link\";\nimport { Button } from \"@/components/ui/button\";\n\nexport default function NotFound() {\n  return (\n    <div className=\"flex flex-col items-center justify-center min-h-[100vh] px-4 text-center\">\n      <h1 className=\"text-6xl font-bold gradient-title mb-4\">404</h1>\n      <h2 className=\"text-2xl font-semibold mb-4\">Page Not Found</h2>\n      <p className=\"text-gray-600 mb-8\">\n        Oops! The page you&apos;re looking for doesn&apos;t exist or has been\n        moved.\n      </p>\n      <Link href=\"/\">\n        <Button>Return Home</Button>\n      </Link>\n    </div>\n  );\n}\n"
  },
  {
    "path": "app/page.js",
    "content": "import React from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport Image from \"next/image\";\nimport {\n  featuresData,\n  howItWorksData,\n  statsData,\n  testimonialsData,\n} from \"@/data/landing\";\nimport HeroSection from \"@/components/hero\";\nimport Link from \"next/link\";\n\nconst LandingPage = () => {\n  return (\n    <div className=\"min-h-screen bg-white\">\n      {/* Hero Section */}\n      <HeroSection />\n\n      {/* Stats Section */}\n      <section className=\"py-20 bg-blue-50\">\n        <div className=\"container mx-auto px-4\">\n          <div className=\"grid grid-cols-2 md:grid-cols-4 gap-8\">\n            {statsData.map((stat, index) => (\n              <div key={index} className=\"text-center\">\n                <div className=\"text-4xl font-bold text-blue-600 mb-2\">\n                  {stat.value}\n                </div>\n                <div className=\"text-gray-600\">{stat.label}</div>\n              </div>\n            ))}\n          </div>\n        </div>\n      </section>\n\n      {/* Features Section */}\n      <section id=\"features\" className=\"py-20\">\n        <div className=\"container mx-auto px-4\">\n          <h2 className=\"text-3xl font-bold text-center mb-12\">\n            Everything you need to manage your finances\n          </h2>\n          <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8\">\n            {featuresData.map((feature, index) => (\n              <Card className=\"p-6\" key={index}>\n                <CardContent className=\"space-y-4 pt-4\">\n                  {feature.icon}\n                  <h3 className=\"text-xl font-semibold\">{feature.title}</h3>\n                  <p className=\"text-gray-600\">{feature.description}</p>\n                </CardContent>\n              </Card>\n            ))}\n          </div>\n        </div>\n      </section>\n\n      {/* How It Works Section */}\n      <section className=\"py-20 bg-blue-50\">\n        <div className=\"container mx-auto px-4\">\n          <h2 className=\"text-3xl font-bold text-center mb-16\">How It Works</h2>\n          <div className=\"grid grid-cols-1 md:grid-cols-3 gap-12\">\n            {howItWorksData.map((step, index) => (\n              <div key={index} className=\"text-center\">\n                <div className=\"w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-6\">\n                  {step.icon}\n                </div>\n                <h3 className=\"text-xl font-semibold mb-4\">{step.title}</h3>\n                <p className=\"text-gray-600\">{step.description}</p>\n              </div>\n            ))}\n          </div>\n        </div>\n      </section>\n\n      {/* Testimonials Section */}\n      <section id=\"testimonials\" className=\"py-20\">\n        <div className=\"container mx-auto px-4\">\n          <h2 className=\"text-3xl font-bold text-center mb-16\">\n            What Our Users Say\n          </h2>\n          <div className=\"grid grid-cols-1 md:grid-cols-3 gap-8\">\n            {testimonialsData.map((testimonial, index) => (\n              <Card key={index} className=\"p-6\">\n                <CardContent className=\"pt-4\">\n                  <div className=\"flex items-center mb-4\">\n                    <Image\n                      src={testimonial.image}\n                      alt={testimonial.name}\n                      width={40}\n                      height={40}\n                      className=\"rounded-full\"\n                    />\n                    <div className=\"ml-4\">\n                      <div className=\"font-semibold\">{testimonial.name}</div>\n                      <div className=\"text-sm text-gray-600\">\n                        {testimonial.role}\n                      </div>\n                    </div>\n                  </div>\n                  <p className=\"text-gray-600\">{testimonial.quote}</p>\n                </CardContent>\n              </Card>\n            ))}\n          </div>\n        </div>\n      </section>\n\n      {/* CTA Section */}\n      <section className=\"py-20 bg-blue-600\">\n        <div className=\"container mx-auto px-4 text-center\">\n          <h2 className=\"text-3xl font-bold text-white mb-4\">\n            Ready to Take Control of Your Finances?\n          </h2>\n          <p className=\"text-blue-100 mb-8 max-w-2xl mx-auto\">\n            Join thousands of users who are already managing their finances\n            smarter with Welth\n          </p>\n          <Link href=\"/dashboard\">\n            <Button\n              size=\"lg\"\n              className=\"bg-white text-blue-600 hover:bg-blue-50 animate-bounce\"\n            >\n              Start Free Trial\n            </Button>\n          </Link>\n        </div>\n      </section>\n    </div>\n  );\n};\n\nexport default LandingPage;\n"
  },
  {
    "path": "components/create-account-drawer.jsx",
    "content": "\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Loader2 } from \"lucide-react\";\nimport useFetch from \"@/hooks/use-fetch\";\nimport { toast } from \"sonner\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Drawer,\n  DrawerContent,\n  DrawerHeader,\n  DrawerTitle,\n  DrawerTrigger,\n  DrawerClose,\n} from \"@/components/ui/drawer\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { createAccount } from \"@/actions/dashboard\";\nimport { accountSchema } from \"@/app/lib/schema\";\n\nexport function CreateAccountDrawer({ children }) {\n  const [open, setOpen] = useState(false);\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n    setValue,\n    watch,\n    reset,\n  } = useForm({\n    resolver: zodResolver(accountSchema),\n    defaultValues: {\n      name: \"\",\n      type: \"CURRENT\",\n      balance: \"\",\n      isDefault: false,\n    },\n  });\n\n  const {\n    loading: createAccountLoading,\n    fn: createAccountFn,\n    error,\n    data: newAccount,\n  } = useFetch(createAccount);\n\n  const onSubmit = async (data) => {\n    await createAccountFn(data);\n  };\n\n  useEffect(() => {\n    if (newAccount) {\n      toast.success(\"Account created successfully\");\n      reset();\n      setOpen(false);\n    }\n  }, [newAccount, reset]);\n\n  useEffect(() => {\n    if (error) {\n      toast.error(error.message || \"Failed to create account\");\n    }\n  }, [error]);\n\n  return (\n    <Drawer open={open} onOpenChange={setOpen}>\n      <DrawerTrigger asChild>{children}</DrawerTrigger>\n      <DrawerContent>\n        <DrawerHeader>\n          <DrawerTitle>Create New Account</DrawerTitle>\n        </DrawerHeader>\n        <div className=\"px-4 pb-4\">\n          <form onSubmit={handleSubmit(onSubmit)} className=\"space-y-4\">\n            <div className=\"space-y-2\">\n              <label\n                htmlFor=\"name\"\n                className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n              >\n                Account Name\n              </label>\n              <Input\n                id=\"name\"\n                placeholder=\"e.g., Main Checking\"\n                {...register(\"name\")}\n              />\n              {errors.name && (\n                <p className=\"text-sm text-red-500\">{errors.name.message}</p>\n              )}\n            </div>\n\n            <div className=\"space-y-2\">\n              <label\n                htmlFor=\"type\"\n                className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n              >\n                Account Type\n              </label>\n              <Select\n                onValueChange={(value) => setValue(\"type\", value)}\n                defaultValue={watch(\"type\")}\n              >\n                <SelectTrigger id=\"type\">\n                  <SelectValue placeholder=\"Select type\" />\n                </SelectTrigger>\n                <SelectContent>\n                  <SelectItem value=\"CURRENT\">Current</SelectItem>\n                  <SelectItem value=\"SAVINGS\">Savings</SelectItem>\n                </SelectContent>\n              </Select>\n              {errors.type && (\n                <p className=\"text-sm text-red-500\">{errors.type.message}</p>\n              )}\n            </div>\n\n            <div className=\"space-y-2\">\n              <label\n                htmlFor=\"balance\"\n                className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n              >\n                Initial Balance\n              </label>\n              <Input\n                id=\"balance\"\n                type=\"number\"\n                step=\"0.01\"\n                placeholder=\"0.00\"\n                {...register(\"balance\")}\n              />\n              {errors.balance && (\n                <p className=\"text-sm text-red-500\">{errors.balance.message}</p>\n              )}\n            </div>\n\n            <div className=\"flex items-center justify-between rounded-lg border p-3\">\n              <div className=\"space-y-0.5\">\n                <label\n                  htmlFor=\"isDefault\"\n                  className=\"text-base font-medium cursor-pointer\"\n                >\n                  Set as Default\n                </label>\n                <p className=\"text-sm text-muted-foreground\">\n                  This account will be selected by default for transactions\n                </p>\n              </div>\n              <Switch\n                id=\"isDefault\"\n                checked={watch(\"isDefault\")}\n                onCheckedChange={(checked) => setValue(\"isDefault\", checked)}\n              />\n            </div>\n\n            <div className=\"flex gap-4 pt-4\">\n              <DrawerClose asChild>\n                <Button type=\"button\" variant=\"outline\" className=\"flex-1\">\n                  Cancel\n                </Button>\n              </DrawerClose>\n              <Button\n                type=\"submit\"\n                className=\"flex-1\"\n                disabled={createAccountLoading}\n              >\n                {createAccountLoading ? (\n                  <>\n                    <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n                    Creating...\n                  </>\n                ) : (\n                  \"Create Account\"\n                )}\n              </Button>\n            </div>\n          </form>\n        </div>\n      </DrawerContent>\n    </Drawer>\n  );\n}\n"
  },
  {
    "path": "components/header.jsx",
    "content": "import React from \"react\";\nimport { Button } from \"./ui/button\";\nimport { PenBox, LayoutDashboard } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { SignedIn, SignedOut, SignInButton, UserButton } from \"@clerk/nextjs\";\nimport { checkUser } from \"@/lib/checkUser\";\nimport Image from \"next/image\";\n\nconst Header = async () => {\n  await checkUser();\n\n  return (\n    <header className=\"fixed top-0 w-full bg-white/80 backdrop-blur-md z-50 border-b\">\n      <nav className=\"container mx-auto px-4 py-4 flex items-center justify-between\">\n        <Link href=\"/\">\n          <Image\n            src={\"/logo.png\"}\n            alt=\"Welth Logo\"\n            width={200}\n            height={60}\n            className=\"h-12 w-auto object-contain\"\n          />\n        </Link>\n\n        {/* Navigation Links - Different for signed in/out users */}\n        <div className=\"hidden md:flex items-center space-x-8\">\n          <SignedOut>\n            <a href=\"#features\" className=\"text-gray-600 hover:text-blue-600\">\n              Features\n            </a>\n            <a\n              href=\"#testimonials\"\n              className=\"text-gray-600 hover:text-blue-600\"\n            >\n              Testimonials\n            </a>\n          </SignedOut>\n        </div>\n\n        {/* Action Buttons */}\n        <div className=\"flex items-center space-x-4\">\n          <SignedIn>\n            <Link\n              href=\"/dashboard\"\n              className=\"text-gray-600 hover:text-blue-600 flex items-center gap-2\"\n            >\n              <Button variant=\"outline\">\n                <LayoutDashboard size={18} />\n                <span className=\"hidden md:inline\">Dashboard</span>\n              </Button>\n            </Link>\n            <a href=\"/transaction/create\">\n              <Button className=\"flex items-center gap-2\">\n                <PenBox size={18} />\n                <span className=\"hidden md:inline\">Add Transaction</span>\n              </Button>\n            </a>\n          </SignedIn>\n          <SignedOut>\n            <SignInButton forceRedirectUrl=\"/dashboard\">\n              <Button variant=\"outline\">Login</Button>\n            </SignInButton>\n          </SignedOut>\n          <SignedIn>\n            <UserButton\n              appearance={{\n                elements: {\n                  avatarBox: \"w-10 h-10\",\n                },\n              }}\n            />\n          </SignedIn>\n        </div>\n      </nav>\n    </header>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "components/hero.jsx",
    "content": "\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport Image from \"next/image\";\nimport { Button } from \"@/components/ui/button\";\nimport Link from \"next/link\";\n\nconst HeroSection = () => {\n  const imageRef = useRef(null);\n\n  useEffect(() => {\n    const imageElement = imageRef.current;\n\n    const handleScroll = () => {\n      const scrollPosition = window.scrollY;\n      const scrollThreshold = 100;\n\n      if (scrollPosition > scrollThreshold) {\n        imageElement.classList.add(\"scrolled\");\n      } else {\n        imageElement.classList.remove(\"scrolled\");\n      }\n    };\n\n    window.addEventListener(\"scroll\", handleScroll);\n    return () => window.removeEventListener(\"scroll\", handleScroll);\n  }, []);\n\n  return (\n    <section className=\"pt-40 pb-20 px-4\">\n      <div className=\"container mx-auto text-center\">\n        <h1 className=\"text-5xl md:text-8xl lg:text-[105px] pb-6 gradient-title\">\n          Manage Your Finances <br /> with Intelligence\n        </h1>\n        <p className=\"text-xl text-gray-600 mb-8 max-w-2xl mx-auto\">\n          An AI-powered financial management platform that helps you track,\n          analyze, and optimize your spending with real-time insights.\n        </p>\n        <div className=\"flex justify-center space-x-4\">\n          <Link href=\"/dashboard\">\n            <Button size=\"lg\" className=\"px-8\">\n              Get Started\n            </Button>\n          </Link>\n          <Link href=\"https://www.youtube.com/roadsidecoder\">\n            <Button size=\"lg\" variant=\"outline\" className=\"px-8\">\n              Watch Demo\n            </Button>\n          </Link>\n        </div>\n        <div className=\"hero-image-wrapper mt-5 md:mt-0\">\n          <div ref={imageRef} className=\"hero-image\">\n            <Image\n              src=\"/banner.jpeg\"\n              width={1280}\n              height={720}\n              alt=\"Dashboard Preview\"\n              className=\"rounded-lg shadow-2xl border mx-auto\"\n              priority\n            />\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nexport default HeroSection;\n"
  },
  {
    "path": "components/ui/badge.jsx",
    "content": "import * as React from \"react\"\nimport { cva } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n  \"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80\",\n        secondary:\n          \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        destructive:\n          \"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80\",\n        outline: \"text-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nfunction Badge({\n  className,\n  variant,\n  ...props\n}) {\n  return (<div className={cn(badgeVariants({ variant }), className)} {...props} />);\n}\n\nexport { Badge, badgeVariants }\n"
  },
  {
    "path": "components/ui/button.jsx",
    "content": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-9 px-4 py-2\",\n        sm: \"h-8 rounded-md px-3 text-xs\",\n        lg: \"h-10 rounded-md px-8\",\n        icon: \"h-9 w-9\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nconst Button = React.forwardRef(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\";\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    );\n  }\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n"
  },
  {
    "path": "components/ui/calendar.jsx",
    "content": "\"use client\";\nimport * as React from \"react\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { DayPicker } from \"react-day-picker\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\nfunction Calendar({\n  className,\n  classNames,\n  showOutsideDays = true,\n  ...props\n}) {\n  return (\n    (<DayPicker\n      showOutsideDays={showOutsideDays}\n      className={cn(\"p-3\", className)}\n      classNames={{\n        months: \"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0\",\n        month: \"space-y-4\",\n        caption: \"flex justify-center pt-1 relative items-center\",\n        caption_label: \"text-sm font-medium\",\n        nav: \"space-x-1 flex items-center\",\n        nav_button: cn(\n          buttonVariants({ variant: \"outline\" }),\n          \"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100\"\n        ),\n        nav_button_previous: \"absolute left-1\",\n        nav_button_next: \"absolute right-1\",\n        table: \"w-full border-collapse space-y-1\",\n        head_row: \"flex\",\n        head_cell:\n          \"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]\",\n        row: \"flex w-full mt-2\",\n        cell: cn(\n          \"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md\",\n          props.mode === \"range\"\n            ? \"[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md\"\n            : \"[&:has([aria-selected])]:rounded-md\"\n        ),\n        day: cn(\n          buttonVariants({ variant: \"ghost\" }),\n          \"h-8 w-8 p-0 font-normal aria-selected:opacity-100\"\n        ),\n        day_range_start: \"day-range-start\",\n        day_range_end: \"day-range-end\",\n        day_selected:\n          \"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground\",\n        day_today: \"bg-accent text-accent-foreground\",\n        day_outside:\n          \"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground\",\n        day_disabled: \"text-muted-foreground opacity-50\",\n        day_range_middle:\n          \"aria-selected:bg-accent aria-selected:text-accent-foreground\",\n        day_hidden: \"invisible\",\n        ...classNames,\n      }}\n      components={{\n        IconLeft: ({ ...props }) => <ChevronLeft className=\"h-4 w-4\" />,\n        IconRight: ({ ...props }) => <ChevronRight className=\"h-4 w-4\" />,\n      }}\n      {...props} />)\n  );\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n"
  },
  {
    "path": "components/ui/card.jsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"rounded-xl border bg-card text-card-foreground shadow\", className)}\n    {...props} />\n))\nCard.displayName = \"Card\"\n\nconst CardHeader = React.forwardRef(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n    {...props} />\n))\nCardHeader.displayName = \"CardHeader\"\n\nconst CardTitle = React.forwardRef(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"font-semibold leading-none tracking-tight\", className)}\n    {...props} />\n))\nCardTitle.displayName = \"CardTitle\"\n\nconst CardDescription = React.forwardRef(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props} />\n))\nCardDescription.displayName = \"CardDescription\"\n\nconst CardContent = React.forwardRef(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n))\nCardContent.displayName = \"CardContent\"\n\nconst CardFooter = React.forwardRef(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex items-center p-6 pt-0\", className)}\n    {...props} />\n))\nCardFooter.displayName = \"CardFooter\"\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }\n"
  },
  {
    "path": "components/ui/checkbox.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\"\nimport { Check } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Checkbox = React.forwardRef(({ className, ...props }, ref) => (\n  <CheckboxPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n      className\n    )}\n    {...props}>\n    <CheckboxPrimitive.Indicator className={cn(\"flex items-center justify-center text-current\")}>\n      <Check className=\"h-4 w-4\" />\n    </CheckboxPrimitive.Indicator>\n  </CheckboxPrimitive.Root>\n))\nCheckbox.displayName = CheckboxPrimitive.Root.displayName\n\nexport { Checkbox }\n"
  },
  {
    "path": "components/ui/drawer.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Drawer = ({\n  shouldScaleBackground = true,\n  ...props\n}) => (\n  <DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />\n)\nDrawer.displayName = \"Drawer\"\n\nconst DrawerTrigger = DrawerPrimitive.Trigger\n\nconst DrawerPortal = DrawerPrimitive.Portal\n\nconst DrawerClose = DrawerPrimitive.Close\n\nconst DrawerOverlay = React.forwardRef(({ className, ...props }, ref) => (\n  <DrawerPrimitive.Overlay\n    ref={ref}\n    className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n    {...props} />\n))\nDrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName\n\nconst DrawerContent = React.forwardRef(({ className, children, ...props }, ref) => (\n  <DrawerPortal>\n    <DrawerOverlay />\n    <DrawerPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background\",\n        className\n      )}\n      {...props}>\n      <div className=\"mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted\" />\n      {children}\n    </DrawerPrimitive.Content>\n  </DrawerPortal>\n))\nDrawerContent.displayName = \"DrawerContent\"\n\nconst DrawerHeader = ({\n  className,\n  ...props\n}) => (\n  <div\n    className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n    {...props} />\n)\nDrawerHeader.displayName = \"DrawerHeader\"\n\nconst DrawerFooter = ({\n  className,\n  ...props\n}) => (\n  <div className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)} {...props} />\n)\nDrawerFooter.displayName = \"DrawerFooter\"\n\nconst DrawerTitle = React.forwardRef(({ className, ...props }, ref) => (\n  <DrawerPrimitive.Title\n    ref={ref}\n    className={cn(\"text-lg font-semibold leading-none tracking-tight\", className)}\n    {...props} />\n))\nDrawerTitle.displayName = DrawerPrimitive.Title.displayName\n\nconst DrawerDescription = React.forwardRef(({ className, ...props }, ref) => (\n  <DrawerPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props} />\n))\nDrawerDescription.displayName = DrawerPrimitive.Description.displayName\n\nexport {\n  Drawer,\n  DrawerPortal,\n  DrawerOverlay,\n  DrawerTrigger,\n  DrawerClose,\n  DrawerContent,\n  DrawerHeader,\n  DrawerFooter,\n  DrawerTitle,\n  DrawerDescription,\n}\n"
  },
  {
    "path": "components/ui/dropdown-menu.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubTrigger\n    ref={ref}\n    className={cn(\n      \"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props}>\n    {children}\n    <ChevronRight className=\"ml-auto\" />\n  </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n  DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.SubContent\n    ref={ref}\n    className={cn(\n      \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n      className\n    )}\n    {...props} />\n))\nDropdownMenuSubContent.displayName =\n  DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (\n  <DropdownMenuPrimitive.Portal>\n    <DropdownMenuPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md\",\n        \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        className\n      )}\n      {...props} />\n  </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0\",\n      inset && \"pl-8\",\n      className\n    )}\n    {...props} />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => (\n  <DropdownMenuPrimitive.CheckboxItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    checked={checked}\n    {...props}>\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n  DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => (\n  <DropdownMenuPrimitive.RadioItem\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}>\n    <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <DropdownMenuPrimitive.ItemIndicator>\n        <Circle className=\"h-2 w-2 fill-current\" />\n      </DropdownMenuPrimitive.ItemIndicator>\n    </span>\n    {children}\n  </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => (\n  <DropdownMenuPrimitive.Label\n    ref={ref}\n    className={cn(\"px-2 py-1.5 text-sm font-semibold\", inset && \"pl-8\", className)}\n    {...props} />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => (\n  <DropdownMenuPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props} />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n  className,\n  ...props\n}) => {\n  return (\n    (<span\n      className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n      {...props} />)\n  );\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n  DropdownMenu,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuGroup,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuRadioGroup,\n}\n"
  },
  {
    "path": "components/ui/input.jsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Input = React.forwardRef(({ className, type, ...props }, ref) => {\n  return (\n    (<input\n      type={type}\n      className={cn(\n        \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        className\n      )}\n      ref={ref}\n      {...props} />)\n  );\n})\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "components/ui/popover.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Popover = PopoverPrimitive.Root\n\nconst PopoverTrigger = PopoverPrimitive.Trigger\n\nconst PopoverAnchor = PopoverPrimitive.Anchor\n\nconst PopoverContent = React.forwardRef(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n  <PopoverPrimitive.Portal>\n    <PopoverPrimitive.Content\n      ref={ref}\n      align={align}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        className\n      )}\n      {...props} />\n  </PopoverPrimitive.Portal>\n))\nPopoverContent.displayName = PopoverPrimitive.Content.displayName\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }\n"
  },
  {
    "path": "components/ui/progress.jsx",
    "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Progress = React.forwardRef(\n  ({ className, value, extraStyles, ...props }, ref) => (\n    <ProgressPrimitive.Root\n      ref={ref}\n      className={cn(\n        \"relative h-2 w-full overflow-hidden rounded-full bg-primary/20\",\n        className\n      )}\n      {...props}\n    >\n      <ProgressPrimitive.Indicator\n        className={`h-full w-full flex-1 bg-primary transition-all ${extraStyles}`}\n        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n      />\n    </ProgressPrimitive.Root>\n  )\n);\nProgress.displayName = ProgressPrimitive.Root.displayName;\n\nexport { Progress };\n"
  },
  {
    "path": "components/ui/select.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { Check, ChevronDown, ChevronUp } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nconst SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (\n  <SelectPrimitive.Trigger\n    ref={ref}\n    className={cn(\n      \"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n      className\n    )}\n    {...props}>\n    {children}\n    <SelectPrimitive.Icon asChild>\n      <ChevronDown className=\"h-4 w-4 opacity-50\" />\n    </SelectPrimitive.Icon>\n  </SelectPrimitive.Trigger>\n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (\n  <SelectPrimitive.ScrollUpButton\n    ref={ref}\n    className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n    {...props}>\n    <ChevronUp className=\"h-4 w-4\" />\n  </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (\n  <SelectPrimitive.ScrollDownButton\n    ref={ref}\n    className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n    {...props}>\n    <ChevronDown className=\"h-4 w-4\" />\n  </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n  SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef(({ className, children, position = \"popper\", ...props }, ref) => (\n  <SelectPrimitive.Portal>\n    <SelectPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        position === \"popper\" &&\n          \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n        className\n      )}\n      position={position}\n      {...props}>\n      <SelectScrollUpButton />\n      <SelectPrimitive.Viewport\n        className={cn(\"p-1\", position === \"popper\" &&\n          \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\")}>\n        {children}\n      </SelectPrimitive.Viewport>\n      <SelectScrollDownButton />\n    </SelectPrimitive.Content>\n  </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef(({ className, ...props }, ref) => (\n  <SelectPrimitive.Label\n    ref={ref}\n    className={cn(\"px-2 py-1.5 text-sm font-semibold\", className)}\n    {...props} />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (\n  <SelectPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n      className\n    )}\n    {...props}>\n    <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n      <SelectPrimitive.ItemIndicator>\n        <Check className=\"h-4 w-4\" />\n      </SelectPrimitive.ItemIndicator>\n    </span>\n    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n  </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (\n  <SelectPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n    {...props} />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n  Select,\n  SelectGroup,\n  SelectValue,\n  SelectTrigger,\n  SelectContent,\n  SelectLabel,\n  SelectItem,\n  SelectSeparator,\n  SelectScrollUpButton,\n  SelectScrollDownButton,\n}\n"
  },
  {
    "path": "components/ui/sonner.jsx",
    "content": "\"use client\";\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\nconst Toaster = ({\n  ...props\n}) => {\n  const { theme = \"system\" } = useTheme()\n\n  return (\n    (<Sonner\n      theme={theme}\n      className=\"toaster group\"\n      toastOptions={{\n        classNames: {\n          toast:\n            \"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg\",\n          description: \"group-[.toast]:text-muted-foreground\",\n          actionButton:\n            \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n          cancelButton:\n            \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground\",\n        },\n      }}\n      {...props} />)\n  );\n}\n\nexport { Toaster }\n"
  },
  {
    "path": "components/ui/switch.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Switch = React.forwardRef(({ className, ...props }, ref) => (\n  <SwitchPrimitives.Root\n    className={cn(\n      \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n      className\n    )}\n    {...props}\n    ref={ref}>\n    <SwitchPrimitives.Thumb\n      className={cn(\n        \"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\"\n      )} />\n  </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"
  },
  {
    "path": "components/ui/table.jsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Table = React.forwardRef(({ className, ...props }, ref) => (\n  <div className=\"relative w-full overflow-auto\">\n    <table\n      ref={ref}\n      className={cn(\"w-full caption-bottom text-sm\", className)}\n      {...props} />\n  </div>\n))\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef(({ className, ...props }, ref) => (\n  <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef(({ className, ...props }, ref) => (\n  <tbody\n    ref={ref}\n    className={cn(\"[&_tr:last-child]:border-0\", className)}\n    {...props} />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef(({ className, ...props }, ref) => (\n  <tfoot\n    ref={ref}\n    className={cn(\"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\", className)}\n    {...props} />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef(({ className, ...props }, ref) => (\n  <tr\n    ref={ref}\n    className={cn(\n      \"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\",\n      className\n    )}\n    {...props} />\n))\nTableRow.displayName = \"TableRow\"\n\nconst TableHead = React.forwardRef(({ className, ...props }, ref) => (\n  <th\n    ref={ref}\n    className={cn(\n      \"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n      className\n    )}\n    {...props} />\n))\nTableHead.displayName = \"TableHead\"\n\nconst TableCell = React.forwardRef(({ className, ...props }, ref) => (\n  <td\n    ref={ref}\n    className={cn(\n      \"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n      className\n    )}\n    {...props} />\n))\nTableCell.displayName = \"TableCell\"\n\nconst TableCaption = React.forwardRef(({ className, ...props }, ref) => (\n  <caption\n    ref={ref}\n    className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n    {...props} />\n))\nTableCaption.displayName = \"TableCaption\"\n\nexport {\n  Table,\n  TableHeader,\n  TableBody,\n  TableFooter,\n  TableHead,\n  TableRow,\n  TableCell,\n  TableCaption,\n}\n"
  },
  {
    "path": "components/ui/tooltip.jsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst TooltipProvider = TooltipPrimitive.Provider\n\nconst Tooltip = TooltipPrimitive.Root\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\nconst TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (\n  <TooltipPrimitive.Portal>\n    <TooltipPrimitive.Content\n      ref={ref}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        className\n      )}\n      {...props} />\n  </TooltipPrimitive.Portal>\n))\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": false,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.js\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"neutral\",\n    \"cssVariables\": true,\n    \"prefix\": \"\"\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\",\n    \"ui\": \"@/components/ui\",\n    \"lib\": \"@/lib\",\n    \"hooks\": \"@/hooks\"\n  },\n  \"iconLibrary\": \"lucide\"\n}"
  },
  {
    "path": "data/categories.js",
    "content": "export const defaultCategories = [\n  // Income Categories\n  {\n    id: \"salary\",\n    name: \"Salary\",\n    type: \"INCOME\",\n    color: \"#22c55e\", // green-500\n    icon: \"Wallet\",\n  },\n  {\n    id: \"freelance\",\n    name: \"Freelance\",\n    type: \"INCOME\",\n    color: \"#06b6d4\", // cyan-500\n    icon: \"Laptop\",\n  },\n  {\n    id: \"investments\",\n    name: \"Investments\",\n    type: \"INCOME\",\n    color: \"#6366f1\", // indigo-500\n    icon: \"TrendingUp\",\n  },\n  {\n    id: \"business\",\n    name: \"Business\",\n    type: \"INCOME\",\n    color: \"#ec4899\", // pink-500\n    icon: \"Building\",\n  },\n  {\n    id: \"rental\",\n    name: \"Rental\",\n    type: \"INCOME\",\n    color: \"#f59e0b\", // amber-500\n    icon: \"Home\",\n  },\n  {\n    id: \"other-income\",\n    name: \"Other Income\",\n    type: \"INCOME\",\n    color: \"#64748b\", // slate-500\n    icon: \"Plus\",\n  },\n\n  // Expense Categories\n  {\n    id: \"housing\",\n    name: \"Housing\",\n    type: \"EXPENSE\",\n    color: \"#ef4444\", // red-500\n    icon: \"Home\",\n    subcategories: [\"Rent\", \"Mortgage\", \"Property Tax\", \"Maintenance\"],\n  },\n  {\n    id: \"transportation\",\n    name: \"Transportation\",\n    type: \"EXPENSE\",\n    color: \"#f97316\", // orange-500\n    icon: \"Car\",\n    subcategories: [\"Fuel\", \"Public Transport\", \"Maintenance\", \"Parking\"],\n  },\n  {\n    id: \"groceries\",\n    name: \"Groceries\",\n    type: \"EXPENSE\",\n    color: \"#84cc16\", // lime-500\n    icon: \"Shopping\",\n  },\n  {\n    id: \"utilities\",\n    name: \"Utilities\",\n    type: \"EXPENSE\",\n    color: \"#06b6d4\", // cyan-500\n    icon: \"Zap\",\n    subcategories: [\"Electricity\", \"Water\", \"Gas\", \"Internet\", \"Phone\"],\n  },\n  {\n    id: \"entertainment\",\n    name: \"Entertainment\",\n    type: \"EXPENSE\",\n    color: \"#8b5cf6\", // violet-500\n    icon: \"Film\",\n    subcategories: [\"Movies\", \"Games\", \"Streaming Services\"],\n  },\n  {\n    id: \"food\",\n    name: \"Food\",\n    type: \"EXPENSE\",\n    color: \"#f43f5e\", // rose-500\n    icon: \"UtensilsCrossed\",\n  },\n  {\n    id: \"shopping\",\n    name: \"Shopping\",\n    type: \"EXPENSE\",\n    color: \"#ec4899\", // pink-500\n    icon: \"ShoppingBag\",\n    subcategories: [\"Clothing\", \"Electronics\", \"Home Goods\"],\n  },\n  {\n    id: \"healthcare\",\n    name: \"Healthcare\",\n    type: \"EXPENSE\",\n    color: \"#14b8a6\", // teal-500\n    icon: \"HeartPulse\",\n    subcategories: [\"Medical\", \"Dental\", \"Pharmacy\", \"Insurance\"],\n  },\n  {\n    id: \"education\",\n    name: \"Education\",\n    type: \"EXPENSE\",\n    color: \"#6366f1\", // indigo-500\n    icon: \"GraduationCap\",\n    subcategories: [\"Tuition\", \"Books\", \"Courses\"],\n  },\n  {\n    id: \"personal\",\n    name: \"Personal Care\",\n    type: \"EXPENSE\",\n    color: \"#d946ef\", // fuchsia-500\n    icon: \"Smile\",\n    subcategories: [\"Haircut\", \"Gym\", \"Beauty\"],\n  },\n  {\n    id: \"travel\",\n    name: \"Travel\",\n    type: \"EXPENSE\",\n    color: \"#0ea5e9\", // sky-500\n    icon: \"Plane\",\n  },\n  {\n    id: \"insurance\",\n    name: \"Insurance\",\n    type: \"EXPENSE\",\n    color: \"#64748b\", // slate-500\n    icon: \"Shield\",\n    subcategories: [\"Life\", \"Home\", \"Vehicle\"],\n  },\n  {\n    id: \"gifts\",\n    name: \"Gifts & Donations\",\n    type: \"EXPENSE\",\n    color: \"#f472b6\", // pink-400\n    icon: \"Gift\",\n  },\n  {\n    id: \"bills\",\n    name: \"Bills & Fees\",\n    type: \"EXPENSE\",\n    color: \"#fb7185\", // rose-400\n    icon: \"Receipt\",\n    subcategories: [\"Bank Fees\", \"Late Fees\", \"Service Charges\"],\n  },\n  {\n    id: \"other-expense\",\n    name: \"Other Expenses\",\n    type: \"EXPENSE\",\n    color: \"#94a3b8\", // slate-400\n    icon: \"MoreHorizontal\",\n  },\n];\n\nexport const categoryColors = defaultCategories.reduce((acc, category) => {\n  acc[category.id] = category.color;\n  return acc;\n}, {});\n"
  },
  {
    "path": "data/landing.js",
    "content": "import {\n  BarChart3,\n  Receipt,\n  PieChart,\n  CreditCard,\n  Globe,\n  Zap,\n} from \"lucide-react\";\n\n// Stats Data\nexport const statsData = [\n  {\n    value: \"50K+\",\n    label: \"Active Users\",\n  },\n  {\n    value: \"$2B+\",\n    label: \"Transactions Tracked\",\n  },\n  {\n    value: \"99.9%\",\n    label: \"Uptime\",\n  },\n  {\n    value: \"4.9/5\",\n    label: \"User Rating\",\n  },\n];\n\n// Features Data\nexport const featuresData = [\n  {\n    icon: <BarChart3 className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Advanced Analytics\",\n    description:\n      \"Get detailed insights into your spending patterns with AI-powered analytics\",\n  },\n  {\n    icon: <Receipt className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Smart Receipt Scanner\",\n    description:\n      \"Extract data automatically from receipts using advanced AI technology\",\n  },\n  {\n    icon: <PieChart className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Budget Planning\",\n    description: \"Create and manage budgets with intelligent recommendations\",\n  },\n  {\n    icon: <CreditCard className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Multi-Account Support\",\n    description: \"Manage multiple accounts and credit cards in one place\",\n  },\n  {\n    icon: <Globe className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Multi-Currency\",\n    description: \"Support for multiple currencies with real-time conversion\",\n  },\n  {\n    icon: <Zap className=\"h-8 w-8 text-blue-600\" />,\n    title: \"Automated Insights\",\n    description: \"Get automated financial insights and recommendations\",\n  },\n];\n\n// How It Works Data\nexport const howItWorksData = [\n  {\n    icon: <CreditCard className=\"h-8 w-8 text-blue-600\" />,\n    title: \"1. Create Your Account\",\n    description:\n      \"Get started in minutes with our simple and secure sign-up process\",\n  },\n  {\n    icon: <BarChart3 className=\"h-8 w-8 text-blue-600\" />,\n    title: \"2. Track Your Spending\",\n    description:\n      \"Automatically categorize and track your transactions in real-time\",\n  },\n  {\n    icon: <PieChart className=\"h-8 w-8 text-blue-600\" />,\n    title: \"3. Get Insights\",\n    description:\n      \"Receive AI-powered insights and recommendations to optimize your finances\",\n  },\n];\n\n// Testimonials Data\nexport const testimonialsData = [\n  {\n    name: \"Sarah Johnson\",\n    role: \"Small Business Owner\",\n    image: \"https://randomuser.me/api/portraits/women/75.jpg\",\n    quote:\n      \"Welth has transformed how I manage my business finances. The AI insights have helped me identify cost-saving opportunities I never knew existed.\",\n  },\n  {\n    name: \"Michael Chen\",\n    role: \"Freelancer\",\n    image: \"https://randomuser.me/api/portraits/men/75.jpg\",\n    quote:\n      \"The receipt scanning feature saves me hours each month. Now I can focus on my work instead of manual data entry and expense tracking.\",\n  },\n  {\n    name: \"Emily Rodriguez\",\n    role: \"Financial Advisor\",\n    image: \"https://randomuser.me/api/portraits/women/74.jpg\",\n    quote:\n      \"I recommend Welth to all my clients. The multi-currency support and detailed analytics make it perfect for international investors.\",\n  },\n];\n"
  },
  {
    "path": "emails/template.jsx",
    "content": "import {\n  Body,\n  Container,\n  Head,\n  Heading,\n  Html,\n  Preview,\n  Section,\n  Text,\n} from \"@react-email/components\";\n\n// Dummy data for preview\nconst PREVIEW_DATA = {\n  monthlyReport: {\n    userName: \"John Doe\",\n    type: \"monthly-report\",\n    data: {\n      month: \"December\",\n      stats: {\n        totalIncome: 5000,\n        totalExpenses: 3500,\n        byCategory: {\n          housing: 1500,\n          groceries: 600,\n          transportation: 400,\n          entertainment: 300,\n          utilities: 700,\n        },\n      },\n      insights: [\n        \"Your housing expenses are 43% of your total spending - consider reviewing your housing costs.\",\n        \"Great job keeping entertainment expenses under control this month!\",\n        \"Setting up automatic savings could help you save 20% more of your income.\",\n      ],\n    },\n  },\n  budgetAlert: {\n    userName: \"John Doe\",\n    type: \"budget-alert\",\n    data: {\n      percentageUsed: 85,\n      budgetAmount: 4000,\n      totalExpenses: 3400,\n    },\n  },\n};\n\nexport default function EmailTemplate({\n  userName = \"\",\n  type = \"monthly-report\",\n  data = {},\n}) {\n  if (type === \"monthly-report\") {\n    return (\n      <Html>\n        <Head />\n        <Preview>Your Monthly Financial Report</Preview>\n        <Body style={styles.body}>\n          <Container style={styles.container}>\n            <Heading style={styles.title}>Monthly Financial Report</Heading>\n\n            <Text style={styles.text}>Hello {userName},</Text>\n            <Text style={styles.text}>\n              Here&rsquo;s your financial summary for {data?.month}:\n            </Text>\n\n            {/* Main Stats */}\n            <Section style={styles.statsContainer}>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Total Income</Text>\n                <Text style={styles.heading}>${data?.stats.totalIncome}</Text>\n              </div>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Total Expenses</Text>\n                <Text style={styles.heading}>${data?.stats.totalExpenses}</Text>\n              </div>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Net</Text>\n                <Text style={styles.heading}>\n                  ${data?.stats.totalIncome - data?.stats.totalExpenses}\n                </Text>\n              </div>\n            </Section>\n\n            {/* Category Breakdown */}\n            {data?.stats?.byCategory && (\n              <Section style={styles.section}>\n                <Heading style={styles.heading}>Expenses by Category</Heading>\n                {Object.entries(data?.stats.byCategory).map(\n                  ([category, amount]) => (\n                    <div key={category} style={styles.row}>\n                      <Text style={styles.text}>{category}</Text>\n                      <Text style={styles.text}>${amount}</Text>\n                    </div>\n                  )\n                )}\n              </Section>\n            )}\n\n            {/* AI Insights */}\n            {data?.insights && (\n              <Section style={styles.section}>\n                <Heading style={styles.heading}>Welth Insights</Heading>\n                {data.insights.map((insight, index) => (\n                  <Text key={index} style={styles.text}>\n                    • {insight}\n                  </Text>\n                ))}\n              </Section>\n            )}\n\n            <Text style={styles.footer}>\n              Thank you for using Welth. Keep tracking your finances for better\n              financial health!\n            </Text>\n          </Container>\n        </Body>\n      </Html>\n    );\n  }\n\n  if (type === \"budget-alert\") {\n    return (\n      <Html>\n        <Head />\n        <Preview>Budget Alert</Preview>\n        <Body style={styles.body}>\n          <Container style={styles.container}>\n            <Heading style={styles.title}>Budget Alert</Heading>\n            <Text style={styles.text}>Hello {userName},</Text>\n            <Text style={styles.text}>\n              You&rsquo;ve used {data?.percentageUsed.toFixed(1)}% of your\n              monthly budget.\n            </Text>\n            <Section style={styles.statsContainer}>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Budget Amount</Text>\n                <Text style={styles.heading}>${data?.budgetAmount}</Text>\n              </div>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Spent So Far</Text>\n                <Text style={styles.heading}>${data?.totalExpenses}</Text>\n              </div>\n              <div style={styles.stat}>\n                <Text style={styles.text}>Remaining</Text>\n                <Text style={styles.heading}>\n                  ${data?.budgetAmount - data?.totalExpenses}\n                </Text>\n              </div>\n            </Section>\n          </Container>\n        </Body>\n      </Html>\n    );\n  }\n}\n\nconst styles = {\n  body: {\n    backgroundColor: \"#f6f9fc\",\n    fontFamily: \"-apple-system, sans-serif\",\n  },\n  container: {\n    backgroundColor: \"#ffffff\",\n    margin: \"0 auto\",\n    padding: \"20px\",\n    borderRadius: \"5px\",\n    boxShadow: \"0 2px 4px rgba(0, 0, 0, 0.1)\",\n  },\n  title: {\n    color: \"#1f2937\",\n    fontSize: \"32px\",\n    fontWeight: \"bold\",\n    textAlign: \"center\",\n    margin: \"0 0 20px\",\n  },\n  heading: {\n    color: \"#1f2937\",\n    fontSize: \"20px\",\n    fontWeight: \"600\",\n    margin: \"0 0 16px\",\n  },\n  text: {\n    color: \"#4b5563\",\n    fontSize: \"16px\",\n    margin: \"0 0 16px\",\n  },\n  section: {\n    marginTop: \"32px\",\n    padding: \"20px\",\n    backgroundColor: \"#f9fafb\",\n    borderRadius: \"5px\",\n    border: \"1px solid #e5e7eb\",\n  },\n  statsContainer: {\n    margin: \"32px 0\",\n    padding: \"20px\",\n    backgroundColor: \"#f9fafb\",\n    borderRadius: \"5px\",\n  },\n  stat: {\n    marginBottom: \"16px\",\n    padding: \"12px\",\n    backgroundColor: \"#fff\",\n    borderRadius: \"4px\",\n    boxShadow: \"0 1px 2px rgba(0, 0, 0, 0.05)\",\n  },\n  row: {\n    display: \"flex\",\n    justifyContent: \"space-between\",\n    padding: \"12px 0\",\n    borderBottom: \"1px solid #e5e7eb\",\n  },\n  footer: {\n    color: \"#6b7280\",\n    fontSize: \"14px\",\n    textAlign: \"center\",\n    marginTop: \"32px\",\n    paddingTop: \"16px\",\n    borderTop: \"1px solid #e5e7eb\",\n  },\n};\n"
  },
  {
    "path": "hooks/use-fetch.js",
    "content": "import { useState } from \"react\";\nimport { toast } from \"sonner\";\n\nconst useFetch = (cb) => {\n  const [data, setData] = useState(undefined);\n  const [loading, setLoading] = useState(null);\n  const [error, setError] = useState(null);\n\n  const fn = async (...args) => {\n    setLoading(true);\n    setError(null);\n\n    try {\n      const response = await cb(...args);\n      setData(response);\n      setError(null);\n    } catch (error) {\n      setError(error);\n      toast.error(error.message);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return { data, loading, error, fn, setData };\n};\n\nexport default useFetch;\n"
  },
  {
    "path": "jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "lib/arcjet.js",
    "content": "import arcjet, { tokenBucket } from \"@arcjet/next\";\n\nconst aj = arcjet({\n  key: process.env.ARCJET_KEY,\n  characteristics: [\"userId\"], // Track based on Clerk userId\n  rules: [\n    // Rate limiting specifically for collection creation\n    tokenBucket({\n      mode: \"LIVE\",\n      refillRate: 10, // 10 collections\n      interval: 3600, // per hour\n      capacity: 10, // maximum burst capacity\n    }),\n  ],\n});\n\nexport default aj;\n"
  },
  {
    "path": "lib/checkUser.js",
    "content": "import { currentUser } from \"@clerk/nextjs/server\";\nimport { db } from \"./prisma\";\n\nexport const checkUser = async () => {\n  const user = await currentUser();\n\n  if (!user) {\n    return null;\n  }\n\n  try {\n    const loggedInUser = await db.user.findUnique({\n      where: {\n        clerkUserId: user.id,\n      },\n    });\n\n    if (loggedInUser) {\n      return loggedInUser;\n    }\n\n    const name = `${user.firstName} ${user.lastName}`;\n\n    const newUser = await db.user.create({\n      data: {\n        clerkUserId: user.id,\n        name,\n        imageUrl: user.imageUrl,\n        email: user.emailAddresses[0].emailAddress,\n      },\n    });\n\n    return newUser;\n  } catch (error) {\n    console.log(error.message);\n  }\n};\n"
  },
  {
    "path": "lib/inngest/client.js",
    "content": "import { Inngest } from \"inngest\";\n\nexport const inngest = new Inngest({\n  id: \"finance-platform\", // Unique app ID\n  name: \"Finance Platform\",\n  retryFunction: async (attempt) => ({\n    delay: Math.pow(2, attempt) * 1000, // Exponential backoff\n    maxAttempts: 2,\n  }),\n});\n"
  },
  {
    "path": "lib/inngest/function.js",
    "content": "import { inngest } from \"./client\";\nimport { db } from \"@/lib/prisma\";\nimport EmailTemplate from \"@/emails/template\";\nimport { sendEmail } from \"@/actions/send-email\";\nimport { GoogleGenerativeAI } from \"@google/generative-ai\";\n\n// 1. Recurring Transaction Processing with Throttling\nexport const processRecurringTransaction = inngest.createFunction(\n  {\n    id: \"process-recurring-transaction\",\n    name: \"Process Recurring Transaction\",\n    throttle: {\n      limit: 10, // Process 10 transactions\n      period: \"1m\", // per minute\n      key: \"event.data.userId\", // Throttle per user\n    },\n  },\n  { event: \"transaction.recurring.process\" },\n  async ({ event, step }) => {\n    // Validate event data\n    if (!event?.data?.transactionId || !event?.data?.userId) {\n      console.error(\"Invalid event data:\", event);\n      return { error: \"Missing required event data\" };\n    }\n\n    await step.run(\"process-transaction\", async () => {\n      const transaction = await db.transaction.findUnique({\n        where: {\n          id: event.data.transactionId,\n          userId: event.data.userId,\n        },\n        include: {\n          account: true,\n        },\n      });\n\n      if (!transaction || !isTransactionDue(transaction)) return;\n\n      // Create new transaction and update account balance in a transaction\n      await db.$transaction(async (tx) => {\n        // Create new transaction\n        await tx.transaction.create({\n          data: {\n            type: transaction.type,\n            amount: transaction.amount,\n            description: `${transaction.description} (Recurring)`,\n            date: new Date(),\n            category: transaction.category,\n            userId: transaction.userId,\n            accountId: transaction.accountId,\n            isRecurring: false,\n          },\n        });\n\n        // Update account balance\n        const balanceChange =\n          transaction.type === \"EXPENSE\"\n            ? -transaction.amount.toNumber()\n            : transaction.amount.toNumber();\n\n        await tx.account.update({\n          where: { id: transaction.accountId },\n          data: { balance: { increment: balanceChange } },\n        });\n\n        // Update last processed date and next recurring date\n        await tx.transaction.update({\n          where: { id: transaction.id },\n          data: {\n            lastProcessed: new Date(),\n            nextRecurringDate: calculateNextRecurringDate(\n              new Date(),\n              transaction.recurringInterval\n            ),\n          },\n        });\n      });\n    });\n  }\n);\n\n// Trigger recurring transactions with batching\nexport const triggerRecurringTransactions = inngest.createFunction(\n  {\n    id: \"trigger-recurring-transactions\", // Unique ID,\n    name: \"Trigger Recurring Transactions\",\n  },\n  { cron: \"0 0 * * *\" }, // Daily at midnight\n  async ({ step }) => {\n    const recurringTransactions = await step.run(\n      \"fetch-recurring-transactions\",\n      async () => {\n        return await db.transaction.findMany({\n          where: {\n            isRecurring: true,\n            status: \"COMPLETED\",\n            OR: [\n              { lastProcessed: null },\n              {\n                nextRecurringDate: {\n                  lte: new Date(),\n                },\n              },\n            ],\n          },\n        });\n      }\n    );\n\n    // Send event for each recurring transaction in batches\n    if (recurringTransactions.length > 0) {\n      const events = recurringTransactions.map((transaction) => ({\n        name: \"transaction.recurring.process\",\n        data: {\n          transactionId: transaction.id,\n          userId: transaction.userId,\n        },\n      }));\n\n      // Send events directly using inngest.send()\n      await inngest.send(events);\n    }\n\n    return { triggered: recurringTransactions.length };\n  }\n);\n\n// 2. Monthly Report Generation\nasync function generateFinancialInsights(stats, month) {\n  const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);\n  const model = genAI.getGenerativeModel({ model: \"gemini-1.5-flash\" });\n\n  const prompt = `\n    Analyze this financial data and provide 3 concise, actionable insights.\n    Focus on spending patterns and practical advice.\n    Keep it friendly and conversational.\n\n    Financial Data for ${month}:\n    - Total Income: $${stats.totalIncome}\n    - Total Expenses: $${stats.totalExpenses}\n    - Net Income: $${stats.totalIncome - stats.totalExpenses}\n    - Expense Categories: ${Object.entries(stats.byCategory)\n      .map(([category, amount]) => `${category}: $${amount}`)\n      .join(\", \")}\n\n    Format the response as a JSON array of strings, like this:\n    [\"insight 1\", \"insight 2\", \"insight 3\"]\n  `;\n\n  try {\n    const result = await model.generateContent(prompt);\n    const response = result.response;\n    const text = response.text();\n    const cleanedText = text.replace(/```(?:json)?\\n?/g, \"\").trim();\n\n    return JSON.parse(cleanedText);\n  } catch (error) {\n    console.error(\"Error generating insights:\", error);\n    return [\n      \"Your highest expense category this month might need attention.\",\n      \"Consider setting up a budget for better financial management.\",\n      \"Track your recurring expenses to identify potential savings.\",\n    ];\n  }\n}\n\nexport const generateMonthlyReports = inngest.createFunction(\n  {\n    id: \"generate-monthly-reports\",\n    name: \"Generate Monthly Reports\",\n  },\n  { cron: \"0 0 1 * *\" }, // First day of each month\n  async ({ step }) => {\n    const users = await step.run(\"fetch-users\", async () => {\n      return await db.user.findMany({\n        include: { accounts: true },\n      });\n    });\n\n    for (const user of users) {\n      await step.run(`generate-report-${user.id}`, async () => {\n        const lastMonth = new Date();\n        lastMonth.setMonth(lastMonth.getMonth() - 1);\n\n        const stats = await getMonthlyStats(user.id, lastMonth);\n        const monthName = lastMonth.toLocaleString(\"default\", {\n          month: \"long\",\n        });\n\n        // Generate AI insights\n        const insights = await generateFinancialInsights(stats, monthName);\n\n        await sendEmail({\n          to: user.email,\n          subject: `Your Monthly Financial Report - ${monthName}`,\n          react: EmailTemplate({\n            userName: user.name,\n            type: \"monthly-report\",\n            data: {\n              stats,\n              month: monthName,\n              insights,\n            },\n          }),\n        });\n      });\n    }\n\n    return { processed: users.length };\n  }\n);\n\n// 3. Budget Alerts with Event Batching\nexport const checkBudgetAlerts = inngest.createFunction(\n  { name: \"Check Budget Alerts\" },\n  { cron: \"0 */6 * * *\" }, // Every 6 hours\n  async ({ step }) => {\n    const budgets = await step.run(\"fetch-budgets\", async () => {\n      return await db.budget.findMany({\n        include: {\n          user: {\n            include: {\n              accounts: {\n                where: {\n                  isDefault: true,\n                },\n              },\n            },\n          },\n        },\n      });\n    });\n\n    for (const budget of budgets) {\n      const defaultAccount = budget.user.accounts[0];\n      if (!defaultAccount) continue; // Skip if no default account\n\n      await step.run(`check-budget-${budget.id}`, async () => {\n        const startDate = new Date();\n        startDate.setDate(1); // Start of current month\n\n        // Calculate total expenses for the default account only\n        const expenses = await db.transaction.aggregate({\n          where: {\n            userId: budget.userId,\n            accountId: defaultAccount.id, // Only consider default account\n            type: \"EXPENSE\",\n            date: {\n              gte: startDate,\n            },\n          },\n          _sum: {\n            amount: true,\n          },\n        });\n\n        const totalExpenses = expenses._sum.amount?.toNumber() || 0;\n        const budgetAmount = budget.amount;\n        const percentageUsed = (totalExpenses / budgetAmount) * 100;\n\n        // Check if we should send an alert\n        if (\n          percentageUsed >= 80 && // Default threshold of 80%\n          (!budget.lastAlertSent ||\n            isNewMonth(new Date(budget.lastAlertSent), new Date()))\n        ) {\n          await sendEmail({\n            to: budget.user.email,\n            subject: `Budget Alert for ${defaultAccount.name}`,\n            react: EmailTemplate({\n              userName: budget.user.name,\n              type: \"budget-alert\",\n              data: {\n                percentageUsed,\n                budgetAmount: parseInt(budgetAmount).toFixed(1),\n                totalExpenses: parseInt(totalExpenses).toFixed(1),\n                accountName: defaultAccount.name,\n              },\n            }),\n          });\n\n          // Update last alert sent\n          await db.budget.update({\n            where: { id: budget.id },\n            data: { lastAlertSent: new Date() },\n          });\n        }\n      });\n    }\n  }\n);\n\nfunction isNewMonth(lastAlertDate, currentDate) {\n  return (\n    lastAlertDate.getMonth() !== currentDate.getMonth() ||\n    lastAlertDate.getFullYear() !== currentDate.getFullYear()\n  );\n}\n\n// Utility functions\nfunction isTransactionDue(transaction) {\n  // If no lastProcessed date, transaction is due\n  if (!transaction.lastProcessed) return true;\n\n  const today = new Date();\n  const nextDue = new Date(transaction.nextRecurringDate);\n\n  // Compare with nextDue date\n  return nextDue <= today;\n}\n\nfunction calculateNextRecurringDate(date, interval) {\n  const next = new Date(date);\n  switch (interval) {\n    case \"DAILY\":\n      next.setDate(next.getDate() + 1);\n      break;\n    case \"WEEKLY\":\n      next.setDate(next.getDate() + 7);\n      break;\n    case \"MONTHLY\":\n      next.setMonth(next.getMonth() + 1);\n      break;\n    case \"YEARLY\":\n      next.setFullYear(next.getFullYear() + 1);\n      break;\n  }\n  return next;\n}\n\nasync function getMonthlyStats(userId, month) {\n  const startDate = new Date(month.getFullYear(), month.getMonth(), 1);\n  const endDate = new Date(month.getFullYear(), month.getMonth() + 1, 0);\n\n  const transactions = await db.transaction.findMany({\n    where: {\n      userId,\n      date: {\n        gte: startDate,\n        lte: endDate,\n      },\n    },\n  });\n\n  return transactions.reduce(\n    (stats, t) => {\n      const amount = t.amount.toNumber();\n      if (t.type === \"EXPENSE\") {\n        stats.totalExpenses += amount;\n        stats.byCategory[t.category] =\n          (stats.byCategory[t.category] || 0) + amount;\n      } else {\n        stats.totalIncome += amount;\n      }\n      return stats;\n    },\n    {\n      totalExpenses: 0,\n      totalIncome: 0,\n      byCategory: {},\n      transactionCount: transactions.length,\n    }\n  );\n}\n"
  },
  {
    "path": "lib/prisma.js",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\nexport const db = globalThis.prisma || new PrismaClient();\n\nif (process.env.NODE_ENV !== \"production\") {\n  globalThis.prisma = db;\n}\n\n// globalThis.prisma: This global variable ensures that the Prisma client instance is\n// reused across hot reloads during development. Without this, each time your application\n// reloads, a new instance of the Prisma client would be created, potentially leading\n// to connection issues.\n"
  },
  {
    "path": "lib/utils.js",
    "content": "import { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs) {\n  return twMerge(clsx(inputs));\n}\n"
  },
  {
    "path": "middleware.js",
    "content": "import arcjet, { createMiddleware, detectBot, shield } from \"@arcjet/next\";\nimport { clerkMiddleware, createRouteMatcher } from \"@clerk/nextjs/server\";\nimport { NextResponse } from \"next/server\";\n\nconst isProtectedRoute = createRouteMatcher([\n  \"/dashboard(.*)\",\n  \"/account(.*)\",\n  \"/transaction(.*)\",\n]);\n\n// Create Arcjet middleware\nconst aj = arcjet({\n  key: process.env.ARCJET_KEY,\n  // characteristics: [\"userId\"], // Track based on Clerk userId\n  rules: [\n    // Shield protection for content and security\n    shield({\n      mode: \"LIVE\",\n    }),\n    detectBot({\n      mode: \"LIVE\", // will block requests. Use \"DRY_RUN\" to log only\n      allow: [\n        \"CATEGORY:SEARCH_ENGINE\", // Google, Bing, etc\n        \"GO_HTTP\", // For Inngest\n        // See the full list at https://arcjet.com/bot-list\n      ],\n    }),\n  ],\n});\n\n// Create base Clerk middleware\nconst clerk = clerkMiddleware(async (auth, req) => {\n  const { userId } = await auth();\n\n  if (!userId && isProtectedRoute(req)) {\n    const { redirectToSignIn } = await auth();\n    return redirectToSignIn();\n  }\n\n  return NextResponse.next();\n});\n\n// Chain middlewares - ArcJet runs first, then Clerk\nexport default createMiddleware(aj, clerk);\n\nexport const config = {\n  matcher: [\n    // Skip Next.js internals and all static files, unless found in search params\n    \"/((?!_next|[^?]*\\\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)\",\n    // Always run for API routes\n    \"/(api|trpc)(.*)\",\n  ],\n};\n"
  },
  {
    "path": "next.config.mjs",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: \"https\",\n        hostname: \"randomuser.me\",\n      },\n    ],\n  },\n\n  experimental: {\n    serverActions: {\n      bodySizeLimit: \"5mb\",\n    },\n  },\n};\n\nexport default nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"finance-platform\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev --turbopack\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\",\n    \"email\": \"email dev\",\n    \"postinstall\": \"prisma generate\"\n  },\n  \"dependencies\": {\n    \"@arcjet/next\": \"^1.0.0-alpha.34\",\n    \"@clerk/nextjs\": \"^6.6.0\",\n    \"@google/generative-ai\": \"^0.21.0\",\n    \"@hookform/resolvers\": \"^3.9.1\",\n    \"@prisma/client\": \"^6.0.1\",\n    \"@radix-ui/react-checkbox\": \"^1.1.2\",\n    \"@radix-ui/react-dialog\": \"^1.1.2\",\n    \"@radix-ui/react-dropdown-menu\": \"^2.1.2\",\n    \"@radix-ui/react-popover\": \"^1.1.2\",\n    \"@radix-ui/react-progress\": \"^1.1.0\",\n    \"@radix-ui/react-select\": \"^2.1.2\",\n    \"@radix-ui/react-slot\": \"^1.1.0\",\n    \"@radix-ui/react-switch\": \"^1.1.1\",\n    \"@radix-ui/react-tooltip\": \"^1.1.4\",\n    \"@react-email/components\": \"0.0.30\",\n    \"class-variance-authority\": \"^0.7.1\",\n    \"clsx\": \"^2.1.1\",\n    \"date-fns\": \"^4.1.0\",\n    \"inngest\": \"^3.27.4\",\n    \"lucide-react\": \"^0.462.0\",\n    \"next\": \"15.0.5\",\n    \"next-themes\": \"^0.4.3\",\n    \"react\": \"^19.0.0-rc-66855b96-20241106\",\n    \"react-day-picker\": \"^8.10.1\",\n    \"react-dom\": \"^19.0.0-rc-66855b96-20241106\",\n    \"react-hook-form\": \"^7.53.2\",\n    \"react-spinners\": \"^0.14.1\",\n    \"recharts\": \"^2.14.1\",\n    \"resend\": \"^4.0.1\",\n    \"sonner\": \"^1.7.0\",\n    \"tailwind-merge\": \"^2.5.5\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"vaul\": \"^1.1.1\",\n    \"zod\": \"^3.23.8\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8\",\n    \"eslint-config-next\": \"15.0.3\",\n    \"postcss\": \"^8\",\n    \"prisma\": \"^6.0.1\",\n    \"react-email\": \"3.0.3\",\n    \"tailwindcss\": \"^3.4.1\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.mjs",
    "content": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n  plugins: {\n    tailwindcss: {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "prisma/migrations/20241204141034_init/migration.sql",
    "content": "-- CreateEnum\nCREATE TYPE \"TransactionType\" AS ENUM ('INCOME', 'EXPENSE');\n\n-- CreateEnum\nCREATE TYPE \"AccountType\" AS ENUM ('CURRENT', 'SAVINGS');\n\n-- CreateEnum\nCREATE TYPE \"TransactionStatus\" AS ENUM ('PENDING', 'COMPLETED', 'FAILED');\n\n-- CreateEnum\nCREATE TYPE \"BudgetPeriod\" AS ENUM ('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY');\n\n-- CreateEnum\nCREATE TYPE \"RecurringInterval\" AS ENUM ('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY');\n\n-- CreateTable\nCREATE TABLE \"users\" (\n    \"id\" TEXT NOT NULL,\n    \"clerkUserId\" TEXT NOT NULL,\n    \"email\" TEXT NOT NULL,\n    \"name\" TEXT,\n    \"imageUrl\" TEXT,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"users_pkey\" PRIMARY KEY (\"id\")\n);\n\n-- CreateTable\nCREATE TABLE \"accounts\" (\n    \"id\" TEXT NOT NULL,\n    \"name\" TEXT NOT NULL,\n    \"type\" \"AccountType\" NOT NULL,\n    \"balance\" DECIMAL(65,30) NOT NULL DEFAULT 0,\n    \"currency\" TEXT NOT NULL,\n    \"isDefault\" BOOLEAN NOT NULL DEFAULT false,\n    \"userId\" TEXT NOT NULL,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"accounts_pkey\" PRIMARY KEY (\"id\")\n);\n\n-- CreateTable\nCREATE TABLE \"categories\" (\n    \"id\" TEXT NOT NULL,\n    \"name\" TEXT NOT NULL,\n    \"type\" \"TransactionType\" NOT NULL,\n    \"color\" TEXT,\n    \"icon\" TEXT,\n    \"userId\" TEXT NOT NULL,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"categories_pkey\" PRIMARY KEY (\"id\")\n);\n\n-- CreateTable\nCREATE TABLE \"transactions\" (\n    \"id\" TEXT NOT NULL,\n    \"type\" \"TransactionType\" NOT NULL,\n    \"amount\" DECIMAL(65,30) NOT NULL,\n    \"currency\" TEXT NOT NULL,\n    \"description\" TEXT,\n    \"date\" TIMESTAMP(3) NOT NULL,\n    \"receiptUrl\" TEXT,\n    \"isRecurring\" BOOLEAN NOT NULL DEFAULT false,\n    \"recurringInterval\" \"RecurringInterval\",\n    \"nextRecurringDate\" TIMESTAMP(3),\n    \"status\" \"TransactionStatus\" NOT NULL DEFAULT 'COMPLETED',\n    \"notes\" TEXT,\n    \"userId\" TEXT NOT NULL,\n    \"categoryId\" TEXT NOT NULL,\n    \"accountId\" TEXT NOT NULL,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"transactions_pkey\" PRIMARY KEY (\"id\")\n);\n\n-- CreateTable\nCREATE TABLE \"budgets\" (\n    \"id\" TEXT NOT NULL,\n    \"amount\" DECIMAL(65,30) NOT NULL,\n    \"currency\" TEXT NOT NULL,\n    \"period\" \"BudgetPeriod\" NOT NULL,\n    \"startDate\" TIMESTAMP(3) NOT NULL,\n    \"endDate\" TIMESTAMP(3),\n    \"userId\" TEXT NOT NULL,\n    \"categoryId\" TEXT NOT NULL,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"budgets_pkey\" PRIMARY KEY (\"id\")\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"users_clerkUserId_key\" ON \"users\"(\"clerkUserId\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"users_email_key\" ON \"users\"(\"email\");\n\n-- CreateIndex\nCREATE INDEX \"accounts_userId_idx\" ON \"accounts\"(\"userId\");\n\n-- CreateIndex\nCREATE INDEX \"categories_userId_idx\" ON \"categories\"(\"userId\");\n\n-- CreateIndex\nCREATE INDEX \"transactions_userId_idx\" ON \"transactions\"(\"userId\");\n\n-- CreateIndex\nCREATE INDEX \"transactions_categoryId_idx\" ON \"transactions\"(\"categoryId\");\n\n-- CreateIndex\nCREATE INDEX \"transactions_accountId_idx\" ON \"transactions\"(\"accountId\");\n\n-- CreateIndex\nCREATE INDEX \"budgets_userId_idx\" ON \"budgets\"(\"userId\");\n\n-- CreateIndex\nCREATE INDEX \"budgets_categoryId_idx\" ON \"budgets\"(\"categoryId\");\n\n-- AddForeignKey\nALTER TABLE \"accounts\" ADD CONSTRAINT \"accounts_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"users\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"categories\" ADD CONSTRAINT \"categories_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"users\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"transactions\" ADD CONSTRAINT \"transactions_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"users\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"transactions\" ADD CONSTRAINT \"transactions_categoryId_fkey\" FOREIGN KEY (\"categoryId\") REFERENCES \"categories\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"transactions\" ADD CONSTRAINT \"transactions_accountId_fkey\" FOREIGN KEY (\"accountId\") REFERENCES \"accounts\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"budgets\" ADD CONSTRAINT \"budgets_userId_fkey\" FOREIGN KEY (\"userId\") REFERENCES \"users\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n-- AddForeignKey\nALTER TABLE \"budgets\" ADD CONSTRAINT \"budgets_categoryId_fkey\" FOREIGN KEY (\"categoryId\") REFERENCES \"categories\"(\"id\") ON DELETE CASCADE ON UPDATE CASCADE;\n"
  },
  {
    "path": "prisma/migrations/20241205074927_remove_currency/migration.sql",
    "content": "/*\n  Warnings:\n\n  - You are about to drop the column `currency` on the `accounts` table. All the data in the column will be lost.\n  - You are about to drop the column `currency` on the `budgets` table. All the data in the column will be lost.\n  - You are about to drop the column `currency` on the `transactions` table. All the data in the column will be lost.\n\n*/\n-- AlterTable\nALTER TABLE \"accounts\" DROP COLUMN \"currency\";\n\n-- AlterTable\nALTER TABLE \"budgets\" DROP COLUMN \"currency\";\n\n-- AlterTable\nALTER TABLE \"transactions\" DROP COLUMN \"currency\";\n"
  },
  {
    "path": "prisma/migrations/20241205094020_remove_categories/migration.sql",
    "content": "/*\n  Warnings:\n\n  - You are about to drop the column `categoryId` on the `budgets` table. All the data in the column will be lost.\n  - You are about to drop the column `categoryId` on the `transactions` table. All the data in the column will be lost.\n  - You are about to drop the `categories` table. If the table is not empty, all the data it contains will be lost.\n  - Added the required column `category` to the `budgets` table without a default value. This is not possible if the table is not empty.\n  - Added the required column `category` to the `transactions` table without a default value. This is not possible if the table is not empty.\n\n*/\n-- DropForeignKey\nALTER TABLE \"budgets\" DROP CONSTRAINT \"budgets_categoryId_fkey\";\n\n-- DropForeignKey\nALTER TABLE \"categories\" DROP CONSTRAINT \"categories_userId_fkey\";\n\n-- DropForeignKey\nALTER TABLE \"transactions\" DROP CONSTRAINT \"transactions_categoryId_fkey\";\n\n-- DropIndex\nDROP INDEX \"budgets_categoryId_idx\";\n\n-- DropIndex\nDROP INDEX \"transactions_categoryId_idx\";\n\n-- AlterTable\nALTER TABLE \"budgets\" DROP COLUMN \"categoryId\",\nADD COLUMN     \"category\" TEXT NOT NULL;\n\n-- AlterTable\nALTER TABLE \"transactions\" DROP COLUMN \"categoryId\",\nADD COLUMN     \"category\" TEXT NOT NULL;\n\n-- DropTable\nDROP TABLE \"categories\";\n"
  },
  {
    "path": "prisma/migrations/20241205094352_remove_categories/migration.sql",
    "content": "/*\n  Warnings:\n\n  - You are about to drop the column `category` on the `budgets` table. All the data in the column will be lost.\n\n*/\n-- AlterTable\nALTER TABLE \"budgets\" DROP COLUMN \"category\";\n"
  },
  {
    "path": "prisma/migrations/20241206121749_budget/migration.sql",
    "content": "/*\n  Warnings:\n\n  - You are about to drop the column `endDate` on the `budgets` table. All the data in the column will be lost.\n  - You are about to drop the column `period` on the `budgets` table. All the data in the column will be lost.\n  - You are about to drop the column `startDate` on the `budgets` table. All the data in the column will be lost.\n  - A unique constraint covering the columns `[userId]` on the table `budgets` will be added. If there are existing duplicate values, this will fail.\n\n*/\n-- DropIndex\nDROP INDEX \"budgets_userId_idx\";\n\n-- AlterTable\nALTER TABLE \"budgets\" DROP COLUMN \"endDate\",\nDROP COLUMN \"period\",\nDROP COLUMN \"startDate\";\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"budgets_userId_key\" ON \"budgets\"(\"userId\");\n"
  },
  {
    "path": "prisma/migrations/20241208092553_budget/migration.sql",
    "content": "-- DropIndex\nDROP INDEX \"budgets_userId_key\";\n\n-- AlterTable\nALTER TABLE \"budgets\" ADD COLUMN     \"lastAlertSent\" TIMESTAMP(3);\n\n-- AlterTable\nALTER TABLE \"transactions\" ADD COLUMN     \"lastProcessed\" TIMESTAMP(3);\n\n-- DropEnum\nDROP TYPE \"BudgetPeriod\";\n\n-- CreateIndex\nCREATE INDEX \"budgets_userId_idx\" ON \"budgets\"(\"userId\");\n"
  },
  {
    "path": "prisma/migrations/20241208122341_budget/migration.sql",
    "content": "/*\n  Warnings:\n\n  - A unique constraint covering the columns `[userId]` on the table `budgets` will be added. If there are existing duplicate values, this will fail.\n\n*/\n-- CreateIndex\nCREATE UNIQUE INDEX \"budgets_userId_key\" ON \"budgets\"(\"userId\");\n"
  },
  {
    "path": "prisma/migrations/20241209133842_remove/migration.sql",
    "content": "/*\n  Warnings:\n\n  - You are about to drop the column `notes` on the `transactions` table. All the data in the column will be lost.\n\n*/\n-- AlterTable\nALTER TABLE \"transactions\" DROP COLUMN \"notes\";\n"
  },
  {
    "path": "prisma/migrations/migration_lock.toml",
    "content": "# Please do not edit this file manually\n# It should be added in your version-control system (i.e. Git)\nprovider = \"postgresql\""
  },
  {
    "path": "prisma/schema.prisma",
    "content": "generator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider  = \"postgresql\"\n  url       = env(\"DATABASE_URL\")\n  directUrl = env(\"DIRECT_URL\")\n}\n\nmodel User {\n  id            String    @id @default(uuid())\n  clerkUserId   String    @unique // clerk user id\n  email         String    @unique\n  name          String?\n  imageUrl      String?\n  transactions  Transaction[]\n  accounts      Account[]\n  budgets       Budget[]\n  createdAt     DateTime      @default(now())\n  updatedAt     DateTime      @updatedAt\n\n  @@map(\"users\")\n}\n\nmodel Account {\n  id           String        @id @default(uuid())\n  name         String\n  type         AccountType\n  balance      Decimal       @default(0) // will ask inital balance while creating an account\n  isDefault    Boolean       @default(false)\n  userId       String\n  user         User          @relation(fields: [userId], references: [id], onDelete: Cascade)\n  transactions Transaction[]\n  createdAt    DateTime      @default(now())\n  updatedAt    DateTime      @updatedAt\n\n  @@index([userId])\n  @@map(\"accounts\")\n}\n\nmodel Transaction {\n  id                String            @id @default(uuid())\n  type             TransactionType\n  amount           Decimal\n  description      String?\n  date             DateTime\n  category         String           \n  receiptUrl       String?\n  isRecurring      Boolean           @default(false)\n  recurringInterval RecurringInterval? // Only used if isRecurring is true\n  nextRecurringDate DateTime?         // Next date for recurring transaction\n  lastProcessed    DateTime?         // Last time this recurring transaction was processed\n  status           TransactionStatus  @default(COMPLETED)\n  userId           String\n  user             User              @relation(fields: [userId], references: [id], onDelete: Cascade)\n  accountId        String\n  account          Account           @relation(fields: [accountId], references: [id], onDelete: Cascade)\n  createdAt        DateTime          @default(now())\n  updatedAt        DateTime          @updatedAt\n\n  @@index([userId])\n  @@index([accountId])\n  @@map(\"transactions\")\n}\n\n\nmodel Budget {\n  id          String       @id @default(uuid())\n  amount      Decimal\n  lastAlertSent DateTime?  // Track when the last alert was sent\n  userId      String       @unique\n  user        User         @relation(fields: [userId], references: [id], onDelete: Cascade)\n  createdAt   DateTime     @default(now())\n  updatedAt   DateTime     @updatedAt\n\n  @@index([userId])\n  @@map(\"budgets\")\n}\n\nenum TransactionType {\n  INCOME\n  EXPENSE\n}\n\nenum AccountType {\n  CURRENT\n  SAVINGS\n}\n\nenum TransactionStatus {\n  PENDING\n  COMPLETED\n  FAILED\n}\n\nenum RecurringInterval {\n  DAILY\n  WEEKLY\n  MONTHLY\n  YEARLY\n}"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n    darkMode: [\"class\"],\n    content: [\n    \"./pages/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./components/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./app/**/*.{js,ts,jsx,tsx,mdx}\",\n  ],\n  theme: {\n  \textend: {\n  \t\tcolors: {\n  \t\t\tbackground: 'hsl(var(--background))',\n  \t\t\tforeground: 'hsl(var(--foreground))',\n  \t\t\tcard: {\n  \t\t\t\tDEFAULT: 'hsl(var(--card))',\n  \t\t\t\tforeground: 'hsl(var(--card-foreground))'\n  \t\t\t},\n  \t\t\tpopover: {\n  \t\t\t\tDEFAULT: 'hsl(var(--popover))',\n  \t\t\t\tforeground: 'hsl(var(--popover-foreground))'\n  \t\t\t},\n  \t\t\tprimary: {\n  \t\t\t\tDEFAULT: 'hsl(var(--primary))',\n  \t\t\t\tforeground: 'hsl(var(--primary-foreground))'\n  \t\t\t},\n  \t\t\tsecondary: {\n  \t\t\t\tDEFAULT: 'hsl(var(--secondary))',\n  \t\t\t\tforeground: 'hsl(var(--secondary-foreground))'\n  \t\t\t},\n  \t\t\tmuted: {\n  \t\t\t\tDEFAULT: 'hsl(var(--muted))',\n  \t\t\t\tforeground: 'hsl(var(--muted-foreground))'\n  \t\t\t},\n  \t\t\taccent: {\n  \t\t\t\tDEFAULT: 'hsl(var(--accent))',\n  \t\t\t\tforeground: 'hsl(var(--accent-foreground))'\n  \t\t\t},\n  \t\t\tdestructive: {\n  \t\t\t\tDEFAULT: 'hsl(var(--destructive))',\n  \t\t\t\tforeground: 'hsl(var(--destructive-foreground))'\n  \t\t\t},\n  \t\t\tborder: 'hsl(var(--border))',\n  \t\t\tinput: 'hsl(var(--input))',\n  \t\t\tring: 'hsl(var(--ring))',\n  \t\t\tchart: {\n  \t\t\t\t'1': 'hsl(var(--chart-1))',\n  \t\t\t\t'2': 'hsl(var(--chart-2))',\n  \t\t\t\t'3': 'hsl(var(--chart-3))',\n  \t\t\t\t'4': 'hsl(var(--chart-4))',\n  \t\t\t\t'5': 'hsl(var(--chart-5))'\n  \t\t\t}\n  \t\t},\n  \t\tborderRadius: {\n  \t\t\tlg: 'var(--radius)',\n  \t\t\tmd: 'calc(var(--radius) - 2px)',\n  \t\t\tsm: 'calc(var(--radius) - 4px)'\n  \t\t}\n  \t}\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n};\n"
  }
]