Skip to content

Conversation

@FedericoLeiva12
Copy link

@FedericoLeiva12 FedericoLeiva12 commented Oct 10, 2025

📝 Description

  • Implemented accounts, transactions, categories and users routes, schemas and services in backend.
  • Adjusted first login flow to correctly create account in database.
  • Generated schemas with all new models.
  • Created seed script to generate initial database data.
  • Added backend docs.

TODO (pending)

Refactor home screen to be able to manage new backend types and replace mock data with real backend data.

  • Bug fix
  • New feature
  • Refactoring
  • Documentation update
  • Other (please specify)

✅ Checklist

  • Code has been tested and works as expected (White/ Darkmode)
  • Documented Code (Please read your code, think about what might be unclear, and explain your code.)
  • Clear Pull Request title and description (Explain "What have you changed?")
  • All new text uses translations. If you added any UI text to the Finance.io frontend, ensure it's internationalized
  • TypeScript is used with clear, explicit types
  • NativeWind/Tailwind CSS is used consistently
  • Only approved icon packs and libraries are used
  • Relevant tests have been added or updated
  • Documentation has been updated (README, Wiki, etc.)
  • Linter/Formatter runs without errors
  • Branch is up-to-date with master/develop

🔍 Screenshots / GIFs (optional)

🔗 Related Issues

Closes #65
Closes #68

Summary by CodeRabbit

  • New Features

    • Account type classification system with Private, Business, Savings, and Shared account categories.
    • Transaction tracking and management with category and currency support.
    • Multi-currency account support with automatic currency handling.
    • Enhanced account dashboard with organized account sections by type.
  • Documentation

    • Backend development guidelines documenting tech stack, authentication, error handling, and feature-adding checklist.
  • Improvements

    • Improved error handling and standardized error messaging across the application.
    • Updated localization across 20+ languages to support new account and transaction features.

@linear
Copy link

linear bot commented Oct 10, 2025

@graphite-app
Copy link

graphite-app bot commented Oct 10, 2025

How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Complete refactor of the database schema from a single "welcomes" table to a relational multi-table structure (accounts, categories, currencies, transactions, users), with corresponding updates to server routers, type definitions, authentication integration, UI components, and localization to support a more sophisticated financial application architecture.

Changes

Cohort / File(s) Change Summary
Database Migrations
drizzle/0000_unique_mercury.sql, drizzle/0001_nostalgic_master_mold.sql, drizzle/0002_careless_gladiator.sql
New schema with accounts, categories, currencies, transactions, users tables using UUID primary keys, foreign key constraints, and indexes. Migration 0001 adds account_types table with typeId tracking. Migration 0002 replaces typeId with references enum (private, business, savings, shared).
Migration Metadata
drizzle/meta/0000_snapshot.json, drizzle/meta/0001_snapshot.json, drizzle/meta/0002_snapshot.json, drizzle/meta/_journal.json, .idea/sqldialects.xml
Updated Drizzle snapshots reflecting schema evolution; migration journal entries updated with timestamps and breakpoints. IDE configuration added for PostgreSQL SQL dialects.
Database Schema Codification
src/db/schema.ts
Replaced welcomeTable with usersTable, currenciesTable, categoriesTable, accountsTable, transactionsTable; added referencesEnum for account types; UUID defaults and high-precision decimals in string mode.
Server Router Restructure
src/server/api/root.ts, src/server/api/trpc.ts
Replaced post/welcome routers with users/accounts/transactions/categories routers. Integrated Clerk client into protectedProcedure for async user context fetching; updated error handling with centralized error factory.
New Server Routers
src/server/routers/users.ts, src/server/routers/accounts.ts, src/server/routers/categories.ts, src/server/routers/transactions.ts
Implemented user management, account CRUD with currency resolution, category listing, and transaction management with user ownership enforcement and relational data joining.
Removed Server Router
src/server/routers/post.ts, src/server/routers/welcome.ts
Deleted obsolete publicProcedure hello endpoint and welcome account creation router.
Re-export Layer
src/server/index.ts
Updated to re-export appRouter from ./api/root instead of local definition.
Error Handling Infrastructure
src/errors/messages.ts, src/errors/trpc.ts
Added centralized error message registry with i18n keys and TRPC error factory methods (notAuthenticated, forbidden, badRequest, notFound, conflict, internal, tooManyRequests).
Type System Refactor
src/types/index.ts
Introduced ClerkUser, Category, Currency, AccountWithCurrency, TransactionWithCategoryAndAccount; removed Card and DashboardData; updated Account and Transaction interfaces with new fields.
Schema Validation
src/schemas/welcomeSchema.ts
Migrated reference field from string validation to referencesEnum; added References type derived from references constant array.
Authentication Flow
src/app/(auth)/sign-in.tsx, src/app/(auth)/sign-up.tsx
Updated sign-in redirect from /start to /; integrated createUser mutation in sign-up flow post-verification.
Root Navigation
src/app/index.tsx
Implemented two-stage authenticated redirect: route to /start if no accounts, route to /(tabs)/banking if accounts exist; added loading guard to prevent flicker.
Onboarding Screen
src/app/start/index.tsx
Changed tRPC mutation to accounts.addAccount; replaced reference TextInput with Select component using referencesEnum options; updated payload mapping (currentAmount → currentBalance).
Dashboard Banking Screen
src/app/(tabs)/banking/index.tsx
Replaced mock data with TRPC-fetched accounts; pre-filtered accounts into Private, Business, Savings, Shared sections via memoized lists; introduced AccountSection reusable component with staggered animations.
UI Components
src/components/ui/select.tsx, src/components/AccountItem.tsx
Implemented new Select component with modal picker; disabled icon rendering in AccountItem (code remains commented).
Mock Data Removal
src/data/mockData.ts
Deleted entire module with mockDashboardData export and related mock structures.
Localization Updates
localization/{en,ar,bn,de,es,fa,fr,ha,hi,id,ja,ko,ps,pt,ru,sw,th,tr,ur,vi,zh}/common.json
Replaced dashboardSafeAccounts key across all 20 language files with dashboardSavings and dashboardShared; added dashboardDailyAccounts and account type keys (Private, Business) in select languages.
Backend Development Documentation
docs/backend-development.md
Added comprehensive guidelines covering tech stack (Node/TypeScript, tRPC, Drizzle, PostgreSQL, Zod), directory layout, authentication/authorization patterns, error handling, response typing, and feature addition checklist.
Database Seeding & Scripts
scripts/seed.ts, package.json
Implemented database seeding for currencies (USD, EUR, ARS) and categories (Groceries, Restaurants, Transport, Bills, Entertainment); added npm scripts for seed, db:migrate, db:studio, db:generate.
Project Initialization
src/index.ts
Removed seed initialization from application startup.

Sequence Diagram

sequenceDiagram
    participant User
    participant App
    participant Clerk
    participant TRPC
    participant DB

    rect rgb(200, 220, 240)
    Note over User,DB: Account Creation Flow
    User->>App: Submit account form (bankName, currentBalance, reference, usage)
    App->>TRPC: accounts.addAccount()
    TRPC->>Clerk: Fetch current user context
    Clerk-->>TRPC: Return ClerkUser
    TRPC->>DB: Check if user exists in users table
    alt User not in DB
        TRPC->>DB: Insert new user row
    end
    TRPC->>DB: Resolve EUR currency ID
    TRPC->>DB: INSERT new account (userId, bankName, currentBalance, reference, usage, currencyId)
    DB-->>TRPC: Return inserted account + currency details
    TRPC-->>App: AccountWithCurrency (account + currency data)
    App->>User: Navigate to /(tabs)/banking
    end

    rect rgb(240, 200, 220)
    Note over User,DB: Account Retrieval Flow
    User->>App: Load banking dashboard
    App->>TRPC: accounts.getAccounts.useQuery()
    TRPC->>DB: SELECT accounts JOIN currencies WHERE userId = ?
    DB-->>TRPC: Return AccountWithCurrency[]
    TRPC-->>App: Filtered list of accounts
    alt Accounts exist
        App->>App: Pre-filter into reference buckets (Private, Business, Savings, Shared)
        App->>User: Render AccountSections with staggered animations
    else No accounts
        App->>User: Redirect to /start onboarding
    end
    end

    rect rgb(200, 240, 220)
    Note over User,DB: Root Route Auth Redirect Logic
    User->>App: Load / (root)
    App->>TRPC: Check if signed in + fetch accounts
    alt Signed in + accounts loaded + empty
        App->>User: Redirect to /start
    else Signed in + accounts exist
        App->>User: Redirect to /(tabs)/banking
    else Not signed in
        App->>User: Render marketing/landing page
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • Database migrations (drizzle/0001_nostalgic_master_mold.sql, drizzle/0002_careless_gladiator.sql) — verify data backfilling logic for account_types to references enum migration, check cascade/restrict delete behavior correctness
  • Relational joins in routers (src/server/routers/accounts.ts, src/server/routers/transactions.ts) — validate foreign key joins return correct nested data structures; confirm ownership checks prevent unauthorized account/transaction access
  • Clerk integration in protectedProcedure (src/server/api/trpc.ts) — ensure async user fetching doesn't introduce race conditions; verify error handling for missing Clerk users
  • Redirect logic in root route (src/app/index.tsx) — confirm loading state prevents navigation flicker; validate redirect precedence for edge cases (loading, empty array, null)
  • Dashboard section filtering and animation (src/app/(tabs)/banking/index.tsx) — verify memoization prevents unnecessary re-renders; check staggered animation delays don't cause accessibility issues
  • Type safety across new structures (src/types/index.ts, src/db/schema.ts) — ensure InferSelectModel imports align with table definitions; validate Clerk type integration

Possibly related PRs

  • Feat/welcome #23 (Feat/welcome) — Earlier implementation of welcomeRouter and welcomeTable that this PR completely replaces with a new multi-table schema and router architecture
  • update components #32 — Overlapping changes to src/data/mockData.ts, src/components/AccountItem.tsx, and src/types/index.ts with conflicting modifications to mock data and type definitions
  • fix routings #59 — Modifies the same authentication routing files (src/app/(auth)/sign-in.tsx, src/app/(auth)/sign-up.tsx, src/app/start/index.tsx) with overlapping sign-in/sign-up flow changes

Suggested reviewers

  • murtazanoori
  • Gamius00

Poem

🐰 Whiskers twitching with delight

From welcomes flat to schemas bright,
A rabbit hops through currencies and accounts,
With references enum and transaction mounts!
No typeIds hop away—just enum states,
As Clerk unlocks authenticated gates! 🗝️✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR implements backend routes, schemas, and migrations for accounts, transactions, categories, and users as required. Database structure migrations are created and seed scripts generated. However, linked issue #65 lacks detailed acceptance criteria, making full verification inconclusive. Verify that all specific objectives from issue #65 are met by cross-referencing with detailed acceptance criteria if available, or confirm requirements were discussed separately.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly relates to the changeset. It describes implementing backend routes and services for existing screens, which matches the core changes in the PR (new TRPC routers, database schema, migrations).
Out of Scope Changes check ✅ Passed Changes include backend implementation, database migrations, schema definitions, API routers, localization updates, and documentation. The PR also includes frontend changes (navigation redirects, new Select component, UI updates) that extend beyond pure backend implementation but align with the first-login flow adjustment and screen integration mentioned in PR description.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch federicoleiva/fin-25-implement-backend-for-existing-screens

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Added account types to the database schema and created related migrations.
- Introduced new API routes for managing accounts and retrieving user data.
- Updated the frontend to handle account creation and retrieval with type support.
- Added error handling for account-related operations.
- Created a backend development guide for consistency and best practices.
- Enhanced the backend development documentation with additional guidelines and best practices.
- Improved code formatting for better readability in various files, including schema and router definitions.
- Added missing newlines in JSON files for consistency.
- Updated mock data structure to align with new account properties.
…Group/finance-io into federicoleiva/fin-25-implement-backend-for-existing-screens
@FedericoLeiva12 FedericoLeiva12 marked this pull request as ready for review October 13, 2025 22:17
@FedericoLeiva12 FedericoLeiva12 changed the title [WIP] FIN-25: Implement backend for existing screens FIN-25: Implement backend for existing screens Oct 13, 2025
- Changed the property name from 'bankName' to 'name' in the mock dashboard data to align with the updated data structure.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/data/mockData.ts (1)

35-47: Inconsistent property: mix of bankName and name will break consumers.

Unify all account entries to use the same key. Since you introduced bankName, align the rest.

       {
         id: "2",
-        name: "DKB",
+        bankName: "DKB",
         amount: "232.93€",
         icon: React.createElement(DKBIcon),
         type: "dkb",
       },
...
       {
         id: "3",
-        name: "Revolut",
+        bankName: "Revolut",
         amount: "12.232.93€",
         icon: React.createElement(RevolutIcon),
         type: "revolut",
       },
...
       {
         id: "4",
-        name: "Depot",
+        bankName: "Depot",
         amount: "43.232.93€",
         icon: React.createElement(LandmarkIcon),
         type: "depot",
       },

Optionally annotate mockDashboardData with a type to catch these mismatches early (or remove the unused Account import).

Also applies to: 50-66

🧹 Nitpick comments (13)
src/db/schema.ts (1)

79-80: Consider adding updatedAt timestamps to track modifications.

Add updatedAt to accounts and transactions for auditing and cache invalidation.

   createdAt: timestamp({ withTimezone: true }).defaultNow().notNull(),
+  updatedAt: timestamp({ withTimezone: true }).defaultNow().notNull(),

Note: Without DB triggers, update this field at write time in your services.

Also applies to: 103-104

src/app/index.tsx (1)

158-164: Add a basic loading fallback for UX.

Show a spinner while accounts load.

-  /// TODO: Add loading state
-  // if (isLoadingAccounts) {
-  //   return <View className="flex-1 items-center justify-center">
-  //     <ActivityIndicator size="large" color={iconColor} />
-  //   </View>;
-  // }
+  if (isLoadingAccounts) {
+    return (
+      <View className="flex-1 items-center justify-center">
+        <ActivityIndicator size="large" color={iconColor} />
+      </View>
+    );
+  }

Add import:

-import {
-  View,
-  ScrollView,
-  useColorScheme,
-  TouchableOpacity,
-  Modal,
-} from "react-native";
+import {
+  View,
+  ScrollView,
+  useColorScheme,
+  TouchableOpacity,
+  Modal,
+  ActivityIndicator,
+} from "react-native";
src/scripts/seed.ts (3)

27-34: Make inserts idempotent and race‑safe with onConflictDoNothing().

Avoid unique violations and extra reads by letting Postgres handle conflicts on unique keys.

[Based on learnings]

