Skip to content

Conversation

@shawnSpy
Copy link

@shawnSpy shawnSpy commented Jan 15, 2026

PR #3: Transaction Tags & Grid Feature

What I Did

This PR implements a new transaction tags feature with a many-to-many relationship between transactions and tags, along with a new data grid component that supports server-side pagination and sorting. The changes include:

  • Database Schema Evolution: Created a new Tag model and established a many-to-many relationship with Transaction through a transaction_tags association table
  • Seed Script Updates: Enhanced the seed script to create tags (work, travel, reimbursable, personal, business, recurring, one-time) and automatically assign them to transactions based on transaction characteristics
  • New Backend API Endpoint: Implemented GET /api/v1/transactions/grid endpoint with server-side pagination (page, size) and sorting (sort_by, sort_order) using raw SQL queries with JOINs
  • Frontend Grid Component: Built a new TransactionGrid React component that displays transactions in a sortable, paginated table with tag badges

Depends On

This PR builds on PR #2 (fix/haunted-codebase branch). The feature branch was created from fix/haunted-codebase to ensure a stable codebase foundation.

How to Test

  1. Setup Database Schema:

    docker compose -p dev -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.dev.yml exec backend php init.php
  2. Seed the Database:

    docker compose -p dev -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.dev.yml exec backend ./seed.sh
  3. Start the Application (if not already running):

    ./run.sh
  4. Test the Grid Endpoint:

    • Navigate to http://localhost:3000 in your browser
    • Scroll down to see the "Transaction Grid" section below the existing transaction list
    • Verify that transactions are displayed with tags as colored badges
    • Test pagination by clicking "Next" and "Previous" buttons
    • Test sorting by clicking on column headers (Date, Description, Amount, Category)
    • Verify that sorting toggles between ascending and descending order
  5. Test API Directly:

    curl "http://localhost:8000/api/v1/transactions/grid?page=1&size=10&sort_by=date&sort_order=desc"

    Should return JSON with items array and total count.

Architectural Decision Record (ADR)

Database Schema Evolution:

  • Chose a many-to-many relationship between Transaction and Tag entities to allow transactions to have multiple tags (e.g., "work", "travel", "reimbursable")
  • Used Doctrine ORM's @ManyToMany annotation with a transaction_tags join table
  • Maintained backward compatibility by keeping the existing category relationship intact

Backend API Design:

  • Created a separate /api/v1/transactions/grid endpoint to avoid breaking changes to existing transaction endpoints
  • Used raw SQL queries with JOINs for performance, aggregating tags using PostgreSQL's JSON_AGG function
  • Implemented server-side pagination (default 10 items per page) to ensure pagination is necessary with seed data
  • Used whitelist validation for sort columns to prevent SQL injection

Frontend State Management:

  • Implemented server-side pagination and sorting to handle large datasets efficiently
  • Used React hooks (useState, useEffect, useCallback) for state management
  • Pagination state (page, total) and sorting state (sort_by, sort_order) trigger new API calls when changed
  • Reset to page 1 when sorting changes to provide intuitive UX

Component Design:

  • Built using standard HTML table elements as requested, avoiding third-party grid libraries
  • Tags displayed as Tailwind CSS badge components for visual clarity
  • Responsive design with mobile-friendly pagination controls

AI Usage Summary

AI tools were used for:

  • Boilerplate code generation of the TransactionGrid component
  • Code review and linting assistance

Achilles added 3 commits January 14, 2026 20:14
- Backend: Add eager loading of category relationship in TransactionController::index()
  to prevent N+1 queries when fetching transactions list
- Frontend: Memoize TransactionList component and use useCallback for
  handleUserProfileClick to prevent unnecessary re-renders when App state changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant