Skip to main content

CashGap Features

CashGap provides a complete suite of financial management tools to help users take control of their money.

Dashboard Overview

The dashboard is the central hub that displays all your financial data at a glance.

Financial Metrics

Total Balance

Real-time calculation of income minus expenses and subscriptions

Income Tracking

Total income with source count and breakdown by frequency

Expense Summary

Total expenses with item count and category breakdown

Active Subscriptions

Monthly and yearly subscription costs with renewal tracking

Dashboard Implementation

The dashboard aggregates data from multiple sources:
app/(dashboard)/dashboard/page.tsx
import { useDashboardData } from "@/hooks";

export default function DashboardPage() {
  const { data, isLoading } = useDashboardData();
  const incomes = data?.incomes ?? [];
  const expenses = data?.expenses ?? [];
  const subscriptions = data?.subscriptions ?? [];

  // Calculate totals
  const stats = useMemo(() => {
    const totalIncome = incomes.reduce((sum, i) => sum + i.amount, 0);
    const totalExpenses = expenses.reduce((sum, e) => sum + e.amount, 0);
    const totalSubscriptions = subscriptions
      .filter(s => s.active)
      .reduce((sum, s) => sum + s.amount, 0);
    const totalBalance = totalIncome - totalExpenses - totalSubscriptions;

    return { totalBalance, totalIncome, totalExpenses, totalSubscriptions };
  }, [incomes, expenses, subscriptions]);

  return (
    <DashboardWrapper>
      {/* Display financial metrics */}
    </DashboardWrapper>
  );
}

Recent Transactions

The dashboard displays the 5 most recent transactions combining income and expenses:
const recentTransactions = useMemo(() => {
  const allTransactions = [
    ...incomes.map(i => ({
      id: i.id,
      name: i.name,
      date: i.date,
      amount: i.amount,
      type: "income" as const,
    })),
    ...expenses.map(e => ({
      id: e.id,
      name: e.name,
      date: e.date,
      amount: -e.amount,
      type: "expense" as const,
    })),
  ];

  return allTransactions
    .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
    .slice(0, 5);
}, [incomes, expenses]);

Income Management

Income Features

Income tracking supports one-time, monthly, and yearly frequencies to accommodate various income sources like salaries, bonuses, freelance work, and investments.
Key Capabilities:
  • Create income entries with name, amount, and date
  • Categorize by frequency (once, monthly, yearly)
  • Add optional notes and categories
  • Edit and delete existing entries
  • Filter by date range and category

Income Statistics

The income page displays three key metrics:
app/income/page.tsx
const totalIncome = incomes.reduce((sum, i) => sum + i.amount, 0);
const monthlyIncome = incomes
  .filter(i => i.frequency === "monthly")
  .reduce((sum, i) => sum + i.amount, 0);
const yearlyIncome = incomes
  .filter(i => i.frequency === "yearly")
  .reduce((sum, i) => sum + i.amount, 0);

Expense Management

Expense Categories

Each expense is assigned to one of eight categories:
Restaurants, groceries, coffee shops, food delivery
  • Icon: Utensils
  • Color: Orange

Category Configuration

app/expenses/page.tsx
const categoryConfig: Record<ExpenseCategory, {
  label: string;
  icon: React.ComponentType<{ className?: string }>;
  color: string;
  bg: string;
}> = {
  food: {
    label: "Food & Dining",
    icon: Utensils,
    color: "text-orange-500",
    bg: "bg-orange-500/10",
  },
  transport: {
    label: "Transport",
    icon: Car,
    color: "text-blue-500",
    bg: "bg-blue-500/10",
  },
  // ... other categories
};

Spending Analysis

The expenses page includes a category breakdown that shows:
  • Total spending per category
  • Percentage of total expenses
  • Visual progress bars
  • Color-coded category cards
const spendingByCategory = useMemo(() => {
  const categoryTotals: Record<string, number> = {};
  expenses.forEach(e => {
    categoryTotals[e.category] = (categoryTotals[e.category] || 0) + e.amount;
  });

  const totalSpending = Object.values(categoryTotals).reduce((sum, v) => sum + v, 0);

  return Object.entries(categoryTotals)
    .map(([category, amount]) => ({
      category,
      amount,
      percentage: totalSpending > 0 ? (amount / totalSpending) * 100 : 0,
    }))
    .sort((a, b) => b.amount - a.amount)
    .slice(0, 5);
}, [expenses]);