-  if (toInsert.length > 0) {
-    await db.insert(currenciesTable).values(toInsert);
+  if (toInsert.length > 0) {
+    await db.insert(currenciesTable).values(toInsert).onConflictDoNothing();
     console.log(
       `Inserted currencies: ${toInsert.map((c) => c.code).join(", ")}`,
     );
   } else {

55-62: Apply the same idempotent pattern to categories.

-  if (toInsert.length > 0) {
-    await db.insert(categoriesTable).values(toInsert);
+  if (toInsert.length > 0) {
+    await db.insert(categoriesTable).values(toInsert).onConflictDoNothing();
     console.log(
       `Inserted categories: ${toInsert.map((c) => c.slug).join(", ")}`,
     );
   } else {

82-89: Apply the same idempotent pattern to account types.

-  if (toInsert.length > 0) {
-    await db.insert(accountTypesTable).values(toInsert);
+  if (toInsert.length > 0) {
+    await db.insert(accountTypesTable).values(toInsert).onConflictDoNothing();
     console.log(
       `Inserted account types: ${toInsert.map((c) => c.slug).join(", ")}`,
     );
   } else {
src/errors/trpc.ts (1)

4-6: Make notAuthenticated accept an optional message (API consistency)

Align with other factories to allow overrides.

-  notAuthenticated: () =>
-    new TRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" }),
+  notAuthenticated: (message: string = "Not authenticated") =>
+    new TRPCError({ code: "UNAUTHORIZED", message }),
src/server/routers/transactions.ts (5)

13-21: Tighten input validation (uuid, decimal, coerce date, non-empty strings)

Prevents bad data and client Date serialization issues.

-    .input(
-      z.object({
-        categoryId: z.string(),
-        company: z.string(),
-        amount: z.string(),
-        datetime: z.date(),
-        description: z.string().optional(),
-        accountId: z.string(),
-      }),
-    )
+    .input(
+      z.object({
+        categoryId: z.string().uuid(),
+        company: z.string().min(1),
+        amount: z.string().regex(/^\d+(\.\d{1,2})?$/, "Amount must be a decimal with up to 2 fraction digits"),
+        datetime: z.coerce.date(),
+        description: z.string().optional(),
+        accountId: z.string().uuid(),
+      }),
+    )

85-93: Tighten getTransactions input validation (uuid, coerce date)

-    .input(
-      z.object({
-        accountId: z.string().optional(),
-        categoryId: z.string().optional(),
-        startDate: z.date().optional(),
-        endDate: z.date().optional(),
-      }),
-    )
+    .input(
+      z.object({
+        accountId: z.string().uuid().optional(),
+        categoryId: z.string().uuid().optional(),
+        startDate: z.coerce.date().optional(),
+        endDate: z.coerce.date().optional(),
+      }),
+    )

95-111: Validate date range before querying

Reject inverted ranges early.

-    .query(
-      async ({ ctx, input }): Promise<TransactionWithCategoryAndAccount[]> => {
+    .query(
+      async ({ ctx, input }): Promise<TransactionWithCategoryAndAccount[]> => {
+        if (input.startDate && input.endDate && input.startDate > input.endDate) {
+          throw errors.badRequest("Invalid date range: startDate must be <= endDate");
+        }
         if (input.accountId) {

4-5: Return results in a deterministic order (e.g., newest first)

Improves UX and test stability.

-import { eq, gte, lte, and } from "drizzle-orm";
+import { eq, gte, lte, and, desc } from "drizzle-orm";
@@
-        const qb = db
+        const qb = db
           .select({
             transactions: transactionsTable,
             categories: categoriesTable,
             accounts: accountsTable,
           })
           .from(transactionsTable)
           .innerJoin(
             accountsTable,
             eq(transactionsTable.accountId, accountsTable.id),
           )
           .innerJoin(
             categoriesTable,
             eq(transactionsTable.categoryId, categoriesTable.id),
-          );
+          )
+          .orderBy(desc(transactionsTable.datetime));

Also applies to: 135-150


6-6: Use type-only imports for TransactionWithCategoryAndAccount

-import { TransactionWithCategoryAndAccount } from "~/types";
+import type { TransactionWithCategoryAndAccount } from "~/types";

Enable "verbatimModuleSyntax": true in tsconfig.json to enforce stripping of type-only imports at compile time.

src/server/routers/accounts.ts (2)

13-19: Strengthen input validation (uuid for typeId, decimal for currentBalance, non-empty strings)

-      z.object({
-        bankName: z.string(),
-        currentBalance: z.string(),
-        reference: z.string(),
-        usage: z.string(),
-        typeId: z.string().optional(),
-      }),
+      z.object({
+        bankName: z.string().min(1),
+        currentBalance: z
+          .string()
+          .regex(/^\d+(\.\d{1,2})?$/, "Balance must be a decimal with up to 2 fraction digits"),
+        reference: z.string().min(1),
+        usage: z.string().min(1),
+        typeId: z.string().uuid().optional(),
+      }),

23-31: Avoid hard-coding currency; allow configuration or input

Consider reading a default from config/env, or accept currencyCode in input.

-      const currency = await db
+      const defaultCurrencyCode = process.env.DEFAULT_CURRENCY_CODE ?? "EUR";
+      const currency = await db
         .select()
         .from(currenciesTable)
-        .where(eq(currenciesTable.code, "EUR"))
+        .where(eq(currenciesTable.code, defaultCurrencyCode))
         .limit(1); // Hardcoded to EUR

If you want to keep it hard-coded for now, add a TODO explaining when it will become configurable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d98530c and ee22919.

📒 Files selected for processing (31)
  • docs/backend-development.md (1 hunks)
  • drizzle/0000_mute_thunderbolt_ross.sql (0 hunks)
  • drizzle/0000_unique_mercury.sql (1 hunks)
  • drizzle/0001_nostalgic_master_mold.sql (1 hunks)
  • drizzle/0001_tense_satana.sql (0 hunks)
  • drizzle/0002_icy_madrox.sql (0 hunks)
  • drizzle/meta/0000_snapshot.json (1 hunks)
  • drizzle/meta/0001_snapshot.json (2 hunks)
  • drizzle/meta/0002_snapshot.json (0 hunks)
  • drizzle/meta/_journal.json (1 hunks)
  • package.json (1 hunks)
  • src/app/(auth)/sign-in.tsx (1 hunks)
  • src/app/(tabs)/banking/index.tsx (3 hunks)
  • src/app/index.tsx (2 hunks)
  • src/app/start/index.tsx (2 hunks)
  • src/data/mockData.ts (2 hunks)
  • src/db/schema.ts (1 hunks)
  • src/errors/messages.ts (1 hunks)
  • src/errors/trpc.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/scripts/seed.ts (1 hunks)
  • src/server/api/root.ts (1 hunks)
  • src/server/api/trpc.ts (2 hunks)
  • src/server/index.ts (1 hunks)
  • src/server/routers/accounts.ts (1 hunks)
  • src/server/routers/categories.ts (1 hunks)
  • src/server/routers/post.ts (0 hunks)
  • src/server/routers/transactions.ts (1 hunks)
  • src/server/routers/users.ts (1 hunks)
  • src/server/routers/welcome.ts (0 hunks)
  • src/types/index.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • drizzle/0002_icy_madrox.sql
  • drizzle/0000_mute_thunderbolt_ross.sql
  • src/server/routers/welcome.ts
  • drizzle/meta/0002_snapshot.json
  • src/server/routers/post.ts
  • drizzle/0001_tense_satana.sql
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.tsx: Use instead of from react-native in UI components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet definitions)

Files:

  • src/app/(auth)/sign-in.tsx
  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
  • src/app/start/index.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements across the codebase

Files:

  • src/app/(auth)/sign-in.tsx
  • src/server/routers/categories.ts
  • src/app/(tabs)/banking/index.tsx
  • src/server/routers/accounts.ts
  • src/errors/trpc.ts
  • src/data/mockData.ts
  • src/server/routers/transactions.ts
  • src/index.ts
  • src/server/routers/users.ts
  • src/errors/messages.ts
  • src/app/index.tsx
  • src/scripts/seed.ts
  • src/db/schema.ts
  • src/app/start/index.tsx
  • src/server/api/root.ts
  • src/server/api/trpc.ts
  • src/types/index.ts
  • src/server/index.ts
src/app/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Expo Router file-based routing; screens and routes live under src/app/

Files:

  • src/app/(auth)/sign-in.tsx
  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
  • src/app/start/index.tsx
src/server/routers/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define tRPC procedures/routers under src/server/routers/

Files:

  • src/server/routers/categories.ts
  • src/server/routers/accounts.ts
  • src/server/routers/transactions.ts
  • src/server/routers/users.ts
src/{server,db}/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Database operations must use Drizzle ORM queries (no raw SQL/ad-hoc clients)

Files:

  • src/server/routers/categories.ts
  • src/server/routers/accounts.ts
  • src/server/routers/transactions.ts
  • src/server/routers/users.ts
  • src/db/schema.ts
  • src/server/api/root.ts
  • src/server/api/trpc.ts
  • src/server/index.ts
src/db/schema.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain the database schema in src/db/schema.ts

Files:

  • src/db/schema.ts
🧠 Learnings (2)
📚 Learning: 2025-08-24T14:28:33.804Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T14:28:33.804Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures/routers under src/server/routers/

Applied to files:

  • src/server/routers/categories.ts
  • src/server/routers/accounts.ts
  • src/server/routers/transactions.ts
  • docs/backend-development.md
  • src/server/routers/users.ts
  • src/server/api/root.ts
  • src/server/index.ts
📚 Learning: 2025-08-24T14:28:33.804Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T14:28:33.804Z
Learning: Applies to src/db/schema.ts : Maintain the database schema in src/db/schema.ts

Applied to files:

  • src/db/schema.ts
🧬 Code graph analysis (11)
src/app/(auth)/sign-in.tsx (1)
src/app/(auth)/sign-up.tsx (1)
  • router (293-293)
src/server/routers/categories.ts (4)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • Category (27-31)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (1)
  • categoriesTable (31-41)
src/server/routers/accounts.ts (6)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • AccountWithCurrencyAndType (38-41)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • accountTypesTable (44-54)
  • accountsTable (57-86)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-14)
src/server/routers/transactions.ts (6)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • TransactionWithCategoryAndAccount (53-56)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • accountsTable (57-86)
  • transactionsTable (89-110)
  • categoriesTable (31-41)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-14)
src/index.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (2)
  • currenciesTable (18-28)
  • accountsTable (57-86)
src/server/routers/users.ts (4)
src/server/api/trpc.ts (1)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • User (3-9)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (1)
  • usersTable (13-15)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/scripts/seed.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • categoriesTable (31-41)
  • accountTypesTable (44-54)
src/app/start/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/server/api/root.ts (3)
src/server/api/trpc.ts (1)
  • createTRPCRouter (114-114)
src/server/routers/users.ts (1)
  • usersRouter (7-30)
src/server/routers/accounts.ts (1)
  • accountsRouter (10-100)
src/server/api/trpc.ts (1)
src/errors/trpc.ts (1)
  • errors (3-18)
🔇 Additional comments (14)
src/app/(auth)/sign-in.tsx (1)

100-103: Redirect to root after sign‑in — LGTM

Centralizing post‑login routing via the index screen matches the new accounts‑gating flow.

src/db/schema.ts (1)

12-110: New normalized schema with UUIDs, FKs, and indexes — solid

Good use of decimal(mode:"string"), uniqueness, and relationship indexes. This aligns well with Drizzle’s best practices.

package.json (1)

10-13: Approve DB tooling scripts
drizzle.config.ts and src/scripts/seed.ts confirmed present and correct.

drizzle/meta/_journal.json (1)

8-10: Journal tags match existing migration files. Both SQL files are present; no migration drift detected.

src/data/mockData.ts (1)

11-12: Track/remove mock data soon.

There’s a TODO to remove mocks. Ensure replacement is wired before merge to avoid stale UI.

src/server/index.ts (1)

1-2: LGTM: centralized re‑export.

Keeps the entrypoint thin and aligned with router composition.

src/types/index.ts (2)

20-20: String type for monetary values is appropriate.

Using string for currentBalance (and amount on line 47) is a good practice to avoid floating-point precision issues with monetary values. This aligns with the database's numeric(18, 2) type, which will be serialized as a string by most PostgreSQL drivers.


38-56: Composite types align well with expected API responses.

The AccountWithCurrencyAndType and TransactionWithCategoryAndAccount interfaces provide clean shapes for joined data from the database. This matches the relational model established in the migrations and will simplify data fetching in tRPC routers.

drizzle/0000_unique_mercury.sql (3)

39-42: Foreign key cascades and restrictions are well-designed.

The ON DELETE policies are appropriate:

  • cascade for user-owned data (accounts, transactions) ensures cleanup when a user is deleted
  • restrict for reference data (currencies, categories) prevents accidental deletion of entities still in use

This protects data integrity while allowing proper cleanup of user-specific resources.


43-49: Indexes support expected query patterns.

The index strategy is comprehensive:

  • User and foreign key indexes (accounts_user_idx, accounts_currency_idx, transactions_account_idx, transactions_category_idx) optimize joins and filtered queries
  • Unique indexes on categories.slug and currencies.code prevent duplicates and enable efficient lookups
  • transactions_datetime_idx supports time-range queries

This should provide good performance for typical banking app queries (user accounts, transaction history, category breakdowns).


1-10: Ensure migrations run before code referencing new column
Application code (e.g. src/index.ts, src/server/routers/accounts.ts, src/db/schema.ts) references accounts.typeId, but this migration creates accounts without that column until 0001. Confirm migrations 0000 and 0001 run atomically before app startup to avoid missing-column errors.

drizzle/meta/0000_snapshot.json (1)

1-375: Snapshot correctly reflects migration 0000.

This auto-generated snapshot accurately documents the database state after applying migration 0000_unique_mercury.sql. The schema definition matches the SQL migration, including:

  • accounts table without typeId (added in migration 0001)
  • All foreign keys and indexes as defined
  • Proper use of UUID and varchar types

No issues detected.

drizzle/meta/0001_snapshot.json (1)

1-453: Snapshot correctly reflects complete schema with account_types.

This auto-generated snapshot accurately documents the database state after applying migration 0001_nostalgic_master_mold.sql. Key additions:

  • account_types table with id, name, slug fields (lines 7-54)
  • typeId column added to accounts (lines 96-101)
  • accounts_type_idx index for typeId lookups (lines 147-161)
  • Foreign key constraint linking accounts.typeId to account_types.id (lines 173-181)

The schema now fully aligns with the TypeScript type definitions in src/types/index.ts.

src/server/routers/accounts.ts (1)

5-5: Import types using import type

Change the import to a type-only import to ensure it’s erased at compile time and avoid runtime overhead:

-import { AccountWithCurrencyAndType } from "~/types";
+import type { AccountWithCurrencyAndType } from "~/types";

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
drizzle/meta/0001_snapshot.json (1)

13-114: Add pgcrypto extension before using gen_random_uuid().

All UUID defaults here rely on gen_random_uuid(), but none of the migrations create the pgcrypto extension that provides that function. On a fresh Postgres instance these tables will fail to migrate because gen_random_uuid is undefined. Please add a migration at the top of the stack with CREATE EXTENSION IF NOT EXISTS "pgcrypto"; (or switch to uuid_generate_v4() with uuid-ossp).

Also applies to: 198-347

🧹 Nitpick comments (9)
package.json (1)

10-13: LGTM! Consider documenting the new scripts.

The new database scripts follow standard Drizzle ORM conventions and enable essential workflows (seeding, migrations, schema generation, Studio UI). Ensure that src/scripts/seed.ts exists and that the team is aware of when to run these scripts (e.g., pnpm seed for initial data, pnpm migrate after schema changes).

Consider adding a brief comment in the README or docs explaining:

  • When to run pnpm seed (initial setup, after migrations)
  • When to run pnpm migrate (after running pnpm generate)
  • What pnpm studio provides (Drizzle Studio UI for inspecting data)
src/errors/messages.ts (1)

1-14: Consider whether error message values should differ from keys.

The current structure has error message values that mirror their keys (e.g., forbidden: "forbidden"). This pattern works for internal error codes but might not be ideal if these messages are ever displayed to users or logged for debugging.

If these are meant to be user-facing or logged for diagnostics, consider using descriptive messages:

 export const error_messages = {
   // Generic
-  forbidden: "forbidden",
-  notFound: "notFound",
-  badRequest: "badRequest",
+  forbidden: "Access forbidden",
+  notFound: "Resource not found",
+  badRequest: "Invalid request",

   // Accounts
-  currencyNotFound: "currencyNotFound",
-  accountTypeNotFound: "accountTypeNotFound",
-  accountNotOwned: "accountNotOwned",
+  currencyNotFound: "Currency not found",
+  accountTypeNotFound: "Account type not found",
+  accountNotOwned: "Account does not belong to user",

   // Transactions
-  transactionNotFound: "transactionNotFound",
+  transactionNotFound: "Transaction not found",
 } as const;

If the current pattern is intentional (e.g., for i18n keys), then this comment can be ignored.

drizzle/0000_unique_mercury.sql (1)

1-49: LGTM! Schema design is sound.

The migration establishes a well-structured relational schema with appropriate:

  • Primary keys (UUIDs with gen_random_uuid())
  • Foreign key constraints (cascade for user/account deletion, restrict for reference data)
  • Indexes on commonly queried fields (userId, currencyId, accountId, categoryId, datetime)
  • Unique constraints on business keys (categories.slug, currencies.code)

Minor suggestions:

  1. The accounts.usage and accounts.reference field names are somewhat generic. Consider renaming for clarity (e.g., accountPurpose, referenceNumber or externalReference).
  2. Add a newline at the end of the file per SQL conventions.

These are low-priority refinements and can be addressed in a future migration if needed.

src/app/index.tsx (1)

144-155: Gate the accounts query on authentication

protectedProcedure rejects unauthenticated calls, but we still fire the query while isSignedIn is false, causing needless 401s/local errors. Guard the query with enabled: !!isSignedIn (and optionally reset on sign-out) so we only hit the endpoint after Clerk confirms the session.

-  const { data: accounts, isLoading: isLoadingAccounts } =
-    trpc.accounts.getAccounts.useQuery();
+  const {
+    data: accounts,
+    isLoading: isLoadingAccounts,
+  } = trpc.accounts.getAccounts.useQuery(undefined, {
+    enabled: !!isSignedIn,
+  });
docs/backend-development.md (1)

78-107: Fix example: and(...) doesn’t support .filter.

and() returns an SQL expression, so calling .filter(Boolean) on its result will throw. Build the array first, filter it, then spread into and. Example fix:

-const whereExpr = and(
-  eq(accountsTable.userId, ctx.userId!),
-  input.accountId
-    ? eq(transactionsTable.accountId, input.accountId)
-    : undefined,
-  input.categoryId
-    ? eq(transactionsTable.categoryId, input.categoryId)
-    : undefined,
-  input.startDate
-    ? gte(transactionsTable.datetime, input.startDate)
-    : undefined,
-  input.endDate ? lte(transactionsTable.datetime, input.endDate) : undefined,
-).filter(Boolean) as any; // build conditionally, or build stepwise without arrays
+const whereConditions = [
+  eq(accountsTable.userId, ctx.userId!),
+  input.accountId
+    ? eq(transactionsTable.accountId, input.accountId)
+    : undefined,
+  input.categoryId
+    ? eq(transactionsTable.categoryId, input.categoryId)
+    : undefined,
+  input.startDate
+    ? gte(transactionsTable.datetime, input.startDate)
+    : undefined,
+  input.endDate ? lte(transactionsTable.datetime, input.endDate) : undefined,
+].filter((expr): expr is NonNullable<typeof expr> => Boolean(expr));
+
+const whereExpr =
+  whereConditions.length > 0 ? and(...whereConditions) : undefined;

This keeps the snippet copy-pastable for readers.

src/server/routers/accounts.ts (2)

13-19: Tighten input validation (min lengths, decimal format, UUID)

Current zod schema allows empty strings and any string for balances. Enforce minimal constraints and correct types.

-      z.object({
-        bankName: z.string(),
-        currentBalance: z.string(),
-        reference: z.string(),
-        usage: z.string(),
-        typeId: z.string().optional(),
-      }),
+      z.object({
+        bankName: z.string().min(1),
+        // 18,2 decimal as string (optional leading minus)
+        currentBalance: z
+          .string()
+          .regex(/^-?\d{1,16}(\.\d{1,2})?$/, "Invalid decimal format"),
+        reference: z.string().min(1),
+        usage: z.string().min(1),
+        typeId: z.string().uuid().optional(),
+      }),

23-30: Parameterize hardcoded currency code

EUR is currently seeded but still hardcoded; accept a currencyCode: string (or currencyId: uuid) in the input—defaulting to “EUR” when omitted—to avoid future 404s.

src/server/routers/transactions.ts (2)

41-51: Pre-check category existence or map FK errors to TRPC errors

Currently relies on FK to fail, bubbling a 500. Either pre-check category or catch DB error and respond with NOT_FOUND.

Example pre-check pattern:

const cat = await db
  .select({ id: categoriesTable.id })
  .from(categoriesTable)
  .where(eq(categoriesTable.id, input.categoryId))
  .limit(1);
if (cat.length === 0) {
  throw errors.notFound(error_messages.notFound);
}

15-21: Strengthen transaction router schema

Replace basic string/date validators with stricter checks:

--- src/server/routers/transactions.ts
@@ -15,7 +15,13 @@
-        categoryId: z.string(),
-        company: z.string(),
-        amount: z.string(),
-        datetime: z.date(),
+        categoryId: z.string().uuid(),
+        company: z.string().min(1),
+        amount: z
+          .string()
+          .regex(/^-?\d{1,16}(?:\.\d{1,2})?$/, "Invalid decimal format"),
+        datetime: z
+          .union([z.date(), z.string().datetime()])
+          .transform(v => (v instanceof Date ? v : new Date(v))),
         description: z.string().optional(),
-        accountId: z.string(),
+        accountId: z.string().uuid(),

Project already uses superjson for TRPC, so ISO date strings will be correctly handled.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d98530c and af684b4.

📒 Files selected for processing (31)
  • docs/backend-development.md (1 hunks)
  • drizzle/0000_mute_thunderbolt_ross.sql (0 hunks)
  • drizzle/0000_unique_mercury.sql (1 hunks)
  • drizzle/0001_nostalgic_master_mold.sql (1 hunks)
  • drizzle/0001_tense_satana.sql (0 hunks)
  • drizzle/0002_icy_madrox.sql (0 hunks)
  • drizzle/meta/0000_snapshot.json (1 hunks)
  • drizzle/meta/0001_snapshot.json (2 hunks)
  • drizzle/meta/0002_snapshot.json (0 hunks)
  • drizzle/meta/_journal.json (1 hunks)
  • package.json (1 hunks)
  • src/app/(auth)/sign-in.tsx (1 hunks)
  • src/app/(tabs)/banking/index.tsx (3 hunks)
  • src/app/index.tsx (2 hunks)
  • src/app/start/index.tsx (2 hunks)
  • src/data/mockData.ts (1 hunks)
  • src/db/schema.ts (1 hunks)
  • src/errors/messages.ts (1 hunks)
  • src/errors/trpc.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/scripts/seed.ts (1 hunks)
  • src/server/api/root.ts (1 hunks)
  • src/server/api/trpc.ts (2 hunks)
  • src/server/index.ts (1 hunks)
  • src/server/routers/accounts.ts (1 hunks)
  • src/server/routers/categories.ts (1 hunks)
  • src/server/routers/post.ts (0 hunks)
  • src/server/routers/transactions.ts (1 hunks)
  • src/server/routers/users.ts (1 hunks)
  • src/server/routers/welcome.ts (0 hunks)
  • src/types/index.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • drizzle/0001_tense_satana.sql
  • src/server/routers/welcome.ts
  • drizzle/0002_icy_madrox.sql
  • src/server/routers/post.ts
  • drizzle/0000_mute_thunderbolt_ross.sql
  • drizzle/meta/0002_snapshot.json
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements across the codebase

Files:

  • src/errors/messages.ts
  • src/scripts/seed.ts
  • src/errors/trpc.ts
  • src/server/routers/accounts.ts
  • src/app/(auth)/sign-in.tsx
  • src/index.ts
  • src/app/(tabs)/banking/index.tsx
  • src/data/mockData.ts
  • src/server/routers/users.ts
  • src/server/index.ts
  • src/app/index.tsx
  • src/server/api/root.ts
  • src/server/routers/categories.ts
  • src/server/api/trpc.ts
  • src/server/routers/transactions.ts
  • src/app/start/index.tsx
  • src/types/index.ts
  • src/db/schema.ts
src/server/routers/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define tRPC procedures/routers under src/server/routers/

Files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
  • src/server/routers/categories.ts
  • src/server/routers/transactions.ts
src/{server,db}/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Database operations must use Drizzle ORM queries (no raw SQL/ad-hoc clients)

Files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
  • src/server/index.ts
  • src/server/api/root.ts
  • src/server/routers/categories.ts
  • src/server/api/trpc.ts
  • src/server/routers/transactions.ts
  • src/db/schema.ts
src/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.tsx: Use instead of from react-native in UI components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet definitions)

Files:

  • src/app/(auth)/sign-in.tsx
  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
  • src/app/start/index.tsx
src/app/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Expo Router file-based routing; screens and routes live under src/app/

Files:

  • src/app/(auth)/sign-in.tsx
  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
  • src/app/start/index.tsx
src/db/schema.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain the database schema in src/db/schema.ts

Files:

  • src/db/schema.ts
🧠 Learnings (1)
📚 Learning: 2025-08-24T14:28:33.804Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T14:28:33.804Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures/routers under src/server/routers/

Applied to files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
  • docs/backend-development.md
  • src/server/index.ts
  • src/server/api/root.ts
  • src/server/routers/categories.ts
  • src/server/routers/transactions.ts
🧬 Code graph analysis (10)
src/scripts/seed.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • categoriesTable (31-41)
  • accountTypesTable (44-54)
src/server/routers/accounts.ts (6)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • AccountWithCurrencyAndType (38-41)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • accountTypesTable (44-54)
  • accountsTable (57-86)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-14)
src/index.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (2)
  • currenciesTable (18-28)
  • accountsTable (57-86)
src/server/routers/users.ts (4)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • User (3-9)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (1)
  • usersTable (13-15)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/server/api/root.ts (3)
src/server/api/trpc.ts (1)
  • createTRPCRouter (114-114)
src/server/routers/users.ts (1)
  • usersRouter (7-30)
src/server/routers/accounts.ts (1)
  • accountsRouter (10-100)
src/server/routers/categories.ts (4)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • Category (27-31)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (1)
  • categoriesTable (31-41)
src/server/api/trpc.ts (1)
src/errors/trpc.ts (1)
  • errors (3-18)
src/server/routers/transactions.ts (6)
src/server/api/trpc.ts (2)
  • createTRPCRouter (114-114)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • TransactionWithCategoryAndAccount (53-56)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • accountsTable (57-86)
  • transactionsTable (89-110)
  • categoriesTable (31-41)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-14)
src/app/start/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
🔇 Additional comments (9)
src/app/(auth)/sign-in.tsx (1)

102-102: LGTM! Redirect aligns with new account-state routing.

The redirect to "/" is appropriate given that the index route now handles conditional navigation based on account state. This change aligns with the broader first-login flow refactoring described in the PR objectives.

src/app/(tabs)/banking/index.tsx (1)

64-92: LGTM! Defensive coding pattern is appropriate.

The addition of optional chaining (account?.name) and fallback empty strings prevents potential runtime errors if the name field is undefined or null. This pattern is applied consistently across private, business, and safe account sections.

Note: The PR TODO mentions refactoring this screen to use real backend data instead of mockDashboardData. Once that work is completed, ensure the backend API guarantees that name is always present, which would allow removal of the fallback logic.

src/app/start/index.tsx (3)

47-47: Verify the unused userData query result.

The trpc.users.getUser.useQuery() is called, but the returned userData is not used elsewhere in this component. This might be:

  1. Intentionally prefetching user data for caching
  2. A leftover from refactoring that can be removed
  3. Intended for future use

If this query is not needed, consider removing it to avoid unnecessary API calls:

-  const { data: userData } = trpc.users.getUser.useQuery();
-

If it's intentionally prefetching data, consider adding a comment explaining the purpose.


49-59: LGTM! Backend integration updated correctly.

The mutation change from the old API to accounts.addAccount aligns with the new backend routers. The success callback appropriately resets the form, clears the amount display, and navigates to the banking tab.


79-82: LGTM! Field name updated to match backend schema.

The change from currentAmount to currentBalance aligns with the backend schema defined in the migration. The form still uses currentAmount internally (in the form state), which is fine—the transformation happens only at the mutation boundary.

src/server/api/trpc.ts (2)

12-22: LGTM! Clerk client initialization is correct.

The imports and initialization of clerkClient follow Clerk's recommended patterns. The secretKey is appropriately sourced from the environment variable.


131-148: LGTM! Authentication middleware properly integrated.

The protectedProcedure correctly:

  • Validates ctx.userId presence
  • Fetches the full Clerk user object
  • Throws notAuthenticated() errors when auth fails
  • Augments the context with the verified user object

One consideration: The clerkClient.users.getUser() call adds latency to every protected procedure. If user data is frequently needed but doesn't change often, consider:

  1. Caching the user object in the request context or a short-lived cache
  2. Only fetching user details when required by specific procedures

However, this optimization is optional and can be deferred until performance becomes a concern. For now, the straightforward approach is appropriate.

src/db/schema.ts (2)

56-86: Schema structure looks solid (FKs, indexes, decimal-as-string)

Good use of uuid PKs, decimal mode "string", and targeted indexes. Aligns with Drizzle best practices.


88-110: Nice transaction model and indexes

Appropriate FKs and datetime index for range queries. No issues.

- Refactored the query conditions in the transactions router for better readability and maintainability by using an array to build conditions.
- Updated the user retrieval logic in the users router to use `onConflictDoNothing` for inserting users, ensuring no duplicates are created.
- Enhanced the backend development documentation with clearer examples and improved code formatting.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/app/(tabs)/banking/index.tsx (1)

67-67: Critical: UI still reads name but backend provides bankName.

The past review comment about this issue remains unresolved. The UI reads account?.name, but the backend's AccountWithCurrencyAndType type (see src/types/index.ts line 23) provides bankName. When you replace mockDashboardData with real backend data, all account names will render as empty strings.

Update all three locations to use bankName:

-            onPress={() => handleAccountPress(account?.name || "", account.id)}
+            onPress={() => handleAccountPress(account?.bankName || "", account.id)}

Also update the rendered name prop in the AccountItem component at lines 64, 76, and 89:

-            name={account.name}
+            name={account.bankName}

Also applies to: 79-79, 92-92

🧹 Nitpick comments (2)
src/server/routers/accounts.ts (2)

23-30: Hardcoded currency limits flexibility.

The currency is hardcoded to "EUR" at line 26, which prevents users from creating accounts in other currencies. This may be intentional for an MVP, but consider parameterizing currencyId in the input schema if multi-currency support is planned.


61-71: Consider using spread syntax for cleaner response construction.

The manual field-by-field construction of the AccountWithCurrencyAndType response is verbose. Using spread syntax can improve readability and reduce duplication.

-      return {
-        id: account[0].id,
-        bankName: account[0].bankName,
-        currentBalance: account[0].currentBalance,
-        reference: account[0].reference,
-        usage: account[0].usage,
-        currencyId: account[0].currencyId,
-        currency: currency[0],
-        typeId: account[0].typeId,
-        type: accountType[0],
-      };
+      return {
+        ...account[0],
+        currency: currency[0],
+        type: accountType[0],
+      };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af684b4 and e9ba125.

📒 Files selected for processing (9)
  • docs/backend-development.md (1 hunks)
  • src/app/(tabs)/banking/index.tsx (3 hunks)
  • src/app/index.tsx (2 hunks)
  • src/index.ts (0 hunks)
  • src/server/api/root.ts (1 hunks)
  • src/server/routers/accounts.ts (1 hunks)
  • src/server/routers/transactions.ts (1 hunks)
  • src/server/routers/users.ts (1 hunks)
  • src/types/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/backend-development.md
  • src/server/api/root.ts
  • src/server/routers/transactions.ts
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.tsx: Use instead of from react-native in UI components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet definitions)

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements across the codebase

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/server/routers/accounts.ts
  • src/types/index.ts
  • src/server/routers/users.ts
  • src/app/index.tsx
src/app/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Expo Router file-based routing; screens and routes live under src/app/

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/app/index.tsx
src/server/routers/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define tRPC procedures/routers under src/server/routers/

Files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
src/{server,db}/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Database operations must use Drizzle ORM queries (no raw SQL/ad-hoc clients)

Files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
🧠 Learnings (1)
📚 Learning: 2025-08-24T14:28:33.804Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-24T14:28:33.804Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures/routers under src/server/routers/

Applied to files:

  • src/server/routers/accounts.ts
  • src/server/routers/users.ts
🧬 Code graph analysis (4)
src/app/(tabs)/banking/index.tsx (1)
src/components/AccountItem.tsx (2)
  • AccountItemProps (7-15)
  • useTheme (17-93)
src/server/routers/accounts.ts (6)
src/server/api/trpc.ts (1)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • AccountWithCurrencyAndType (42-45)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • accountTypesTable (44-54)
  • accountsTable (57-86)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-14)
src/server/routers/users.ts (4)
src/server/api/trpc.ts (1)
  • protectedProcedure (131-148)
src/types/index.ts (1)
  • ClerkUser (7-13)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (1)
  • usersTable (13-15)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
🔇 Additional comments (4)
src/types/index.ts (1)

3-13: LGTM! Past naming concern addressed.

The rename from User to ClerkUser and the added comment clarify that this interface represents Clerk authentication data rather than the database schema. This resolves the previous review comment about potential confusion.

src/server/routers/users.ts (1)

7-20: LGTM! Past runtime crash fixed.

The previous critical issue where existingUsers[0] was undefined after an insert has been resolved. The code now uses onConflictDoNothing() for an idempotent upsert and returns the known userId directly, preventing the runtime crash.

src/server/routers/accounts.ts (1)

32-46: LGTM! Past UUID vs slug mismatch fixed.

The previous critical bug where typeId (a UUID) was compared against accountTypesTable.slug has been resolved. The code now correctly uses accountTypesTable.id when typeId is provided and falls back to slug "private" when absent.

src/app/index.tsx (1)

144-157: LGTM! Past authentication and logging concerns resolved.

The previous issues have been addressed:

  • The accounts query is now properly guarded with enabled: isSignedIn, preventing unauthorized calls.
  • PII logging has been removed.
  • The conditional redirect logic correctly routes users based on their account status.


const codes = currencies.map((c) => c.code);

const existing = await db
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please comment it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make more comments, for every function ... etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Collaborator

@Gamius00 Gamius00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please comment:

  • Functions
  • Variables and States

But you dont have to comment your schema or something.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to have everything in one place, and we figured out that the wiki is the best for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A problem we haven't thought about yet then is that if we put everything in the wiki AIs won't be able to access all of that information......... hmmmm......

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean its important for like GH Copilot if it creates a PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only that but also if it works in your IDE. Then it has context and knows what to do.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements comprehensive backend infrastructure for the Finance-IO mobile app, replacing mock data with real database operations. The changes include new tRPC routers for users, accounts, transactions, and categories, a complete database schema redesign with proper relationships and indexes, and improved authentication flow with Clerk integration.

Key Changes:

  • Replaced the temporary welcomeRouter with production-ready routers for users, accounts, transactions, and categories with proper authorization
  • Redesigned database schema from a simple welcomes table to a full relational structure with users, accounts, account types, currencies, categories, and transactions
  • Updated sign-in flow to redirect to the root route, which now intelligently routes users to onboarding or banking based on account existence

Reviewed Changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/types/index.ts Added TypeScript interfaces for backend entities (ClerkUser, Account, Transaction, Category, Currency) with relationship types
src/server/routers/users.ts New users router that fetches Clerk user details and creates user records
src/server/routers/transactions.ts New transactions router with authorization checks for adding and querying transactions
src/server/routers/accounts.ts New accounts router for creating and listing accounts with currency and type relationships
src/server/routers/categories.ts New categories router for retrieving transaction categories
src/server/api/trpc.ts Enhanced protectedProcedure to fetch and attach full Clerk user object to context
src/server/api/root.ts Updated router registration to use new domain routers instead of deprecated ones
src/db/schema.ts Complete schema redesign with proper tables for users, accounts, transactions, categories, currencies, and account types
src/scripts/seed.ts New seed script to populate currencies, categories, and account types
src/app/index.tsx Enhanced root route with conditional routing based on account existence
src/app/start/index.tsx Updated to use new accounts router and getUser query
src/app/(auth)/sign-in.tsx Changed post-sign-in redirect from /start to / for smart routing
src/errors/trpc.ts New standardized error factory for consistent TRPC error handling
src/errors/messages.ts New i18n-ready error message keys
docs/backend-development.md Comprehensive backend development guide documenting patterns and conventions

Copy link
Member

@FleetAdmiralJakob FleetAdmiralJakob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please create a video and upload it here showing how the app looks and works with these features added?

Comment on lines +12 to +14
{ code: "USD", name: "US Dollar" },
{ code: "EUR", name: "Euro" },
{ code: "ARS", name: "Argentine Peso" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we create a type here with all the currencies we want to support and also use that in the schema. Unite everything in one file so it's easy to change and then reuse it everywhere

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's do it with an enum

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Comment on lines +18 to +28
export const currenciesTable = pgTable(
"currencies",
{
id: uuid().defaultRandom().primaryKey(),
name: varchar({ length: 100 }).notNull(),
code: varchar({ length: 10 }).notNull(), // e.g. USD, EUR, ARS
},
(table) => ({
codeUniqueIdx: uniqueIndex("currencies_code_unique_idx").on(table.code),
}),
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a table storing different currencies?

src/db/schema.ts Outdated
Comment on lines 44 to 55
export const accountTypesTable = pgTable(
"account_types",
{
id: uuid().defaultRandom().primaryKey(),
name: varchar({ length: 100 }).notNull(),
slug: varchar({ length: 120 }).notNull(),
},
(table) => ({
slugUniqueIdx: uniqueIndex("account_types_slug_unique_idx").on(table.slug),
}),
);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you name an example of an account type? Why can't this be an enum or something? Why should this be a table?

Comment on lines +30 to +41
// Categories
export const categoriesTable = pgTable(
"categories",
{
id: uuid().defaultRandom().primaryKey(),
name: varchar({ length: 100 }).notNull(),
slug: varchar({ length: 120 }).notNull(),
},
(table) => ({
slugUniqueIdx: uniqueIndex("categories_slug_unique_idx").on(table.slug),
}),
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing with currencies and account types: Does this need to be its own table?

Comment on lines +38 to +44
const categories = [
{ name: "Groceries", slug: "groceries" },
{ name: "Restaurants", slug: "restaurants" },
{ name: "Transport", slug: "transport" },
{ name: "Bills", slug: "bills" },
{ name: "Entertainment", slug: "entertainment" },
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as with the currencies

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure what happens if Clerk and our DB get out of sync.

Why did we choose not to use Clerk Webhooks to sync both?
https://clerk.com/docs/guides/development/webhooks/overview

Comment on lines 7 to 13
export interface ClerkUser {
id: string;
firstName: string;
lastName: string;
emails: string[];
phoneNumbers: string[];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be kept in sync if we change settings in the Clerk dashboard. Also, we don't have phone number support AFAIK.

Comment on lines 15 to 60
export interface AccountType {
id: string;
name: string;
slug: string;
}

export interface Account {
id: string;
bankName: string;
currentBalance: string;
reference: string;
usage: string;
currencyId: string;
typeId: string;
}

export interface Category {
id: string;
name: string;
slug: string;
}

export interface Currency {
id: string;
code: string;
}

export interface AccountWithCurrencyAndType extends Account {
currency: Currency;
type: AccountType;
}

export interface Transaction {
id: string;
categoryId: string;
company: string;
amount: string;
icon: React.ReactNode;
type: "paypal" | "dkb" | "revolut" | "depot";
datetime: Date;
description: string;
accountId: string;
}

export interface TransactionWithCategoryAndAccount extends Transaction {
category: Category;
account: Account;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't these types be inferred or something from Drizzle e.g.

Like so we don't violate DRY.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
docs/backend-development.md (1)

51-51: Minor: Hyphenate "floating-point" (compound adjective).

Line 51 should read "floating-point" when modifying "issues."

Apply this diff:

-- For currency amounts, use Drizzle `decimal({ precision, scale, mode: "string" })` and handle values as strings at the API boundary to avoid floating point issues.
+- For currency amounts, use Drizzle `decimal({ precision, scale, mode: "string" })` and handle values as strings at the API boundary to avoid floating-point issues.
src/app/index.tsx (1)

161-162: Simplify redundant null coalescing and optional chaining.

The expression (accounts || [])?.length > 0 is unnecessarily complex. Since (accounts || []) always returns an array, the optional chaining ?. is redundant.

Apply this diff to simplify:

-  } else if (isSignedIn && !isLoadingAccounts && (accounts || [])?.length > 0) {
+  } else if (isSignedIn && !isLoadingAccounts && accounts && accounts.length > 0) {
     return <Redirect href={"./(tabs)/banking"} />;

Alternatively:

-  } else if (isSignedIn && !isLoadingAccounts && (accounts || [])?.length > 0) {
+  } else if (isSignedIn && !isLoadingAccounts && (accounts?.length ?? 0) > 0) {
     return <Redirect href={"./(tabs)/banking"} />;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9ba125 and b5355d7.

📒 Files selected for processing (3)
  • docs/backend-development.md (1 hunks)
  • src/app/index.tsx (2 hunks)
  • src/server/routers/transactions.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/routers/transactions.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/index.tsx
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/index.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{server,services}/**/*.{ts,tsx} : Database operations must use Drizzle ORM queries (no raw SQL in app code)

Applied to files:

  • docs/backend-development.md
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures in src/server/routers/

Applied to files:

  • docs/backend-development.md
🧬 Code graph analysis (1)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
🪛 LanguageTool
docs/backend-development.md

[grammar] ~51-~51: Use a hyphen to join words.
Context: ...gs at the API boundary to avoid floating point issues. ### Zod Validation Patter...

(QB_NEW_EN_HYPHEN)

🔇 Additional comments (3)
docs/backend-development.md (2)

75-116: ✅ Where-clause example now correctly typed and compiles.

The secure list query example has been corrected from the previous feedback: typed conditions array with type-guarded filter, no any casts, and proper SQL expression composition. This teaches the right pattern and aligns with the "no any" rule stated at line 139.


1-150: Comprehensive and practical backend development guide.

This documentation successfully codifies the backend patterns, security practices (ownership checks, protectedProcedure), and type-safety principles across the new routers, schemas, and error handling. The concrete example (lines 78–116) demonstrates correct Drizzle composition and Zod validation, and the checklist (lines 143–150) gives developers clear entry points for adding features. Aligns well with the learnings on Drizzle ORM usage and tRPC router conventions.

src/app/index.tsx (1)

150-153: LGTM! Query is properly guarded.

The accounts query is correctly guarded with enabled: isSignedIn, which prevents unnecessary requests when the user is not authenticated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c66112d and c4957e7.

📒 Files selected for processing (1)
  • drizzle/0001_nostalgic_master_mold.sql (1 hunks)
🔇 Additional comments (1)
drizzle/0001_nostalgic_master_mold.sql (1)

1-41: Safe migration pattern now in place.

The migration correctly applies the recommended safe pattern: add nullable column → seed default → backfill → enforce NOT NULL → add constraints. Once the column name typo above is fixed, this migration will safely handle both fresh databases and existing data.

@FleetAdmiralJakob FleetAdmiralJakob self-assigned this Oct 28, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/app/index.tsx (1)

161-161: Simplify the redundant condition.

The expression (accounts || [])?.length > 0 is unnecessarily complex. Since the first condition already handles the case where accounts is falsy or empty, you can simplify this to accounts.length > 0.

-  } else if (isSignedIn && !isLoadingAccounts && (accounts || [])?.length > 0) {
+  } else if (isSignedIn && !isLoadingAccounts && accounts.length > 0) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 200d817 and 55c80cb.

📒 Files selected for processing (3)
  • src/app/index.tsx (2 hunks)
  • src/server/routers/accounts.ts (1 hunks)
  • src/types/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/server/routers/accounts.ts
  • src/types/index.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/index.tsx
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/index.tsx
🧬 Code graph analysis (1)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)

Comment on lines 165 to 170
// TODO: Add loading state
// if (isLoadingAccounts) {
// return <View className="flex-1 items-center justify-center">
// <ActivityIndicator size="large" color={iconColor} />
// </View>;
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement the loading state to improve UX.

While accounts are loading, signed-in users see the full landing page content instead of a loading indicator. This creates a confusing experience where the page briefly flashes before redirecting.

Additionally, if this code were uncommented as-is, it would cause a reference error because iconColor is used on line 168 but not defined until line 172.

Based on past review comments, this TODO should be tracked as a ticket. However, the loading state should be implemented soon to avoid user confusion during the accounts fetch.

Here's a corrected implementation:

-  // TODO: Add loading state
-  // if (isLoadingAccounts) {
-  //   return <View className="flex-1 items-center justify-center">
-  //     <ActivityIndicator size="large" color={iconColor} />
-  //   </View>;
-  // }
+  if (isSignedIn && isLoadingAccounts) {
+    const loadingIconColor = scheme === "dark" ? "#E0E0E0" : "#111827";
+    return (
+      <View className="flex-1 items-center justify-center">
+        <ActivityIndicator size="large" color={loadingIconColor} />
+      </View>
+    );
+  }

You'll also need to import ActivityIndicator:

 import {
   View,
   ScrollView,
   useColorScheme,
   TouchableOpacity,
   Modal,
+  ActivityIndicator,
 } from "react-native";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// TODO: Add loading state
// if (isLoadingAccounts) {
// return <View className="flex-1 items-center justify-center">
// <ActivityIndicator size="large" color={iconColor} />
// </View>;
// }
if (isSignedIn && isLoadingAccounts) {
const loadingIconColor = scheme === "dark" ? "#E0E0E0" : "#111827";
return (
<View className="flex-1 items-center justify-center">
<ActivityIndicator size="large" color={loadingIconColor} />
</View>
);
}
Suggested change
// TODO: Add loading state
// if (isLoadingAccounts) {
// return <View className="flex-1 items-center justify-center">
// <ActivityIndicator size="large" color={iconColor} />
// </View>;
// }
import {
View,
ScrollView,
useColorScheme,
TouchableOpacity,
Modal,
ActivityIndicator,
} from "react-native";
🤖 Prompt for AI Agents
In src/app/index.tsx around lines 165 to 170, the TODO for the accounts loading
state must be implemented: when isLoadingAccounts is true, return a centered
View with an ActivityIndicator; ensure ActivityIndicator is imported from
react-native (or the UI lib used) and that iconColor is defined or calculated
before it's referenced (move iconColor above the loading check or replace with a
known color variable); remove the commented block and add a short ticket
reference comment if required.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • .idea/sqldialects.xml: Language not supported
Comments suppressed due to low confidence (1)

src/app/start/index.tsx:47

  • Unused variable userData.
  const { data: userData } = trpc.users.getUser.useQuery();

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/app/index.tsx (3)

179-186: Simplify the accounts existence check.

The condition accounts && accounts.length > 0 can be simplified using optional chaining.

Apply this diff:

   else if (
     isSignedIn &&
     !isLoadingAccounts &&
-    accounts &&
-    accounts.length > 0
+    (accounts?.length ?? 0) > 0
   ) {
     return <Redirect href={"./(tabs)/banking"} />;
   }

162-167: Add error handling for the accounts query.

The query doesn't capture error states. If the fetch fails, accounts will be undefined and isLoadingAccounts will be false, causing the user to be silently redirected to the start screen without any error feedback.

Capture and handle the error state:

-  const { data: accounts, isLoading: isLoadingAccounts } =
-    trpc.accounts.getAccounts.useQuery(undefined, {
-      enabled: isSignedIn,
-    });
+  const {
+    data: accounts,
+    isLoading: isLoadingAccounts,
+    error: accountsError,
+  } = trpc.accounts.getAccounts.useQuery(undefined, {
+    enabled: isSignedIn,
+  });

Then add error handling before the redirect logic:

if (accountsError && isSignedIn) {
  return (
    <View className="flex-1 items-center justify-center">
      <AppText className="text-danger dark:text-danger">
        {t("errorLoadingAccounts")}
      </AppText>
    </View>
  );
}

189-194: Implement the loading state to improve UX.

While accounts are loading, signed-in users see the full landing page before redirecting, causing a confusing flash. The commented code references iconColor which is defined later (line 196), causing a scope error.

Implement the loading state with a correctly scoped color variable:

-  // TODO: Add loading state
-  // if (isLoadingAccounts) {
-  //   return <View className="flex-1 items-center justify-center">
-  //     <ActivityIndicator size="large" color={iconColor} />
-  //   </View>;
-  // }
+  if (isSignedIn && isLoadingAccounts) {
+    const loadingIconColor = scheme === "dark" ? "#E0E0E0" : "#111827";
+    return (
+      <View className="flex-1 items-center justify-center">
+        <ActivityIndicator size="large" color={loadingIconColor} />
+      </View>
+    );
+  }

Import ActivityIndicator:

 import {
   View,
   ScrollView,
   useColorScheme,
   TouchableOpacity,
   Modal,
+  ActivityIndicator,
 } from "react-native";
🧹 Nitpick comments (5)
src/scripts/seed.ts (3)

10-35: Add TSDoc comments and consider centralizing lookup data.

Per previous review feedback, functions should include documentation. Additionally, the hardcoded currency list could be centralized as a typed constant or enum to enable reuse across the codebase (e.g., for validation or default values).

Example documentation:

+/**
+ * Seeds the currencies table with default currency entries (USD, EUR, ARS).
+ * Skips currencies that already exist based on their code.
+ */
 async function seedCurrencies() {

For centralizing lookup data, consider creating src/constants/lookups.ts:

export const SUPPORTED_CURRENCIES = [
  { code: "USD", name: "US Dollar" },
  { code: "EUR", name: "Euro" },
  { code: "ARS", name: "Argentine Peso" },
] as const;

export type CurrencyCode = typeof SUPPORTED_CURRENCIES[number]["code"];

This would allow the seed script and other parts of the codebase (validation, defaults) to reference the same source of truth.


37-63: Add TSDoc and consider refactoring to reduce duplication.

This function follows the same pattern as seedCurrencies. Adding TSDoc comments would improve maintainability, and the three seed functions could be refactored into a single generic helper to eliminate duplication.

Example documentation:

+/**
+ * Seeds the categories table with default category entries.
+ * Skips categories that already exist based on their slug.
+ */
 async function seedCategories() {

Consider a generic helper like:

async function seedLookupTable<T extends { slug: string } | { code: string }>(
  table: PgTable,
  data: T[],
  uniqueField: keyof T,
  tableName: string
) {
  const keys = data.map((item) => item[uniqueField]);
  const existing = await db.select().from(table).where(inArray(table[uniqueField], keys));
  const existingKeys = new Set(existing.map((e) => e[uniqueField]));
  const toInsert = data.filter((item) => !existingKeys.has(item[uniqueField]));
  
  if (toInsert.length > 0) {
    await db.insert(table).values(toInsert);
    console.log(`Inserted ${tableName}: ${toInsert.map(i => i[uniqueField]).join(", ")}`);
  } else {
    console.log(`${tableName} already seeded`);
  }
}

65-90: Add TSDoc comment.

+/**
+ * Seeds the account_types table with default account type entries.
+ * Skips account types that already exist based on their slug.
+ */
 async function seedAccountTypes() {
src/app/start/index.tsx (1)

76-81: Consider aligning form field names and types with the backend schema.

The form collects currentAmount as a number but the mutation expects currentBalance as a string. While the toString() conversion works, this mismatch creates maintenance overhead and potential bugs.

Option 1: Rename the form field to match the backend:

 defaultValues: {
   bankName: "",
-  currentAmount: 0,
+  currentBalance: "0.00",
   reference: "",
   usage: "",
 },

Then update the field handling to work with strings directly, or keep the current UX and handle conversion at the form level.

Option 2: Update the backend schema to accept a number and handle conversion server-side (preferred if backend controls the precision).

src/app/index.tsx (1)

171-177: Simplify the null check with optional chaining.

The condition (!accounts || accounts?.length === 0) is redundant because optional chaining already handles null/undefined cases.

Apply this diff:

   if (
     isSignedIn &&
     !isLoadingAccounts &&
-    (!accounts || accounts?.length === 0)
+    !accounts?.length
   ) {
     return <Redirect href={"./start"} />;
   }

The ?. operator returns undefined when accounts is null/undefined, making the explicit null check unnecessary.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55c80cb and 2e24435.

📒 Files selected for processing (5)
  • docs/backend-development.md (1 hunks)
  • src/app/index.tsx (2 hunks)
  • src/app/start/index.tsx (2 hunks)
  • src/scripts/seed.ts (1 hunks)
  • src/server/api/trpc.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/start/index.tsx
  • src/app/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/start/index.tsx
  • src/server/api/trpc.ts
  • src/scripts/seed.ts
  • src/app/index.tsx
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/start/index.tsx
  • src/app/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/start/index.tsx
  • src/app/index.tsx
src/{server,services}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Database operations must use Drizzle ORM queries (no raw SQL in app code)

Files:

  • src/server/api/trpc.ts
🧠 Learnings (2)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures in src/server/routers/

Applied to files:

  • src/server/api/trpc.ts
  • docs/backend-development.md
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
PR: The-Creative-Programming-Group/finance-io#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{server,services}/**/*.{ts,tsx} : Database operations must use Drizzle ORM queries (no raw SQL in app code)

Applied to files:

  • docs/backend-development.md
🧬 Code graph analysis (4)
src/app/start/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/server/api/trpc.ts (1)
src/errors/trpc.ts (1)
  • errors (3-18)
src/scripts/seed.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (3)
  • currenciesTable (18-28)
  • categoriesTable (31-41)
  • accountTypesTable (44-54)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
🔇 Additional comments (4)
docs/backend-development.md (1)

1-150: Excellent backend development guidelines—comprehensive and well-structured.

The documentation provides clear, actionable patterns for authentication, error handling, Drizzle ORM usage, and type safety. The code examples demonstrate proper TypeScript practices, and the guidelines align well with the project's strict typing requirements.

src/scripts/seed.ts (2)

1-8: LGTM—imports are clean and correct.

The imports include only what's needed: environment config, database access, table schemas, and the inArray helper for the read-before-write checks.


92-101: LGTM—clean orchestration and error handling.

The sequential execution and top-level error handler with process.exit(1) are appropriate for a seed script.

src/app/start/index.tsx (1)

47-57: LGTM! Well-structured mutation with error handling.

The mutation properly handles both success and error cases, resets the form state, and navigates the user appropriately. The error handler provides clear feedback to the user.

Comment on lines +135 to +141
let user;
try {
user = await clerkClient.users.getUser(ctx.userId);
} catch (err) {
console.error("Error fetching user from Clerk:", err);
throw errors.notAuthenticated();
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Consider caching or deferring the user fetch to avoid a round-trip to Clerk on every protected request.

Fetching the full user object from Clerk on every protected procedure call introduces latency and increases load on both Clerk's API and your server. In many procedures, only userId is required.

Recommended solutions:

  1. Defer user fetch — Remove the fetch from the middleware and let individual procedures call clerkClient.users.getUser(ctx.userId) only when they need the full user object.

  2. Add caching — If the user object is frequently needed, cache it with a short TTL:

import { LRUCache } from 'lru-cache';

const userCache = new LRUCache<string, User>({
  max: 500,
  ttl: 1000 * 60 * 5, // 5 minutes
});

export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
  if (!ctx.userId) {
    throw errors.notAuthenticated();
  }

  let user = userCache.get(ctx.userId);
  if (!user) {
    try {
      user = await clerkClient.users.getUser(ctx.userId);
      userCache.set(ctx.userId, user);
    } catch (err) {
      console.error("Error fetching user from Clerk:", err);
      throw errors.notAuthenticated();
    }
  }

  return next({
    ctx: {
      ...ctx,
      userId: ctx.userId,
      user,
    },
  });
});
  1. Use a helper procedure — Create protectedProcedureWithUser for procedures that need the full user object, keeping protectedProcedure lightweight with only userId.
🤖 Prompt for AI Agents
In src/server/api/trpc.ts around lines 135-141, the middleware eagerly fetches
the full Clerk user for every protected request causing unnecessary latency and
load; either remove the fetch so middleware only validates ctx.userId and let
individual procedures fetch the full user when needed, or implement a short-TTL
LRU cache for user objects and read/write it in the middleware (or create a
separate protectedProcedureWithUser variant) so only procedures that require the
full user get it from Clerk or the cache; update the middleware to throw
errors.notAuthenticated() if no ctx.userId, and if you add caching ensure
thread-safe access, a reasonable max size/TTL, and proper error handling/logging
when Clerk fetches fail.

Copy link
Member

@FleetAdmiralJakob FleetAdmiralJakob Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Does it make sense to use lru-cache in a serverless environment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/coderabbitai We will use a serverless environment with a 99% probability. Please reevaluate your answer and tips. And update your review comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai We will use a serverless environment with a 99% probability. Please reevaluate your answer and tips. And update your review comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FleetAdmiralJakob, you're absolutely right to question the LRU cache recommendation for a serverless environment. In-memory caching is ineffective in serverless since each function invocation is stateless and the cache would be cold on every (or most) requests.

Here's the revised recommendation for serverless:

Primary solution: Defer the user fetch

Remove the user fetch from the middleware entirely. Let individual procedures call clerkClient.users.getUser(ctx.userId) only when they actually need the full user object:

export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
  if (!ctx.userId) {
    throw errors.notAuthenticated();
  }

  return next({
    ctx: {
      ...ctx,
      userId: ctx.userId,
    },
  });
});

Then in procedures that need the full user:

myProcedure: protectedProcedure.query(async ({ ctx }) => {
  const user = await clerkClient.users.getUser(ctx.userId);
  // ... use user data
}),

Alternative: Create a separate procedure variant

Keep the lightweight protectedProcedure and create protectedProcedureWithUser for procedures that need the full user object:

export const protectedProcedureWithUser = protectedProcedure.use(async ({ ctx, next }) => {
  let user;
  try {
    user = await clerkClient.users.getUser(ctx.userId);
  } catch (err) {
    console.error("Error fetching user from Clerk:", err);
    throw errors.notAuthenticated();
  }

  return next({
    ctx: {
      ...ctx,
      user,
    },
  });
});

This keeps your serverless functions fast by avoiding unnecessary API calls to Clerk on every protected request.


🧠 Learnings used
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures in src/server/routers/

Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{app,components,services,utils,contexts}/**/*.{ts,tsx} : Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Learnt from: FleetAdmiralJakob
Repo: The-Creative-Programming-Group/finance-io PR: 6
File: src/app/_layout.tsx:51-52
Timestamp: 2025-05-31T15:45:23.954Z
Learning: FleetAdmiralJakob prefers using .env.example files with sensible defaults (minus secrets) for environment variable management, allowing developers to copy and customize them for different deployment environments.

- improve types
- removed "account types" table
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (16)
src/components/ui/select.tsx (2)

54-72: Consider using theme-aware color for ChevronDown icon.

The hard-coded #888 color on line 71 may not adapt well to theme changes. Consider using a theme-aware color or extracting the color to match your design system.

Example approach:

-        <ChevronDown size={18} color="#888" />
+        <ChevronDown size={18} className="text-text/50 dark:text-dark-text/50" />

Note: Verify that lucide-react-native supports className prop, otherwise you may need to use a theme hook or design token.


74-116: Consider theme-aware colors for drag handle and check icon.

Two color-related refinements:

  1. Line 87: The drag handle uses bg-gray-400 which may need a dark mode variant for consistency.
  2. Line 108: The Check icon uses hard-coded #16a34a (green), which doesn't adapt to theme changes.

Example approach:

-            <View className="bg-gray-400 mb-3 h-1.5 w-12 self-center rounded-full" />
+            <View className="mb-3 h-1.5 w-12 self-center rounded-full bg-gray-400 dark:bg-gray-600" />
-                    {isActive ? <Check size={18} color="#16a34a" /> : null}
+                    {isActive ? <Check size={18} className="text-green-600 dark:text-green-500" /> : null}

Note: Verify that lucide-react-native supports className prop for the Check icon, otherwise consider using a theme hook or design token.

src/app/(auth)/sign-up.tsx (3)

35-36: Add error handling configuration to the mutation.

The mutation lacks error handling configuration. If createUser.mutateAsync() fails during sign-up, users won't see a helpful error message.

Consider adding error callbacks:

-  const createUser = trpc.users.createUser.useMutation();
+  const createUser = trpc.users.createUser.useMutation({
+    onError: (error) => {
+      console.error("Failed to create user:", error);
+      setError("Failed to create account. Please try again.");
+    },
+  });

147-148: Add specific error handling for database user creation.

If the database user creation fails, the generic error handler (line 153-180) may display a misleading "Verification failed" message to users when the actual issue is database connectivity.

Consider wrapping the mutation in a specific try-catch:

       if (completeSignUp.status === "complete") {
         await setActive({ session: completeSignUp.createdSessionId });
-        // Add the user in the database
-        await createUser.mutateAsync();
+        // Add the user in the database
+        try {
+          await createUser.mutateAsync();
+        } catch (dbError) {
+          console.error("Failed to create user in database:", dbError);
+          setError("Account created but failed to initialize profile. Please contact support.");
+          setIsSubmitting(false);
+          return;
+        }
         router.replace("/start");

164-165: Add specific error handling for database user creation in fallback path.

Similar to the main verification path, if database user creation fails here, the catch block (line 170) will display "Email already verified. Please sign in." which is misleading when the actual issue is database connectivity.

Apply the same pattern as suggested for line 147-148:

             if (signInAttempt?.status === "complete") {
               await setActive({ session: signInAttempt.createdSessionId });
-              // Add the user in the database
-              await createUser.mutateAsync();
+              // Add the user in the database
+              try {
+                await createUser.mutateAsync();
+              } catch (dbError) {
+                console.error("Failed to create user in database:", dbError);
+                setError("Account exists but failed to initialize profile. Please contact support.");
+                setIsSubmitting(false);
+                return;
+              }
               router.replace("/start");
src/scripts/seed.ts (3)

7-11: Consider centralizing the currencies list.

The hardcoded currencies list should be defined in a shared location (e.g., in src/db/schema.ts alongside the schema definition or in a constants file) and imported here. This ensures consistency across seeding, validation, and schema constraints.

Based on past review feedback suggesting unifying currency definitions.

For example, in src/db/schema.ts:

export const SUPPORTED_CURRENCIES = [
  { code: "USD", name: "US Dollar" },
  { code: "EUR", name: "Euro" },
  { code: "ARS", name: "Argentine Peso" },
] as const;

Then import and use in the seed script:

+import { SUPPORTED_CURRENCIES } from "~/db/schema";
+
 async function seedCurrencies() {
-  const currencies = [
-    { code: "USD", name: "US Dollar" },
-    { code: "EUR", name: "Euro" },
-    { code: "ARS", name: "Argentine Peso" },
-  ];
+  const currencies = [...SUPPORTED_CURRENCIES];

34-40: Consider centralizing the categories list.

Similar to currencies, the hardcoded categories list should be defined in a shared location to ensure consistency. This is especially important if categories are used for validation or displayed in the UI.

Based on past review feedback suggesting unifying category definitions.


61-64: Consider parallel execution for independent seeds.

Since currencies and categories are independent, you could seed them in parallel for faster execution.

 async function main() {
-  await seedCurrencies();
-  await seedCategories();
+  await Promise.all([
+    seedCurrencies(),
+    seedCategories(),
+  ]);
 }
src/types/index.ts (3)

19-26: Consider using Drizzle-inferred types to avoid duplication.

As noted in past feedback, these manual type definitions duplicate the schema definitions and violate DRY. Drizzle ORM can infer types directly from schema definitions.

Based on past review feedback.

Example approach:

import { type InferSelectModel } from 'drizzle-orm';
import { accountsTable } from '~/db/schema';

export type Account = InferSelectModel<typeof accountsTable>;

This ensures types stay in sync with schema changes automatically.


28-32: Apply Drizzle type inference here as well.

The same recommendation to use Drizzle's InferSelectModel applies to Category to maintain consistency and avoid schema drift.

export type Category = InferSelectModel<typeof categoriesTable>;

43-51: Apply Drizzle type inference to Transaction as well.

Same recommendation to use InferSelectModel<typeof transactionsTable> for consistency.

src/server/routers/accounts.ts (3)

25-38: Consider moving user creation to sign-up flow.

The user existence check and creation logic duplicates what should happen during user registration (handled by createUser mutation in the users router). Creating users within the account creation flow can lead to:

  • Race conditions if multiple accounts are created simultaneously
  • Inconsistent user creation flow
  • Logic duplication

Consider either:

  1. Ensuring createUser is always called during sign-up (it already is per src/app/(auth)/sign-up.tsx)
  2. Or adding a foreign key constraint check that will fail gracefully if user doesn't exist

If you choose to keep this logic, add a transaction wrapper:

await db.transaction(async (tx) => {
  const existingUser = await tx.select()...
  if (!existingUser.length) {
    await tx.insert(usersTable)...
  }
  // ... rest of account creation
});

69-77: Simplify return object construction.

The manual property mapping can be simplified using spread operators for cleaner, more maintainable code.

       return {
-        id: account[0].id,
-        bankName: account[0].bankName,
-        currentBalance: account[0].currentBalance,
-        reference: account[0].reference,
-        usage: account[0].usage,
-        currencyId: account[0].currencyId,
+        ...account[0],
         currency: currency[0],
       };

95-103: Simplify result mapping.

Similar to the addAccount return, the mapping can be simplified with spread operators.

       return rows.map(({ accounts, currency }) => ({
-        id: accounts.id,
-        bankName: accounts.bankName,
-        currentBalance: accounts.currentBalance,
-        reference: accounts.reference,
-        usage: accounts.usage,
-        currencyId: accounts.currencyId,
+        ...accounts,
         currency,
       }));
.idea/sqldialects.xml (1)

1-8: Consider adding .idea/ directory to .gitignore.

This IntelliJ IDEA configuration file is environment-specific. The .idea/ directory should typically be excluded from version control to avoid conflicts between team members' IDE settings.

Add to .gitignore:

.idea/

However, some teams choose to commit certain .idea/ files for consistency. Verify your team's convention.

src/schemas/welcomeSchema.ts (1)

3-5: Consider renaming to avoid confusion with schema.ts export.

The file creates a local referencesEnum (Zod enum), but src/db/schema.ts also exports a referencesEnum (Drizzle pgEnum). While they serve different purposes, the identical naming could cause confusion when importing.

Consider one of these alternatives:

-export const referencesEnum = z.enum(references);
+export const referenceValidation = z.enum(references);

Or import and re-export from schema.ts if the Drizzle enum is compatible:

// If compatible with Zod's expected format
import { referencesEnum as dbReferencesEnum } from "~/db/schema";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e24435 and ae7db7e.

📒 Files selected for processing (15)
  • .idea/sqldialects.xml (1 hunks)
  • drizzle/0002_careless_gladiator.sql (1 hunks)
  • drizzle/meta/0002_snapshot.json (3 hunks)
  • drizzle/meta/_journal.json (1 hunks)
  • localization/it/common.json (1 hunks)
  • src/app/(auth)/sign-up.tsx (4 hunks)
  • src/app/start/index.tsx (4 hunks)
  • src/components/ui/select.tsx (1 hunks)
  • src/db/schema.ts (1 hunks)
  • src/errors/messages.ts (1 hunks)
  • src/schemas/welcomeSchema.ts (3 hunks)
  • src/scripts/seed.ts (1 hunks)
  • src/server/routers/accounts.ts (1 hunks)
  • src/server/routers/users.ts (1 hunks)
  • src/types/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • localization/it/common.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/start/index.tsx
  • drizzle/meta/_journal.json
  • src/server/routers/users.ts
  • src/errors/messages.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/(auth)/sign-up.tsx
  • src/components/ui/select.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/(auth)/sign-up.tsx
  • src/server/routers/accounts.ts
  • src/components/ui/select.tsx
  • src/schemas/welcomeSchema.ts
  • src/scripts/seed.ts
  • src/db/schema.ts
  • src/types/index.ts
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/(auth)/sign-up.tsx
  • src/components/ui/select.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/(auth)/sign-up.tsx
src/server/routers/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define tRPC procedures in src/server/routers/

Files:

  • src/server/routers/accounts.ts
src/{server,services}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Database operations must use Drizzle ORM queries (no raw SQL in app code)

Files:

  • src/server/routers/accounts.ts
src/db/schema.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain the database schema in src/db/schema.ts

Files:

  • src/db/schema.ts
🧠 Learnings (7)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/server/routers/**/*.ts : Define tRPC procedures in src/server/routers/

Applied to files:

  • src/server/routers/accounts.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{app,components,services,utils,contexts}/**/*.{ts,tsx} : Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Applied to files:

  • src/server/routers/accounts.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to **/*.tsx : Use <AppText> instead of <Text> from react-native in all components

Applied to files:

  • src/components/ui/select.tsx
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/db/schema.ts : Maintain the database schema in src/db/schema.ts

Applied to files:

  • src/schemas/welcomeSchema.ts
  • src/scripts/seed.ts
  • src/db/schema.ts
📚 Learning: 2025-06-25T17:49:26.926Z
Learnt from: murtazanoori
Repo: The-Creative-Programming-Group/finance-io PR: 23
File: src/components/zodSchema.ts:4-8
Timestamp: 2025-06-25T17:49:26.926Z
Learning: In Zod schema validation for this project, the pattern of using chained min() validations with different messages is intentional - min(1) with "field is required" for empty inputs, followed by min(2) with "must be at least 2 characters" for inputs that are too short. This provides differentiated error messaging for better UX.

Applied to files:

  • src/schemas/welcomeSchema.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{server,services}/**/*.{ts,tsx} : Database operations must use Drizzle ORM queries (no raw SQL in app code)

Applied to files:

  • src/db/schema.ts
  • src/types/index.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: If modifying authentication features, verify Clerk auth flow (sign-in/sign-up)

Applied to files:

  • src/types/index.ts
🧬 Code graph analysis (6)
src/app/(auth)/sign-up.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/server/routers/accounts.ts (7)
src/server/api/trpc.ts (2)
  • createTRPCRouter (113-113)
  • protectedProcedure (130-150)
src/db/schema.ts (4)
  • referencesEnum (46-46)
  • usersTable (14-16)
  • currenciesTable (19-29)
  • accountsTable (49-74)
src/schemas/welcomeSchema.ts (1)
  • referencesEnum (5-5)
src/types/index.ts (2)
  • AccountWithCurrency (39-41)
  • Account (19-26)
src/db/index.ts (1)
  • db (6-6)
src/errors/trpc.ts (1)
  • errors (3-18)
src/errors/messages.ts (1)
  • error_messages (1-13)
src/components/ui/select.tsx (1)
src/components/ui/AppText.tsx (1)
  • AppText (14-48)
src/schemas/welcomeSchema.ts (1)
src/db/schema.ts (2)
  • referencesEnum (46-46)
  • references (44-44)
src/scripts/seed.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (2)
  • currenciesTable (19-29)
  • categoriesTable (32-42)
src/db/schema.ts (1)
src/schemas/welcomeSchema.ts (1)
  • referencesEnum (5-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (21)
src/components/ui/select.tsx (3)

1-18: LGTM! Clean imports and well-defined types.

The component correctly imports AppText instead of React Native's Text, and the TypeScript types are explicit and comprehensive.


20-44: LGTM! Well-structured component logic.

The component setup is clean with appropriate use of useMemo to optimize the selected item lookup, and handlers are correctly implemented.


54-115: Excellent accessibility implementation!

The component includes comprehensive accessibility props (accessibilityRole, accessibilityState, accessibilityLabel, accessibilityHint) throughout, ensuring a good experience for users with assistive technologies.

src/app/(auth)/sign-up.tsx (1)

20-20: LGTM!

The TRPC client import is correctly structured and follows the expected pattern.

src/scripts/seed.ts (2)

1-5: LGTM!

Imports are clean and all necessary for the seeding operations.


66-69: LGTM!

Error handling follows the standard pattern for Node.js scripts with appropriate exit codes.

src/types/index.ts (3)

1-11: LGTM!

The ClerkUser interface is well-documented and the naming clearly indicates it represents Clerk authentication data rather than database schema. The comment about phone numbers addresses past concerns.


39-41: LGTM!

The composite type appropriately models the joined account-currency relationship returned by API queries.


53-56: LGTM!

The composite type appropriately models the joined transaction data with category and account relationships.

drizzle/meta/0002_snapshot.json (1)

1-382: Auto-generated migration snapshot.

This Drizzle Kit snapshot file is machine-generated and should not be manually edited. The schema definitions appear consistent with the related migration SQL files and schema.ts changes.

src/db/schema.ts (7)

1-11: LGTM!

All imports are necessary and correctly sourced from Drizzle ORM.


13-16: LGTM!

The minimal users table appropriately stores only the Clerk user ID as a foreign key anchor, with user details managed by Clerk.


18-29: LGTM!

The currencies table enables multi-currency support with appropriate constraints. The unique index on code prevents duplicate currency codes. This design allows for extensibility if additional currencies need to be added via seeding rather than code changes.


31-42: LGTM!

The categories table with unique slug index supports extensible transaction categorization. The slug field enables URL-friendly identifiers for potential filtering or routing.


44-46: LGTM!

The references enum with a fixed set of values is an appropriate replacement for the account_types table, providing type safety without the overhead of a separate table.


48-74: LGTM!

The accounts table is well-designed with:

  • Appropriate foreign key cascade/restrict rules
  • String mode for decimal fields to prevent JavaScript float precision issues
  • Proper indexes on foreign keys for query optimization
  • Timestamp tracking with defaultNow

76-98: LGTM!

The transactions table follows consistent design patterns with appropriate:

  • Cascade delete for accountId (transactions belong to accounts)
  • Restrict delete for categoryId (preserve referential integrity)
  • String mode for decimal amounts
  • Indexes on foreign keys and datetime for efficient queries
src/server/routers/accounts.ts (2)

1-10: LGTM!

All imports are necessary and correctly sourced. Note that referencesEnum is imported from welcomeSchema.ts, which may be a legacy file name.


19-19: Remove unused typeId parameter.

The typeId input parameter is defined but never used in the procedure. This was likely left over from when account types were stored in a separate table (before migration 0002 removed the account_types table).

       z.object({
         bankName: z.string(),
         currentBalance: z.string(),
         reference: referencesEnum,
         usage: z.string(),
-        typeId: z.string().optional(),
       }),

Likely an incorrect or invalid review comment.

drizzle/0002_careless_gladiator.sql (1)

1-7: Original review comment concerns are valid but require manual verification of actual data.

The review comment correctly identifies the critical risk in this migration. Analysis of the full schema evolution shows:

  1. Data loss is intentional: The migration consolidates from a two-column approach (typeId foreign key + reference varchar) to a single reference ENUM column. The account_types table drop is a deliberate schema redesign, not an oversight.

  2. Statement ordering is redundant but safe: The DROP INDEX "accounts_type_idx" on line 5 is redundant because DROP TABLE ... CASCADE on line 3 already removes dependent indexes. This won't cause failures, just unnecessary syntax.

  3. USING cast is the critical risk: Line 6 casts existing reference varchar values to the ENUM type. This will fail if ANY existing value in the reference column is outside the enum set ('private', 'business', 'savings', 'shared'). The seed.ts file contains no reference data population, so the actual runtime values are unknown.

Action required: Before deploying this migration to a database with existing data, verify that all values currently in accounts.reference are valid enum members. If the reference column contains values outside ('private', 'business', 'savings', 'shared'), the migration will fail with a constraint violation.

src/schemas/welcomeSchema.ts (1)

15-15: LGTM!

Using referencesEnum directly in the base schema is appropriate for backend validation without internationalization.

"Reference must be less than 50 characters",
),
}),
reference: referencesEnum,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add internationalized error message for consistency.

The reference field lacks custom error messages, breaking the established pattern where all other fields (bankName, currentAmount, usage) provide detailed i18n messages. Default Zod enum errors won't be internationalized.

Based on learnings: The project intentionally uses differentiated error messaging for better UX.

Apply this diff to add i18n error handling:

-    reference: referencesEnum,
+    reference: referencesEnum.error(() =>
+      t("referenceRequired", "Please select a valid reference type")
+    ),

Or using Zod 4's unified error parameter for more specific messaging:

-    reference: referencesEnum,
+    reference: z.enum(references, {
+      error: (issue) => {
+        if (issue.code === 'invalid_enum_value') {
+          return t("referenceInvalid", "Please select: Private, Business, Savings, or Shared");
+        }
+      }
+    }),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/schemas/welcomeSchema.ts around line 52, the reference field currently
uses the raw enum (reference: referencesEnum,) and lacks an internationalized
custom error like the other fields; update this to provide the same i18n-aware
error handling by supplying a custom error message for invalid/required enum
values (either by using Zod 4's unified error param/errorMap or by passing a
message option to the enum) matching the project's i18n key pattern used for
bankName/currentAmount/usage so validation returns the localized error string.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
localization/ur/common.json (1)

1-1: Update frontend component references to use new dashboard localization keys.

The UI components are not fully wired to the new keys. Specifically:

  • dashboardShared is defined in all localization files but has zero references in frontend code (missing wiring)
  • dashboardSafeAccounts (old key) is still used in src/app/start/index.tsx:90 and should be replaced with dashboardSavings
  • Consider also updating the "shared" reference in the same file from sharedFunds to dashboardShared for consistency

Fix required:

  • src/app/start/index.tsx:90 – Replace t("dashboardSafeAccounts", "Savings") with t("dashboardSavings")
  • src/app/start/index.tsx:91 – Replace t("sharedFunds", "Shared") with t("dashboardShared") if applicable
♻️ Duplicate comments (3)
src/types/index.ts (2)

15-19: Verify AccountType interface is still needed.

A previous review flagged this interface as potentially unused dead code after the migration dropped the account_types table and accounts.typeId column. Verify whether this interface is referenced anywhere in the codebase.

Run this verification script:

#!/bin/bash
# Description: Search for AccountType usage across the codebase

# Search for AccountType references excluding this file
rg -n '\bAccountType\b' --type ts --type tsx -g '!src/types/index.ts' -C 2

If the script returns no results, remove this unused interface:

-export interface AccountType {
-  id: string;
-  name: string;
-  slug: string;
-}
-

36-39: Currency interface missing 'name' field from schema.

The database schema defines three fields for currencies: id, name, and code. This interface is missing the name field, causing a mismatch between the TypeScript type and the database schema.

Apply this diff to add the missing field:

 export interface Currency {
   id: string;
+  name: string;
   code: string;
 }

Alternatively, use Drizzle's type inference to automatically derive the type from the schema:

import type { InferSelectModel } from 'drizzle-orm';
import { currenciesTable } from '~/db/schema';

export type Currency = InferSelectModel<typeof currenciesTable>;

Based on past review comments suggesting DRY principles for Drizzle types.

src/schemas/welcomeSchema.ts (1)

54-64: Restore localized validation message for reference.

Switching to referencesEnum without an error hook now surfaces Zod’s default English message for both empty and invalid selections, breaking the localized UX we established for other fields. Please wrap the enum with a localized error handler so end users still receive translated guidance.

-    reference: referencesEnum,
+    reference: referencesEnum.error((issue) =>
+      issue.input === undefined || issue.input === ""
+        ? t("referenceRequired", "Reference is required")
+        : t("referenceRequired", "Please select a valid reference type"),
+    ),

Feel free to introduce a dedicated referenceInvalid key if you want a distinct message for bad values.

Based on learnings

🧹 Nitpick comments (3)
src/app/(tabs)/banking/index.tsx (3)

14-15: Use the loading state to improve UX.

The isLoadingAccounts variable is defined but never used. Consider showing a loading indicator or skeleton UI while data is being fetched, and prevent rendering account sections until loading completes.

Apply this diff to add loading state handling:

+  if (isLoadingAccounts) {
+    return (
+      <SafeAreaView className="flex-1 bg-background dark:bg-dark-background">
+        <StatusBar
+          barStyle={isDark ? "light-content" : "dark-content"}
+          backgroundColor={colors.background}
+        />
+        <ScrollView className="flex-1 p-8">
+          <AppText className="text-center" style={{ color: colors.text }}>
+            {t("loading")}
+          </AppText>
+        </ScrollView>
+      </SafeAreaView>
+    );
+  }
+
  const handleAccountPress = (accountName: string, accountId: string) => {

22-29: Add empty state handling for better UX.

When accounts is undefined or empty, the component renders a blank screen with no feedback. Add an empty state message or prompt to guide users when no accounts exist.

Add this check after the loading state handling:

  if (!accounts || accounts.length === 0) {
    return (
      <SafeAreaView className="flex-1 bg-background dark:bg-dark-background">
        <StatusBar
          barStyle={isDark ? "light-content" : "dark-content"}
          backgroundColor={colors.background}
        />
        <ScrollView className="flex-1 p-8">
          <AppText className="text-center" style={{ color: colors.text }}>
            {t("noAccountsFound")}
          </AppText>
        </ScrollView>
      </SafeAreaView>
    );
  }

114-116: Use consistent conditional rendering pattern.

This section uses the && operator for conditional rendering, while other sections (lines 59-61, 63-65, 80-82, 97-99) use the ternary operator with null. Choose one pattern and apply it consistently throughout the component.

Apply this diff to match the pattern used in other sections:

-        {accounts?.find((account) => account.reference === "shared") && (
+        {accounts?.find((account) => account.reference === "shared") ? (
          <SectionHeader title={t("sharedFunds")} delay={1000} />
-        )}
+        ) : null}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae7db7e and 010a387.

📒 Files selected for processing (27)
  • localization/ar/common.json (1 hunks)
  • localization/bn/common.json (1 hunks)
  • localization/de/common.json (1 hunks)
  • localization/en/common.json (1 hunks)
  • localization/es/common.json (1 hunks)
  • localization/fa/common.json (1 hunks)
  • localization/fr/common.json (1 hunks)
  • localization/ha/common.json (1 hunks)
  • localization/hi/common.json (1 hunks)
  • localization/id/common.json (1 hunks)
  • localization/it/common.json (2 hunks)
  • localization/ja/common.json (1 hunks)
  • localization/ko/common.json (1 hunks)
  • localization/ps/common.json (1 hunks)
  • localization/pt/common.json (1 hunks)
  • localization/ru/common.json (1 hunks)
  • localization/sw/common.json (1 hunks)
  • localization/th/common.json (1 hunks)
  • localization/tr/common.json (1 hunks)
  • localization/ur/common.json (1 hunks)
  • localization/vi/common.json (1 hunks)
  • localization/zh/common.json (1 hunks)
  • src/app/(tabs)/banking/index.tsx (2 hunks)
  • src/components/AccountItem.tsx (4 hunks)
  • src/data/mockData.ts (0 hunks)
  • src/schemas/welcomeSchema.ts (3 hunks)
  • src/types/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/data/mockData.ts
🧰 Additional context used
📓 Path-based instructions (5)
localization/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Store and edit language/i18n resources in the localization/ directory

Files:

  • localization/ur/common.json
  • localization/ja/common.json
  • localization/tr/common.json
  • localization/vi/common.json
  • localization/sw/common.json
  • localization/es/common.json
  • localization/fa/common.json
  • localization/bn/common.json
  • localization/hi/common.json
  • localization/en/common.json
  • localization/id/common.json
  • localization/pt/common.json
  • localization/zh/common.json
  • localization/fr/common.json
  • localization/ar/common.json
  • localization/ps/common.json
  • localization/ko/common.json
  • localization/ha/common.json
  • localization/th/common.json
  • localization/ru/common.json
  • localization/it/common.json
  • localization/de/common.json
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/components/AccountItem.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/schemas/welcomeSchema.ts
  • src/types/index.ts
  • src/components/AccountItem.tsx
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/components/AccountItem.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/(tabs)/banking/index.tsx
🧠 Learnings (7)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{app,components,services,utils,contexts}/**/*.{ts,tsx} : Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Applied to files:

  • src/app/(tabs)/banking/index.tsx
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/db/schema.ts : Maintain the database schema in src/db/schema.ts

Applied to files:

  • src/schemas/welcomeSchema.ts
📚 Learning: 2025-06-25T17:49:26.926Z
Learnt from: murtazanoori
Repo: The-Creative-Programming-Group/finance-io PR: 23
File: src/components/zodSchema.ts:4-8
Timestamp: 2025-06-25T17:49:26.926Z
Learning: In Zod schema validation for this project, the pattern of using chained min() validations with different messages is intentional - min(1) with "field is required" for empty inputs, followed by min(2) with "must be at least 2 characters" for inputs that are too short. This provides differentiated error messaging for better UX.

Applied to files:

  • src/schemas/welcomeSchema.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: If modifying authentication features, verify Clerk auth flow (sign-in/sign-up)

Applied to files:

  • src/types/index.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{server,services}/**/*.{ts,tsx} : Database operations must use Drizzle ORM queries (no raw SQL in app code)

Applied to files:

  • src/types/index.ts
📚 Learning: 2025-10-03T19:02:03.226Z
Learnt from: FleetAdmiralJakob
Repo: The-Creative-Programming-Group/finance-io PR: 50
File: src/app/(tabs)/_layout.tsx:5-37
Timestamp: 2025-10-03T19:02:03.226Z
Learning: In lucide-react-native, icons can be imported with an "Icon" suffix (e.g., ChartColumnIcon, CreditCardIcon, SettingsIcon) in addition to the default plain names (ChartColumn, CreditCard, Settings). The suffixed names are available through alternate package exports.

Applied to files:

  • src/components/AccountItem.tsx
📚 Learning: 2025-07-01T08:06:09.826Z
Learnt from: murtazanoori
Repo: The-Creative-Programming-Group/finance-io PR: 29
File: src/components/Icons.tsx:58-68
Timestamp: 2025-07-01T08:06:09.826Z
Learning: In React Native projects using custom fonts, replace native Text components with a custom AppText component located in the components folder. AppText provides automatic font handling and consistent theming. Use the `bold`, `semibold`, or `medium` props instead of `fontWeight` in styles. This is enforced by ESLint rules (no-restricted-syntax).

Applied to files:

  • src/components/AccountItem.tsx
🧬 Code graph analysis (3)
src/app/(tabs)/banking/index.tsx (4)
src/contexts/ThemeContext.tsx (1)
  • useTheme (66-72)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/components/SectionHeader.tsx (1)
  • SectionHeader (12-35)
src/components/AccountItem.tsx (1)
  • AccountItem (17-96)
src/schemas/welcomeSchema.ts (1)
src/db/schema.ts (2)
  • referencesEnum (46-46)
  • references (44-44)
src/types/index.ts (1)
src/schemas/welcomeSchema.ts (1)
  • References (7-7)
🔇 Additional comments (19)
localization/ha/common.json (1)

93-98: Frontend code not updated to use new localization keys—critical runtime failures will occur.

The localization changes in ha/common.json are incomplete. While the new keys are properly added to all 22 locales, the frontend code was not updated and contains three critical mismatches:

  • src/app/start/index.tsx line ~76: Uses t("dashboardSafeAccounts") (removed from localization) instead of t("dashboardSavings")
  • src/app/start/index.tsx line ~79: Uses t("sharedFunds") instead of t("dashboardShared")
  • src/app/(tabs)/banking/index.tsx line: Uses t("sharedFunds") instead of t("dashboardShared")

The "savings" and "shared" account types will fail to display translated labels at runtime. Update these files to reference the new localization keys.

⛔ Skipped due to learnings
Learnt from: murtazanoori
Repo: The-Creative-Programming-Group/finance-io PR: 33
File: localization/en.json:1-73
Timestamp: 2025-08-04T14:05:24.979Z
Learning: In the finance-io project, the contributor murtazanoori prefers flat translation structure over nested grouping due to implementation costs: bulk rename across components, coordination needed for all 16 language files, and lookup performance considerations. The flat structure with ~73-79 keys per language file is acceptable for their current scale.
localization/it/common.json (1)

48-48: Localization updates align with new account reference schema.

The changes consistently implement the transition from a single "safe accounts" label to distinct "savings" and "shared" categories across Italian localization. The referencePlaceholder update also improves translation consistency by using "Aziendale" instead of mixed English/Italian.

Also applies to: 103-104

localization/fa/common.json (1)

176-177: Persian dashboard labels correctly updated.

The new dashboard category keys are properly translated and align with the consistent pattern across all locales.

localization/sw/common.json (1)

96-97: Swahili dashboard terminology updated consistently.

The additions align with the multi-locale refactor replacing "safe accounts" with distinct savings/shared categories.

localization/ko/common.json (1)

104-105: Korean dashboard labels correctly translated.

The new keys are properly localized and follow the established cross-locale pattern.

localization/tr/common.json (1)

103-104: Turkish dashboard terminology updated.

The new keys are properly translated and consistent with the multi-locale refactor.

localization/vi/common.json (1)

96-97: Vietnamese dashboard labels properly translated.

The additions follow the consistent pattern across all localized files.

localization/bn/common.json (1)

95-96: Bengali dashboard terminology correctly updated.

The new keys are properly localized and consistent with the cross-locale refactor.

localization/ur/common.json (1)

104-105: Urdu dashboard labels correctly translated.

The new keys are properly localized and complete the consistent pattern across all 8 localized files.

localization/hi/common.json (1)

104-105: LGTM! Localization keys updated consistently.

The addition of dashboardSavings and dashboardShared keys aligns with the broader refactoring to split dashboard account categories across all locales.

localization/fr/common.json (1)

96-97: LGTM! French translations added correctly.

The translations for "Économies" (Savings) and "Partagé" (Shared) are appropriate and consistent with the refactoring pattern.

localization/de/common.json (2)

113-113: LGTM! Translation refinement applied.

The update from "Tägliche Konten" to "Tageskonten" is a valid translation refinement.


116-117: LGTM! German translations for new dashboard keys.

"Ersparnisse" (Savings) and "Gemeinsam" (Shared) are appropriate translations.

localization/id/common.json (1)

96-97: LGTM! Indonesian translations added.

"Tabungan" (Savings) and "Bersama" (Shared) are appropriate Indonesian translations for the new dashboard keys.

localization/ru/common.json (1)

103-104: LGTM! Russian translations added correctly.

"Сбережения" (Savings) and "Совместный" (Shared) are appropriate Russian translations.

localization/ja/common.json (1)

104-105: LGTM! Japanese translations added.

"貯蓄" (Savings) and "共有" (Shared) are appropriate Japanese translations for the new dashboard keys.

localization/en/common.json (1)

96-97: LGTM! Base English translations for new dashboard keys.

The addition of "Savings" and "Shared" keys properly establishes the base translations for all other locales in this refactoring.

src/types/index.ts (2)

3-13: Well-documented type with clear context.

The comment clearly explains that ClerkUser represents data from Clerk's authentication service rather than the database schema, preventing confusion. The note about phone numbers being a paid feature is also helpful.

As noted in a past review comment, remember to keep this interface in sync with the Clerk dashboard configuration if you modify user fields there.


60-144: Excellent documentation and type safety for navigation.

The extensive documentation for NavigationItems provides clear context, usage examples, and maintenance guidelines. The enum ensures type-safe i18n keys for navigation labels, and the examples demonstrate integration with both Expo Router and react-i18next effectively.

@FleetAdmiralJakob
Copy link
Member

20251031-2306-12.3153163.mp4

The file is missing a trailing newline, which is required by POSIX standards and may cause issues with some tools.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b50ea4d and 7a58670.

📒 Files selected for processing (2)
  • src/app/(tabs)/banking/index.tsx (2 hunks)
  • src/types/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/(tabs)/banking/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/(tabs)/banking/index.tsx
  • src/types/index.ts
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/(tabs)/banking/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/(tabs)/banking/index.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/db/schema.ts : Maintain the database schema in src/db/schema.ts

Applied to files:

  • src/types/index.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: If modifying authentication features, verify Clerk auth flow (sign-in/sign-up)

Applied to files:

  • src/types/index.ts
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{server,services}/**/*.{ts,tsx} : Database operations must use Drizzle ORM queries (no raw SQL in app code)

Applied to files:

  • src/types/index.ts
🧬 Code graph analysis (2)
src/app/(tabs)/banking/index.tsx (5)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/schemas/welcomeSchema.ts (1)
  • References (7-7)
src/types/index.ts (1)
  • Account (17-24)
src/components/SectionHeader.tsx (1)
  • SectionHeader (12-35)
src/components/AccountItem.tsx (2)
  • AccountItem (17-98)
  • AccountItemProps (7-15)
src/types/index.ts (2)
src/schemas/welcomeSchema.ts (1)
  • References (7-7)
src/db/schema.ts (2)
  • categoriesTable (32-42)
  • currenciesTable (19-29)

Comment on lines +1 to +3
import { References } from "~/schemas/welcomeSchema";
import { categoriesTable, currenciesTable } from "~/db/schema";
import { InferSelectModel } from "drizzle-orm";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Prevent shipping server-only modules to the client

These value imports pull ~/db/schema and drizzle-orm into the React Native bundle, which will crash Metro because those modules depend on Node runtime APIs. Switch them to type-only imports so the compiler erases them at emit time.

-import { References } from "~/schemas/welcomeSchema";
-import { categoriesTable, currenciesTable } from "~/db/schema";
-import { InferSelectModel } from "drizzle-orm";
+import type { References } from "~/schemas/welcomeSchema";
+import type { categoriesTable, currenciesTable } from "~/db/schema";
+import type { InferSelectModel } from "drizzle-orm";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { References } from "~/schemas/welcomeSchema";
import { categoriesTable, currenciesTable } from "~/db/schema";
import { InferSelectModel } from "drizzle-orm";
import type { References } from "~/schemas/welcomeSchema";
import type { categoriesTable, currenciesTable } from "~/db/schema";
import type { InferSelectModel } from "drizzle-orm";
🤖 Prompt for AI Agents
In src/types/index.ts lines 1-3, the imports from "~/db/schema" and
"drizzle-orm" are being pulled into the client bundle; change those to type-only
imports so the compiler erases them at emit time: use "import type" for
InferSelectModel and for categoriesTable and currenciesTable (and for References
if it's a type) so runtime modules that depend on Node APIs are not included in
the React Native bundle.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
drizzle/0002_careless_gladiator.sql (1)

1-7: Add trailing newline at end of file.

The file is missing a trailing newline, which is required by POSIX standards and may cause issues with some tools and git workflows.

Apply this fix:

 ALTER TABLE "accounts" ALTER COLUMN "reference" SET DATA TYPE "public"."references" USING "reference"::"public"."references";--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "typeId";
+ALTER TABLE "accounts" DROP COLUMN "typeId";
🧹 Nitpick comments (2)
src/app/(tabs)/banking/index.tsx (2)

25-35: Simplify static reference constants.

The REFERENCE object is wrapped in useMemo with an empty dependency array, but these are static values that never change. Move them outside the component for clarity and performance.

Apply this refactor:

+// Type-safe reference constants (above component)
+const REFERENCE = {
+  PRIVATE: "private" as References,
+  BUSINESS: "business" as References,
+  SAVINGS: "savings" as References,
+  SHARED: "shared" as References,
+} as const;
+
 const Dashboard = () => {
   const { colors, isDark } = useTheme();
   const { t } = useTranslation();

   const { data: accounts } = trpc.accounts.getAccounts.useQuery();

-  // Type-safe reference constants
-  const REFERENCE = React.useMemo(
-    () =>
-      ({
-        PRIVATE: "private" as References,
-        BUSINESS: "business" as References,
-        SAVINGS: "savings" as References,
-        SHARED: "shared" as References,
-      }) as const,
-    [],
-  );

Then remove REFERENCE from all useMemo dependency arrays (lines 40, 44, 48, 52):

   const privateAccounts = React.useMemo(
     () => (accounts ?? []).filter((a) => a.reference === REFERENCE.PRIVATE),
-    [accounts, REFERENCE],
+    [accounts],
   );

117-119: Consider using && for conditional rendering.

The ternary with null works correctly but && is more idiomatic for conditional rendering in React.

-        {hasDailyAccounts ? (
-          <SectionHeader title={t("dashboardDailyAccounts")} delay={400} />
-        ) : null}
+        {hasDailyAccounts && (
+          <SectionHeader title={t("dashboardDailyAccounts")} delay={400} />
+        )}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a58670 and f1d8e2f.

📒 Files selected for processing (3)
  • drizzle/0002_careless_gladiator.sql (1 hunks)
  • src/app/(tabs)/banking/index.tsx (2 hunks)
  • src/server/routers/accounts.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/routers/accounts.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/(tabs)/banking/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/(tabs)/banking/index.tsx
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/(tabs)/banking/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/(tabs)/banking/index.tsx
🧬 Code graph analysis (1)
src/app/(tabs)/banking/index.tsx (5)
src/utils/trpc.ts (1)
  • trpc (4-4)
src/schemas/welcomeSchema.ts (1)
  • References (7-7)
src/types/index.ts (1)
  • Account (17-24)
src/components/SectionHeader.tsx (1)
  • SectionHeader (12-35)
src/components/AccountItem.tsx (1)
  • AccountItem (17-98)
🔇 Additional comments (4)
drizzle/0002_careless_gladiator.sql (1)

6-6: Verify accounts.reference column contains only valid enum values before applying migration.

The CAST operation on line 6 will fail if the column contains NULL values or any data not matching the four enum variants. However, codebase analysis shows all application writes to the reference column are validated through the Zod schema (enforcing 'private', 'business', 'savings', 'shared' in lowercase), and no seed data or direct SQL bypasses were found. Run a pre-migration query to confirm the data state:

SELECT DISTINCT "reference", COUNT(*) FROM "accounts" GROUP BY "reference";
SELECT COUNT(*) FROM "accounts" WHERE "reference" IS NULL;

Proceed only if all existing values match the enum variants exactly.

src/app/(tabs)/banking/index.tsx (3)

8-10: LGTM!

The new imports correctly support TRPC data fetching and type-safe account handling.


59-80: LGTM!

The AccountSection component is well-structured and reusable. It correctly uses account.bankName (addressing previous feedback), applies staggered animation delays, and handles empty states gracefully.


138-151: Verify identical animation delays for Savings and Shared sections.

Both sections use baseDelay={1000}, which will cause them to animate simultaneously. Confirm whether this is intentional or if Shared Funds should have a later delay (e.g., 1200) for sequential animation.

Comment on lines +16 to +18
const { data: accounts } = trpc.accounts.getAccounts.useQuery();

// TODO: Implement loading and error states
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement loading and error states for the accounts query.

The query runs without handling loading or error states. Users will see undefined behavior during loading and no feedback on failure.

Add state handling:

-  const { data: accounts } = trpc.accounts.getAccounts.useQuery();
+  const { data: accounts, isLoading, isError, error } = trpc.accounts.getAccounts.useQuery();

-  // TODO: Implement loading and error states
+  if (isLoading) {
+    return (
+      <SafeAreaView className="flex-1 bg-background dark:bg-dark-background">
+        <AppText className="text-center mt-8">{t("loading")}</AppText>
+      </SafeAreaView>
+    );
+  }
+
+  if (isError) {
+    return (
+      <SafeAreaView className="flex-1 bg-background dark:bg-dark-background">
+        <AppText className="text-center mt-8 text-red-500">
+          {t("errorLoadingAccounts")}: {error.message}
+        </AppText>
+      </SafeAreaView>
+    );
+  }
🤖 Prompt for AI Agents
In src/app/(tabs)/banking/index.tsx around lines 16 to 18, the trpc query result
is used directly without handling loading or error states; update the component
to read isLoading, isError and error from trpc.accounts.getAccounts.useQuery(),
render a loading indicator (spinner/placeholder) while isLoading is true, render
a clear error message or retry UI when isError is true (use error.message), and
guard usages of accounts (e.g., render an empty state or return early) to avoid
undefined behavior; ensure the UI returns meaningful fallback content in both
loading and error cases before rendering the accounts list.

cleaned up the scripts structure
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
scripts/seed.ts (3)

6-31: Add function documentation and consider extracting currencies to a shared constant.

Past reviewers requested more comments for functions, which hasn't been addressed yet. Additionally, FleetAdmiralJakob suggested creating a shared type or constant for currencies that can be reused in the schema to ensure consistency.

Add JSDoc documentation:

+/**
+ * Seeds the database with default currencies (USD, EUR, ARS).
+ * This function is idempotent and will only insert currencies that don't already exist.
+ */
 async function seedCurrencies() {

Consider extracting the currencies to a shared constant in src/db/schema.ts or a new constants file:

// In src/db/schema.ts or src/db/constants.ts
export const SUPPORTED_CURRENCIES = [
  { code: "USD", name: "US Dollar" },
  { code: "EUR", name: "Euro" },
  { code: "ARS", name: "Argentine Peso" },
] as const;

Then import and use it in the seed script:

+import { SUPPORTED_CURRENCIES } from "~/db/schema";

 async function seedCurrencies() {
-  const currencies = [
-    { code: "USD", name: "US Dollar" },
-    { code: "EUR", name: "Euro" },
-    { code: "ARS", name: "Argentine Peso" },
-  ];
+  const currencies = SUPPORTED_CURRENCIES;

Based on past review comments.


33-59: Add function documentation and consider extracting categories to a shared constant.

Similar to seedCurrencies, this function needs JSDoc documentation as requested by past reviewers. FleetAdmiralJakob also suggested extracting categories to a shared constant that can be reused in the schema.

Add JSDoc documentation:

+/**
+ * Seeds the database with default categories (Groceries, Restaurants, Transport, Bills, Entertainment).
+ * This function is idempotent and will only insert categories that don't already exist.
+ */
 async function seedCategories() {

Consider extracting the categories to a shared constant in src/db/schema.ts or a new constants file:

// In src/db/schema.ts or src/db/constants.ts
export const DEFAULT_CATEGORIES = [
  { name: "Groceries", slug: "groceries" },
  { name: "Restaurants", slug: "restaurants" },
  { name: "Transport", slug: "transport" },
  { name: "Bills", slug: "bills" },
  { name: "Entertainment", slug: "entertainment" },
] as const;

Then import and use it in the seed script:

+import { DEFAULT_CATEGORIES } from "~/db/schema";

 async function seedCategories() {
-  const categories = [
-    { name: "Groceries", slug: "groceries" },
-    { name: "Restaurants", slug: "restaurants" },
-    { name: "Transport", slug: "transport" },
-    { name: "Bills", slug: "bills" },
-    { name: "Entertainment", slug: "entertainment" },
-  ];
+  const categories = DEFAULT_CATEGORIES;

Based on past review comments.


61-64: Add function documentation.

The main function should have JSDoc documentation as requested by past reviewers.

+/**
+ * Main entry point for the seed script.
+ * Seeds currencies and categories in sequence.
+ */
 async function main() {

Based on past review comments.

src/app/index.tsx (1)

169-186: Redirect logic is sound; consider simplifying the null check.

The redirect flow correctly guards against premature redirects during loading. However, the condition at line 174 can be simplified.

Apply this diff to simplify the redundant check:

   if (
     isSignedIn &&
     !isLoadingAccounts &&
-    (!accounts || accounts?.length === 0)
+    !accounts?.length
   ) {
     return <Redirect href={"./start"} />;

Note: The loading state UI (showing an ActivityIndicator while isLoadingAccounts is true) is tracked in Linear issue FIN-31 and not yet implemented. While the redirect logic correctly waits for loading to complete, signed-in users will briefly see the landing page content before being redirected, which may cause a visual flash.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1d8e2f and ed578d5.

📒 Files selected for processing (3)
  • package.json (1 hunks)
  • scripts/seed.ts (1 hunks)
  • src/app/index.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.tsx: Use instead of from react-native in all components
All styling should use TailwindCSS classes via NativeWind (avoid inline StyleSheet-based styles)

Files:

  • src/app/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow TypeScript strict mode requirements throughout the codebase

Files:

  • src/app/index.tsx
  • scripts/seed.ts
src/{app,components,services,utils,contexts}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Files:

  • src/app/index.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place screens and routes under src/app/ to follow Expo Router file-based routing

Files:

  • src/app/index.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/{app,components,services,utils,contexts}/**/*.{ts,tsx} : Client-side API calls should use tRPC procedures (do not call REST endpoints directly)

Applied to files:

  • src/app/index.tsx
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to **/*.{ts,tsx} : Follow TypeScript strict mode requirements throughout the codebase

Applied to files:

  • src/app/index.tsx
📚 Learning: 2025-10-19T13:03:20.337Z
Learnt from: CR
Repo: The-Creative-Programming-Group/finance-io PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-19T13:03:20.337Z
Learning: Applies to src/db/schema.ts : Maintain the database schema in src/db/schema.ts

Applied to files:

  • scripts/seed.ts
🧬 Code graph analysis (2)
src/app/index.tsx (1)
src/utils/trpc.ts (1)
  • trpc (4-4)
scripts/seed.ts (2)
src/db/index.ts (1)
  • db (6-6)
src/db/schema.ts (2)
  • currenciesTable (19-29)
  • categoriesTable (32-42)
🔇 Additional comments (1)
scripts/seed.ts (1)

66-69: LGTM!

The error handling is appropriate for a seed script, properly logging errors and exiting with a non-zero code on failure.

Comment on lines +162 to +167
const { data: accounts, isLoading: isLoadingAccounts } =
trpc.accounts.getAccounts.useQuery(undefined, {
// Only fetch accounts when signed in to avoid unauthorized requests
enabled: isSignedIn,
// Note: You could tune caching behavior here (e.g., `staleTime`) if needed
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for the accounts query.

A past review comment flagged that the query lacks error handling. If the fetch fails, accounts will be undefined and isLoadingAccounts will be false, causing the user to be silently redirected to the start screen without any error feedback.

Destructure the error and add error handling:

-  const { data: accounts, isLoading: isLoadingAccounts } =
-    trpc.accounts.getAccounts.useQuery(undefined, {
-      // Only fetch accounts when signed in to avoid unauthorized requests
-      enabled: isSignedIn,
-      // Note: You could tune caching behavior here (e.g., `staleTime`) if needed
-    });
+  const {
+    data: accounts,
+    isLoading: isLoadingAccounts,
+    error: accountsError,
+  } = trpc.accounts.getAccounts.useQuery(undefined, {
+    enabled: isSignedIn,
+  });

Then add error handling before the redirect logic (after line 167):

  if (accountsError && isSignedIn) {
    return (
      <View className="flex-1 items-center justify-center">
        <AppText className="text-red-500 dark:text-red-400">
          {t("errorLoadingAccounts")}
        </AppText>
      </View>
    );
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create migrations for new database structure Analysis and design of integrations of existing screens

4 participants