Skip to content

fix: make the dashboard show total by currency #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 12, 2025

Conversation

bassgeta
Copy link
Contributor

@bassgeta bassgeta commented Aug 6, 2025

Problem

The invoices sent and received just put together the totals and "converted" the value to dollars.

Solution

Display the values by currency instead.

Changes

  • Moved lib/invoice into lib/helpers and renamed the index file to invoice.ts.
  • Created a new currency helper file which receives an array of "payments" and groups them by currency.
  • Use the 2 new helpers across subscriptions, payments, commitments, invoices sent and invoice received components.
  • I've made it so that each component needs to format its own payments into an array of amounts and currencies.

Testing

  1. Get paid should show totals by currency
image 2. Pay should show totals by currency image 3. Dashboard subscriptions should show totals by currency (both commitments and spent) image 4. Subscription plans subscribers should show totals by currency image 5. Subscription plans payments should also show totals by currency image

@bassgeta bassgeta self-assigned this Aug 6, 2025
Copy link
Contributor

coderabbitai bot commented Aug 6, 2025

Walkthrough

This change refactors several components and modules to centralize and standardize currency aggregation and formatting logic using new helper functions. Multiple files update their import paths to use these helpers, and dashboard components now support multi-currency totals using the new MultiCurrencyStatCard and related utilities. No changes were made to exported entity signatures.

Changes

