frontend Tutorial

Building an Expense Tracker Frontend - Part 3: Dashboard & Expenses List

In this part, we will build the main Dashboard of our application, where users can see a list of their recent expenses.

Expenses API

First, let's define the API call to fetch expenses.

// src/api/expenses.ts
import axios from 'axios';

const API_URL = 'http://localhost:9000/api';

export interface Expense {
  id: number;
  description: string;
  amount: number;
  currency: string;
  date: string;
  category: string;
}

export const getExpenses = async (userId: number) => {
  const response = await axios.get(`${API_URL}/users/${userId}/expenses`);
  return response.data;
};

Expense List Component

We'll use useQuery from TanStack Query to fetch the data. This handles loading and error states for us automatically.

// src/components/ExpenseList.tsx
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import { getExpenses, Expense } from '../api/expenses';

interface ExpenseListProps {
  userId: number;
}

const ExpenseList: React.FC<ExpenseListProps> = ({ userId }) => {
  const { isPending, error, data } = useQuery({
    queryKey: ['expenses', userId],
    queryFn: () => getExpenses(userId),
  });

  if (isPending) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <table className="min-w-full divide-y divide-gray-200">
      <thead className="bg-gray-50">
        <tr>
          <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Date</th>
          <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Description</th>
          <th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase">Amount</th>
        </tr>
      </thead>
      <tbody className="bg-white divide-y divide-gray-200">
        {data.map((expense: Expense) => (
          <tr key={expense.id}>
            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
              {new Date(expense.date).toLocaleDateString()}
            </td>
            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
              {expense.description}
            </td>
            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right">
              {expense.amount.toFixed(2)} {expense.currency}
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

export default ExpenseList;

Dashboard Page

Finally, we create the Dashboard page to host our list.

// src/pages/DashboardPage.tsx
import React from 'react';
import ExpenseList from '../components/ExpenseList';

const DashboardPage: React.FC = () => {
  const userId = 1; // Replace with real user ID from auth context

  return (
    <div className="min-h-screen bg-gray-100">
      <nav className="bg-white shadow-sm">
        {/* Navigation Bar */}
      </nav>

      <main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
        <div className="bg-white shadow overflow-hidden sm:rounded-lg">
          <ExpenseList userId={userId} />
        </div>
      </main>
    </div>
  );
};

export default DashboardPage;

Next Steps

In the next part, we will create the forms to Add and Edit expenses.

Continue to Part 4: Add/Edit Expense Forms