Subscription Tracking

Subscription Features

Active/Inactive Status

Pause subscriptions without deleting them to track seasonal services

Billing Reminders

Get alerts for subscriptions renewing in the next 7 days

Cost Calculations

Automatic monthly and yearly cost calculations regardless of billing frequency

Next Billing Date

Track when each subscription will charge next

Subscription Management

app/subscriptions/page.tsx
// Calculate monthly total (normalize yearly to monthly)
const monthlyTotal = activeSubscriptions.reduce((sum, s) => {
  return sum + (s.frequency === "yearly" ? s.amount / 12 : s.amount);
}, 0);
const yearlyTotal = monthlyTotal * 12;

// Find upcoming renewals (next 7 days)
const today = new Date();
const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
const upcomingRenewals = activeSubscriptions.filter(s => {
  const billingDate = new Date(s.nextBillingDate);
  return billingDate >= today && billingDate <= nextWeek;
});

Subscription Actions

Users can perform these actions on subscriptions:
1

Toggle Active/Paused

const handleToggle = async () => {
  await toggleSubscriptionActive(subscription.id);
  await queryClient.invalidateQueries({ queryKey: queryKeys.dashboard });
};
2

Edit Subscription

Navigate to edit page: /subscriptions/${id}/edit
3

Delete Subscription

const handleDelete = async () => {
  await deleteSubscription(subscription.id);
  await queryClient.invalidateQueries({ queryKey: queryKeys.dashboard });
};

User Interface Components

Collapsible Sections

Dashboard sections use Radix UI’s Collapsible component for better organization:
<Collapsible open={transactionsOpen} onOpenChange={setTransactionsOpen}>
  <CollapsibleTrigger asChild>
    <button className="flex items-center gap-3 w-full group">
      <div className="flex items-center gap-2">
        <ArrowUpDown className="h-5 w-5 text-primary" />
        <h3 className="text-lg font-semibold">Recent Transactions</h3>
      </div>
      <ChevronDown className={cn(
        "h-5 w-5 transition-transform",
        transactionsOpen && "rotate-180"
      )} />
    </button>
  </CollapsibleTrigger>
  <CollapsibleContent>
    {/* Transaction list */}
  </CollapsibleContent>
</Collapsible>
Contextual actions use dropdown menus:
<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <button className="p-2.5 rounded-2xl hover:bg-muted">
      <MoreVertical className="h-4 w-4" />
    </button>
  </DropdownMenuTrigger>
  <DropdownMenuContent align="end">
    <DropdownMenuItem asChild>
      <Link href={`/income/${income.id}/edit`}>
        <Edit className="h-4 w-4 mr-2" />
        Edit
      </Link>
    </DropdownMenuItem>
    <DropdownMenuItem onClick={handleDelete}>
      <Trash2 className="h-4 w-4 mr-2" />
      Delete
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

Data Fetching & Caching

TanStack Query Integration

CashGap uses TanStack Query for efficient data fetching:
hooks/use-finance.ts
export const queryKeys = {
  dashboard: ['dashboard'],
  incomes: ['incomes'],
  expenses: ['expenses'],
  subscriptions: ['subscriptions'],
};

export function useDashboardData() {
  return useQuery({
    queryKey: queryKeys.dashboard,
    queryFn: async () => {
      const response = await fetch('/api/dashboard');
      if (!response.ok) throw new Error('Failed to fetch dashboard data');
      return response.json();
    },
  });
}

Cache Invalidation

After mutations, the cache is invalidated to refetch fresh data:
await queryClient.invalidateQueries({ queryKey: queryKeys.dashboard });

Responsive Design

All pages are fully responsive with mobile-first design:
  • Grid layouts adjust from 1 column on mobile to 2-4 columns on desktop
  • Touch-friendly button sizes (minimum 44x44px)
  • Collapsible navigation on mobile
  • Optimized typography scales

Next Steps

Authentication

Learn about authentication implementation

Income Tracking

Deep dive into income tracking

Expense Management

Explore expense management details

Subscriptions

Manage recurring subscriptions

Build docs developers (and LLMs) love