Cohort / File(s) Change Summary
Import Path Refactors
src/app/api/webhook/route.ts, src/app/i/[id]/page.tsx, src/app/invoices/create/page.tsx, src/components/invoice-creator.tsx, src/components/batch-payout.tsx
Updated import paths for getInvoiceCount, generateInvoiceNumber, and handleBatchPayment to new helper module locations under @/lib/helpers/*. No logic changes.
Batch Payment Helper Import Refactor
src/components/dashboard/invoices-received.tsx
Changed import of handleBatchPayment to new helper location. Added imports and usage of calculateTotalsByCurrency, formatCurrencyTotals, and switched from StatCard to MultiCurrencyStatCard for displaying multi-currency totals. Refactored logic to aggregate and display totals by currency.
Multi-Currency Support for Sent Invoices
src/components/dashboard/invoices-sent.tsx
Replaced single-currency total calculation with multi-currency aggregation and formatting using helper functions. Updated UI to use MultiCurrencyStatCard for totals.
Multi-Currency Subscription Summaries
src/components/dashboard/subscriptions.tsx
Refactored subscription commitment and spent amount calculations to use new helper functions for currency aggregation and formatting, removing direct BigNumber arithmetic from the component.
Multi-Currency Payment Table
src/components/subscription-plans/blocks/payments-table.tsx
Replaced manual BigNumber-based revenue aggregation per currency with helper-based aggregation and formatting. Removed inline error handling, delegating it to helpers.
Multi-Currency Subscribers Table
src/components/subscription-plans/blocks/subscribers-table.tsx
Refactored revenue calculation for active subscribers to use helper functions for multi-currency aggregation and formatting, removing direct BigNumber arithmetic and manual error handling.
New Currency Helper Module
src/lib/helpers/currency.ts
Added calculateTotalsByCurrency and formatCurrencyTotals helper functions for aggregating and formatting currency totals using ethers BigNumber utilities. Introduced PaymentItem interface.

Sequence Diagram(s)

sequenceDiagram
  participant Component
  participant Helpers
  participant ethers

  Component->>Helpers: calculateTotalsByCurrency(items)
  Helpers->>ethers: parseUnits(amount, 18) for each item
  Helpers->>Helpers: Sum amounts per currency
  Helpers->>ethers: formatUnits(total, 18) per currency
  Helpers-->>Component: Record<currency, totalString>

  Component->>Helpers: formatCurrencyTotals(totals)
  Helpers-->>Component: Array<{amount, currency}> (filtered, formatted)
  Component->>UI: Render MultiCurrencyStatCard(values)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15–25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • rodrigopavezi
  • MantisClone

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/106-multi-currency-stats

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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 (1)
src/components/dashboard/invoices-received.tsx (1)

209-213: Consider updating batch payment selection display for multi-currency consistency.

The selected invoices total still uses a simple sum with a hardcoded dollar sign, which may not accurately represent multi-currency selections. Consider updating this to use the same currency helper functions for consistency.

-              <p className="text-sm text-zinc-600">
-                Total: $
-                {selectedInvoices
-                  .reduce((sum, invoice) => sum + Number(invoice.amount), 0)
-                  .toLocaleString()}
-              </p>
+              {(() => {
+                const selectedItems = selectedInvoices.map(invoice => ({
+                  amount: invoice.amount,
+                  currency: invoice.paymentCurrency
+                }));
+                const totals = calculateTotalsByCurrency(selectedItems);
+                const values = formatCurrencyTotals(totals);
+                return (
+                  <div className="text-sm text-zinc-600">
+                    Total: {values.map(({amount, currency}) => 
+                      `${amount} ${currency}`
+                    ).join(' + ')}
+                  </div>
+                );
+              })()}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d413e5 and 949dc56.

📒 Files selected for processing (11)
  • src/app/api/webhook/route.ts (1 hunks)
  • src/app/i/[id]/page.tsx (1 hunks)
  • src/app/invoices/create/page.tsx (1 hunks)
  • src/components/batch-payout.tsx (1 hunks)
  • src/components/dashboard/invoices-received.tsx (4 hunks)
  • src/components/dashboard/invoices-sent.tsx (3 hunks)
  • src/components/dashboard/subscriptions.tsx (2 hunks)
  • src/components/invoice-creator.tsx (1 hunks)
  • src/components/subscription-plans/blocks/payments-table.tsx (2 hunks)
  • src/components/subscription-plans/blocks/subscribers-table.tsx (2 hunks)
  • src/lib/helpers/currency.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#83
File: src/components/create-recurring-payment/blocks/create-recurring-payment-form.tsx:127-138
Timestamp: 2025-06-23T09:14:42.979Z
Learning: In the RequestNetwork/easy-invoice codebase, when suggesting improvements like error handling for transaction approvals, the user bassgeta prefers consistency over isolated improvements. They prefer not to implement error handling in one place unless it's done consistently across all similar transaction flows in the codebase.
Learnt from: bassgeta
PR: RequestNetwork/easy-invoice#91
File: drizzle/0007_messy_silver_fox.sql:1-12
Timestamp: 2025-07-14T14:17:05.340Z
Learning: In the RequestNetwork/easy-invoice codebase, monetary amounts are stored as text in the database schema rather than numeric types. This is a deliberate architectural decision to maintain consistency across all amount fields in the system.
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#21
File: src/app/invoices/[ID]/page.tsx:113-148
Timestamp: 2025-02-20T10:27:02.993Z
Learning: The easy-invoice project prefers simpler, direct implementations over abstract utilities. For example, using `.toFixed(2)` directly instead of creating separate number formatting utilities.
📚 Learning: the handleformsubmit function in src/components/invoice-form.tsx correctly uses data.clientemail fro...
Learnt from: rodrigopavezi
PR: RequestNetwork/easy-invoice#45
File: src/components/invoice-form.tsx:316-319
Timestamp: 2025-05-19T13:00:48.790Z
Learning: The handleFormSubmit function in src/components/invoice-form.tsx correctly uses data.clientEmail from the form submission data to find matching payers, which is the proper implementation.

Applied to files:

  • src/app/invoices/create/page.tsx
  • src/app/i/[id]/page.tsx
  • src/app/api/webhook/route.ts
  • src/components/batch-payout.tsx
  • src/components/invoice-creator.tsx
  • src/components/dashboard/invoices-sent.tsx
  • src/components/dashboard/invoices-received.tsx
  • src/components/subscription-plans/blocks/payments-table.tsx
📚 Learning: the easy-invoice project prefers simpler, direct implementations over abstract utilities. for exampl...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#21
File: src/app/invoices/[ID]/page.tsx:113-148
Timestamp: 2025-02-20T10:27:02.993Z
Learning: The easy-invoice project prefers simpler, direct implementations over abstract utilities. For example, using `.toFixed(2)` directly instead of creating separate number formatting utilities.

Applied to files:

  • src/app/invoices/create/page.tsx
  • src/components/subscription-plans/blocks/subscribers-table.tsx
  • src/app/i/[id]/page.tsx
  • src/components/invoice-creator.tsx
  • src/components/dashboard/invoices-sent.tsx
  • src/lib/helpers/currency.ts
  • src/components/dashboard/invoices-received.tsx
  • src/components/dashboard/subscriptions.tsx
  • src/components/subscription-plans/blocks/payments-table.tsx
📚 Learning: the payrequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicp...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:88-109
Timestamp: 2025-02-12T12:40:14.742Z
Learning: The payRequest endpoint in src/server/routers/invoice.ts is intentionally kept public (using publicProcedure) to allow invoice sharing and payment by anyone with the payment reference, similar to how payment links work in other payment systems.

Applied to files:

  • src/app/invoices/create/page.tsx
  • src/app/i/[id]/page.tsx
  • src/app/api/webhook/route.ts
  • src/components/batch-payout.tsx
  • src/components/dashboard/invoices-received.tsx
📚 Learning: the `payrequest` function in the invoice router retrieves payment transactions calldata from the req...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/server/routers/invoice.ts:0-0
Timestamp: 2025-02-12T13:04:46.135Z
Learning: The `payRequest` function in the invoice router retrieves payment transactions calldata from the Request API and does not process the payment itself. The actual payment status update happens through the webhook handler.

Applied to files:

  • src/app/api/webhook/route.ts
📚 Learning: in the easy-invoice codebase, error handling for trpc queries is not required at the component level...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#8
File: src/app/invoice-me/page.tsx:21-21
Timestamp: 2025-02-14T12:48:42.125Z
Learning: In the easy-invoice codebase, error handling for TRPC queries is not required at the component level as confirmed by the maintainer.

Applied to files:

  • src/app/api/webhook/route.ts
📚 Learning: in `src/server/routers/payment.ts`, the batchpay input validation already handles empty arrays corre...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#67
File: src/server/routers/payment.ts:47-49
Timestamp: 2025-06-04T12:02:39.411Z
Learning: In `src/server/routers/payment.ts`, the batchPay input validation already handles empty arrays correctly. The `batchPaymentFormSchema.shape.payouts.optional()` inherits the `.min(1, "At least one payment is required")` validation from the original schema, so empty payouts arrays are automatically rejected even when the field is made optional.

Applied to files:

  • src/components/batch-payout.tsx
  • src/components/dashboard/invoices-received.tsx
📚 Learning: in src/components/batch-payout.tsx, the user prefers to keep the simple 2-second timeout for appkit ...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#64
File: src/components/batch-payout.tsx:100-106
Timestamp: 2025-06-04T10:08:40.123Z
Learning: In src/components/batch-payout.tsx, the user prefers to keep the simple 2-second timeout for AppKit initialization over more complex polling mechanisms when the current approach is working adequately. They favor simplicity over potentially more robust but complex solutions.

Applied to files:

  • src/components/batch-payout.tsx
📚 Learning: invoice items are validated at creation time through the schema validation in src/lib/schemas/invoic...
Learnt from: aimensahnoun
PR: RequestNetwork/easy-invoice#2
File: src/app/invoices/[ID]/page.tsx:160-175
Timestamp: 2025-02-12T12:42:40.076Z
Learning: Invoice items are validated at creation time through the schema validation in src/lib/schemas/invoice.ts, ensuring that items array is non-empty and each item has required description, quantity, and price fields. Runtime validation in display components is not necessary.

Applied to files:

  • src/components/invoice-creator.tsx
🧬 Code Graph Analysis (5)
src/components/subscription-plans/blocks/subscribers-table.tsx (1)
src/lib/helpers/currency.ts (2)
  • calculateTotalsByCurrency (8-35)
  • formatCurrencyTotals (37-49)
src/components/dashboard/invoices-sent.tsx (2)
src/lib/helpers/currency.ts (2)
  • calculateTotalsByCurrency (8-35)
  • formatCurrencyTotals (37-49)
src/components/multi-currency-stat-card.tsx (1)
  • MultiCurrencyStatCard (12-39)
src/components/dashboard/invoices-received.tsx (2)
src/lib/helpers/currency.ts (2)
  • calculateTotalsByCurrency (8-35)
  • formatCurrencyTotals (37-49)
src/components/multi-currency-stat-card.tsx (1)
  • MultiCurrencyStatCard (12-39)
src/components/dashboard/subscriptions.tsx (1)
src/lib/helpers/currency.ts (2)
  • calculateTotalsByCurrency (8-35)
  • formatCurrencyTotals (37-49)
src/components/subscription-plans/blocks/payments-table.tsx (1)
src/lib/helpers/currency.ts (2)
  • calculateTotalsByCurrency (8-35)
  • formatCurrencyTotals (37-49)
🔇 Additional comments (18)
src/components/invoice-creator.tsx (1)

6-6: LGTM! Clean import path refactoring.

The import path update aligns with the broader refactoring to centralize helper functions in the @/lib/helpers directory.

src/lib/helpers/currency.ts (2)

8-35: Well-implemented currency aggregation with proper BigNumber handling.

The function correctly uses BigNumber arithmetic to maintain precision when aggregating amounts across currencies. The error handling ensures graceful degradation when invalid amounts are encountered.


37-49: Good filtering logic for valid currency totals.

The function properly filters out zero amounts and handles parsing errors gracefully. The use of gt(0) ensures only positive amounts are included in the final output.

src/app/invoices/create/page.tsx (1)

5-5: LGTM! Consistent import path refactoring.

The import path update is consistent with the broader effort to organize helper functions in the @/lib/helpers directory.

src/app/i/[id]/page.tsx (1)

5-5: LGTM! Consistent with the helper function reorganization.

The import path update maintains consistency across the codebase while improving module organization.

src/components/batch-payout.tsx (1)

58-58: LGTM! Import path update aligns with helper function refactoring.

The import path change is consistent with the broader effort to organize helper functions in the @/lib/helpers directory.

src/app/api/webhook/route.ts (1)

3-4: LGTM! Clean import path refactoring.

The import paths have been correctly updated to reflect the reorganization of helper functions into the @/lib/helpers directory, which improves code organization and modularity.

src/components/subscription-plans/blocks/subscribers-table.tsx (2)

24-27: LGTM! Clean import of standardized currency helpers.

The new imports bring in centralized currency calculation and formatting utilities, which will improve consistency across the application.

Also applies to: 32-32


185-195: Excellent refactoring to use centralized currency helpers.

The replacement of manual BigNumber arithmetic with standardized helper functions improves code maintainability and consistency. The new approach is cleaner and delegates error handling to the helper functions, making this component more focused on its primary responsibility.

src/components/dashboard/subscriptions.tsx (2)

25-28: LGTM! Import of centralized currency utilities.

The new imports align with the broader refactoring effort to standardize currency calculations across the application.


212-232: Excellent refactoring for multi-currency support.

The replacement of manual BigNumber operations with centralized helper functions significantly improves code maintainability. The separation of commitment and spent calculations is logical and the use of helper functions ensures consistent currency handling across the application.

src/components/dashboard/invoices-received.tsx (2)

14-14: LGTM! Clean import updates for currency helpers and batch payment.

The import path updates align with the modularization effort, bringing in the new currency calculation utilities and MultiCurrencyStatCard component.

Also applies to: 15-18, 31-31


85-92: Excellent multi-currency refactoring.

The replacement of manual calculations with centralized helper functions and the switch to MultiCurrencyStatCard properly supports multi-currency totals, aligning with the PR objective to display totals by currency.

Also applies to: 237-241

src/components/dashboard/invoices-sent.tsx (2)

12-15: LGTM! Consistent import of currency utilities.

The new imports align with the multi-currency refactoring pattern used across other dashboard components.

Also applies to: 21-21


57-64: Excellent multi-currency implementation.

The refactoring to use centralized currency helpers and MultiCurrencyStatCard perfectly aligns with the PR objective to display totals by currency. The implementation is consistent with other dashboard components and eliminates manual BigNumber operations.

Also applies to: 82-86

src/components/subscription-plans/blocks/payments-table.tsx (3)

22-25: LGTM! Clean import structure for currency helpers.

The import of calculateTotalsByCurrency and formatCurrencyTotals from the centralized currency helper module aligns well with the PR objective to standardize currency handling across dashboard components.


30-30: Verify the continued need for ethers utils import.

The utils import from ethers is still being used in the PaymentRow component (lines 64-65) for individual payment amount formatting, so this import remains necessary.


155-161: Excellent refactoring to centralize currency calculations.

The refactoring successfully replaces complex inline BigNumber operations with clean, reusable helper functions. This change:

  1. Improves maintainability - Currency calculation logic is now centralized in src/lib/helpers/currency.ts
  2. Reduces complexity - Eliminates manual BigNumber aggregation and error handling in the component
  3. Enhances consistency - Uses the same pattern as other dashboard components mentioned in the AI summary

The transformation from manual aggregation to helper functions is clean and the data flow remains clear: filtered payments → simplified payment items → currency totals → formatted values.

@MantisClone MantisClone merged commit 3b41f40 into main Aug 12, 2025
3 checks passed
@MantisClone MantisClone deleted the fix/106-multi-currency-stats branch August 12, 2025 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

EasyInvoice - Fix Dashboard stat cards to display totals separately per currency
2 participants