mendsalbert / ai-finance-tracking

52 stars 17 forks source link

Error #30

Open MuskanNazim opened 2 weeks ago

MuskanNazim commented 2 weeks ago

not showing any error....but expenses page shows nothing

codeautopilot[bot] commented 2 weeks ago

Potential solution

The primary issue seems to be the lack of error handling and feedback when the expenses list is empty. Additionally, there might be issues with the user object not being correctly populated or the database query failing silently. To solve this, we need to implement error handling, conditional rendering for empty states, and ensure the user object is correctly populated before making the database query.

What is causing this bug?

The bug is likely caused by a combination of factors:

  1. User Object: If the user object is not correctly populated, the getAllExpenses function will not execute, leading to an empty expenses page.
  2. Database Query: If the database query does not return any results or fails silently, the expensesList will remain empty.
  3. Lack of Error Handling: There is no error handling for the database query, so any issues during data fetching are not reported.
  4. Conditional Rendering: The component does not handle the case where expensesList is empty, providing no feedback to the user.

Code

Implementation details and code snippets

1. Update utils/dbConfig.jsx

Move the database connection string to environment variables to enhance security.

import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";

const sql = neon(process.env.DATABASE_URL);

export const db = drizzle(sql, { schema });

2. Update app/(routes)/dashboard/expenses/page.jsx

Add error handling, conditional rendering, and ensure the user object is correctly populated.

import React, { useEffect, useState } from 'react';
import { useUser } from '@clerk/nextjs';
import { db } from '../../../utils/dbConfig';
import { Expenses, Budgets } from '../../../utils/schema';
import ExpenseListTable from './_components/ExpenseListTable';
import { eq, desc } from 'drizzle-orm';

const ExpensesPage = () => {
  const { user } = useUser();
  const [expensesList, setExpensesList] = useState([]);

  const getAllExpenses = async () => {
    try {
      const result = await db.select({
        id: Expenses.id,
        name: Expenses.name,
        amount: Expenses.amount,
        createdAt: Expenses.createdAt
      }).from(Budgets)
      .rightJoin(Expenses, eq(Budgets.id, Expenses.budgetId))
      .where(eq(Budgets.createdBy, user?.primaryEmailAddress.emailAddress))
      .orderBy(desc(Expenses.id));
      setExpensesList(result);
    } catch (error) {
      console.error("Failed to fetch expenses:", error);
    }
  };

  useEffect(() => {
    if (user && user.primaryEmailAddress) {
      getAllExpenses();
    }
  }, [user]);

  return (
    <div className='p-10'>
      <h2 className='font-bold text-3xl'>My Expenses</h2>
      {expensesList.length > 0 ? (
        <ExpenseListTable refreshData={() => getAllExpenses()} expensesList={expensesList} />
      ) : (
        <p>No expenses found.</p>
      )}
    </div>
  );
};

export default ExpensesPage;

3. Update app/(routes)/dashboard/expenses/_components/ExpenseListTable.jsx

Add conditional rendering for empty data, error handling for deletion, and loading states.

import React, { useState } from "react";
import { toast } from "react-toastify";
import { db } from "../../../utils/dbConfig";
import { Expenses } from "../../../utils/schema";
import { eq } from "drizzle-orm";

function ExpenseListTable({ expensesList, refreshData }) {
  const [loading, setLoading] = useState(false);

  const deleteExpense = async (expense) => {
    setLoading(true);
    try {
      const result = await db
        .delete(Expenses)
        .where(eq(Expenses.id, expense.id))
        .returning();

      if (result) {
        toast("Expense Deleted!");
        refreshData();
      }
    } catch (error) {
      toast.error("Failed to delete expense. Please try again.");
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="mt-3">
      <h2 className="font-bold text-lg">Latest Expenses</h2>
      {loading ? (
        <p>Loading...</p>
      ) : expensesList && expensesList.length > 0 ? (
        <>
          <div className="grid grid-cols-4 rounded-tl-xl rounded-tr-xl bg-slate-200 p-2 mt-3">
            <h2 className="font-bold">Name</h2>
            <h2 className="font-bold">Amount</h2>
            <h2 className="font-bold">Date</h2>
            <h2 className="font-bold">Action</h2>
          </div>
          {expensesList.map((expenses, index) => (
            <div key={index} className="grid grid-cols-4 bg-slate-50 rounded-bl-xl rounded-br-xl p-2">
              <h2>{expenses.name}</h2>
              <h2>{expenses.amount}</h2>
              <h2>{expenses.createdAt}</h2>
              <h2
                onClick={() => deleteExpense(expenses)}
                className="text-red-500 cursor-pointer"
              >
                Delete
              </h2>
            </div>
          ))}
        </>
      ) : (
        <p>No expenses to display.</p>
      )}
    </div>
  );
}

export default ExpenseListTable;

How to replicate the bug

  1. Navigate to the expenses page.
  2. Ensure that the user is logged in.
  3. Observe that the expenses page shows nothing and no error is displayed.

By implementing the above changes, we should be able to handle errors gracefully, provide feedback when no expenses are found, and ensure the user object is correctly populated before making the database query. This should resolve the issue where the expenses page shows nothing.

Click here to create a Pull Request with the proposed solution

Files used for this task:

Changes on utils/dbConfig.jsx # Analysis Report for `utils/dbConfig.jsx` ## Overview The file `utils/dbConfig.jsx` is responsible for setting up the database connection using the `neon` and `drizzle` libraries. It imports the database schema from a local module and establishes a connection to a PostgreSQL database hosted on AWS. ## Code Review Here is the source code for `utils/dbConfig.jsx`: ```javascript import { neon } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-http"; import * as schema from "./schema"; const sql = neon( "postgresql://finan-smart_owner:uk3aed9QZotj@ep-wispy-breeze-a5iadk8t.us-east-2.aws.neon.tech/finan-smart?sslmode=require" ); export const db = drizzle(sql, { schema }); ``` ### Key Components 1. **Database Connection String**: The connection string is hardcoded and includes the username, password, host, and database name. 2. **Neon and Drizzle Libraries**: These libraries are used to establish a connection and interact with the database. 3. **Schema Import**: The schema is imported from a local file, which is essential for defining the database structure. ## Potential Issues 1. **Hardcoded Credentials**: The database credentials are hardcoded in the file, which is a security risk. These should be stored in environment variables. 2. **Connection Issues**: If the connection string is incorrect or the database server is down, the connection will fail. 3. **Schema Mismatch**: If the schema defined in `./schema` does not match the actual database schema, it could lead to issues in data fetching. ## Recommendations 1. **Environment Variables**: Move the database connection string to environment variables to enhance security. 2. **Error Handling**: Implement error handling to catch and log any connection issues. 3. **Schema Validation**: Ensure that the schema defined in `./schema` matches the actual database schema. ## Conclusion The `utils/dbConfig.jsx` file appears to be correctly setting up the database connection using the `neon` and `drizzle` libraries. However, the hardcoded credentials and lack of error handling could be potential issues. These should be addressed to ensure a secure and robust connection to the database. This file alone does not seem to be the direct cause of the issue where the expenses page shows nothing. The problem might lie in the data fetching logic or how the data is passed to and rendered by the components. Further investigation in the other relevant files is necessary.
Changes on app/(routes)/dashboard/expenses/page.jsx # Bug Analysis Report for `app/(routes)/dashboard/expenses/page.jsx` ## Overview The file `app/(routes)/dashboard/expenses/page.jsx` is responsible for fetching and displaying the user's expenses on the expenses page. The user reported that the expenses page shows nothing, and no error is displayed. ## Analysis ### Data Fetching Logic The data fetching logic is encapsulated in the `getAllExpenses` function, which is called within a `useEffect` hook that runs when the `user` object is available. #### Key Points: 1. **Database Query**: ```javascript const result = await db.select({ id: Expenses.id, name: Expenses.name, amount: Expenses.amount, createdAt: Expenses.createdAt }).from(Budgets) .rightJoin(Expenses, eq(Budgets.id, Expenses.budgetId)) .where(eq(Budgets.createdBy, user?.primaryEmailAddress.emailAddress)) .orderBy(desc(Expenses.id)); ``` - The query selects fields from the `Expenses` table and performs a right join with the `Budgets` table. - It filters the results based on the `createdBy` field in the `Budgets` table, matching it with the user's primary email address. 2. **State Management**: - The fetched data is stored in the `expensesList` state using `setExpensesList(result)`. 3. **Component Rendering**: - The `ExpenseListTable` component is rendered with the `expensesList` passed as a prop. ### Potential Issues 1. **User Object**: - The `user` object is fetched using the `useUser` hook from `@clerk/nextjs`. If the `user` object is not correctly populated, the `getAllExpenses` function will not execute. 2. **Database Query**: - If the database query does not return any results, the `expensesList` will remain empty. - There is no error handling for the database query. If the query fails, it will silently fail without notifying the user. 3. **Conditional Rendering**: - The component does not handle the case where `expensesList` is empty. It should provide feedback to the user if no expenses are found. ## Recommendations 1. **Add Error Handling**: - Wrap the database query in a try-catch block to handle any potential errors. ```javascript const getAllExpenses = async () => { try { const result = await db.select({ id: Expenses.id, name: Expenses.name, amount: Expenses.amount, createdAt: Expenses.createdAt }).from(Budgets) .rightJoin(Expenses, eq(Budgets.id, Expenses.budgetId)) .where(eq(Budgets.createdBy, user?.primaryEmailAddress.emailAddress)) .orderBy(desc(Expenses.id)); setExpensesList(result); } catch (error) { console.error("Failed to fetch expenses:", error); } } ``` 2. **Handle Empty State**: - Add conditional rendering to handle the case where `expensesList` is empty. ```javascript return (

My Expenses

{expensesList.length > 0 ? ( getAllExpenses()} expensesList={expensesList} /> ) : (

No expenses found.

)}
); ``` 3. **Verify User Object**: - Ensure that the `user` object is correctly populated before making the database query. ```javascript useEffect(() => { if (user && user.primaryEmailAddress) { getAllExpenses(); } }, [user]); ``` ## Conclusion The primary issue seems to be the lack of error handling and feedback when the expenses list is empty. Implementing the above recommendations should help in diagnosing and resolving the issue where the expenses page shows nothing.
Changes on app/(routes)/dashboard/expenses/_components/ExpenseListTable.jsx # Analysis Report for `ExpenseListTable.jsx` ## Overview The `ExpenseListTable.jsx` file is responsible for rendering a list of expenses and providing functionality to delete an expense. The component receives `expensesList` and `refreshData` as props, which are used to display the expenses and refresh the data after an expense is deleted. ## Key Areas to Investigate 1. **Data Reception and Rendering**: Ensure that the `expensesList` prop is correctly received and rendered. 2. **Error Handling**: Check if there is adequate error handling for scenarios where data fetching or deletion fails. 3. **Loading States**: Verify if there are loading states to improve user experience during data fetching or deletion. ## Findings ### Data Reception and Rendering - The component correctly maps over the `expensesList` prop to render each expense. - There is no conditional rendering to handle cases where `expensesList` might be empty or undefined. ### Error Handling - The `deleteExpense` function has basic error handling using a toast notification for successful deletion but lacks error handling for failed deletion attempts. - There is no error handling for scenarios where `expensesList` might be empty or undefined. ### Loading States - The component does not include any loading states to indicate data fetching or deletion in progress. ## Recommendations ### 1. Conditional Rendering for Empty Data Add conditional rendering to handle cases where `expensesList` is empty or undefined. This will improve user experience by providing feedback when there are no expenses to display. ```jsx return (

Latest Expenses

{expensesList && expensesList.length > 0 ? ( <>

Name

Amount

Date

Action

{expensesList.map((expenses, index) => (

{expenses.name}

{expenses.amount}

{expenses.createdAt}

deleteExpense(expenses)} className="text-red-500 cursor-pointer" > Delete

))} ) : (

No expenses to display.

)}
); ``` ### 2. Error Handling for Deletion Add error handling for the `deleteExpense` function to notify the user if the deletion fails. ```jsx const deleteExpense = async (expense) => { try { const result = await db .delete(Expenses) .where(eq(Expenses.id, expense.id)) .returning(); if (result) { toast("Expense Deleted!"); refreshData(); } } catch (error) { toast.error("Failed to delete expense. Please try again."); } }; ``` ### 3. Loading States Introduce loading states to indicate when data is being fetched or an expense is being deleted. ```jsx import React, { useState } from "react"; function ExpenseListTable({ expensesList, refreshData }) { const [loading, setLoading] = useState(false); const deleteExpense = async (expense) => { setLoading(true); try { const result = await db .delete(Expenses) .where(eq(Expenses.id, expense.id)) .returning(); if (result) { toast("Expense Deleted!"); refreshData(); } } catch (error) { toast.error("Failed to delete expense. Please try again."); } finally { setLoading(false); } }; return (

Latest Expenses

{loading ? (

Loading...

) : expensesList && expensesList.length > 0 ? ( <>

Name

Amount

Date

Action

{expensesList.map((expenses, index) => (

{expenses.name}

{expenses.amount}

{expenses.createdAt}

deleteExpense(expenses)} className="text-red-500 cursor-pointer" > Delete

))} ) : (

No expenses to display.

)}
); } export default ExpenseListTable; ``` ## Conclusion The `ExpenseListTable.jsx` file is mostly functional but lacks some user experience improvements such as conditional rendering for empty data, error handling for deletion failures, and loading states. Implementing these recommendations should help address the issue of the expenses page showing nothing.