Skip to content

Commit 3b41f40

Browse files
fix: make the dashboard show total by currency (#115)
Co-authored-by: MantisClone <david.huntmateo@request.network>
1 parent 18acbb1 commit 3b41f40

File tree

14 files changed

+135
-126
lines changed

14 files changed

+135
-126
lines changed

src/app/api/webhook/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import crypto from "node:crypto";
22
import { ResourceNotFoundError } from "@/lib/errors";
3-
import { getInvoiceCount } from "@/lib/invoice";
4-
import { generateInvoiceNumber } from "@/lib/invoice/client";
3+
import { generateInvoiceNumber } from "@/lib/helpers/client";
4+
import { getInvoiceCount } from "@/lib/helpers/invoice";
55
import { db } from "@/server/db";
66
import {
77
paymentDetailsPayersTable,

src/app/i/[id]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BackgroundWrapper } from "@/components/background-wrapper";
22
import { Footer } from "@/components/footer";
33
import { Header } from "@/components/header";
44
import { InvoiceCreator } from "@/components/invoice-creator";
5-
import { getInvoiceCount } from "@/lib/invoice";
5+
import { getInvoiceCount } from "@/lib/helpers/invoice";
66
import { api } from "@/trpc/server";
77
import { ArrowLeft } from "lucide-react";
88
import type { Metadata } from "next";

src/app/invoices/create/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BackgroundWrapper } from "@/components/background-wrapper";
22
import { Footer } from "@/components/footer";
33
import { Header } from "@/components/header";
44
import { InvoiceCreator } from "@/components/invoice-creator";
5-
import { getInvoiceCount } from "@/lib/invoice";
5+
import { getInvoiceCount } from "@/lib/helpers/invoice";
66
import { getCurrentSession } from "@/server/auth";
77
import { ArrowLeft } from "lucide-react";
88
import Link from "next/link";

src/components/batch-payout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import {
5555
formatCurrencyLabel,
5656
getPaymentCurrenciesForPayout,
5757
} from "@/lib/constants/currencies";
58-
import { handleBatchPayment } from "@/lib/invoice/batch-payment";
58+
import { handleBatchPayment } from "@/lib/helpers/batch-payment";
5959
import { payoutSchema } from "@/lib/schemas/payment";
6060
import { api } from "@/trpc/react";
6161
import { z } from "zod";

src/components/dashboard/invoices-received.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111
TableRow,
1212
} from "@/components/ui/table/table";
1313
import { ID_TO_APPKIT_NETWORK, NETWORK_TO_ID } from "@/lib/constants/chains";
14-
import { handleBatchPayment } from "@/lib/invoice/batch-payment";
14+
import { handleBatchPayment } from "@/lib/helpers/batch-payment";
15+
import {
16+
calculateTotalsByCurrency,
17+
formatCurrencyTotals,
18+
} from "@/lib/helpers/currency";
1519
import type { Request } from "@/server/db/schema";
1620
import { api } from "@/trpc/react";
1721
import {
@@ -24,6 +28,7 @@ import { ethers } from "ethers";
2428
import { AlertCircle, DollarSign, FileText } from "lucide-react";
2529
import { useState } from "react";
2630
import { toast } from "sonner";
31+
import { MultiCurrencyStatCard } from "../multi-currency-stat-card";
2732
import { StatCard } from "../stat-card";
2833
import { EmptyState } from "../ui/table/empty-state";
2934
import { Pagination } from "../ui/table/pagination";
@@ -77,8 +82,14 @@ export const InvoicesReceived = ({
7782
refetchInterval: RETRIEVE_ALL_INVOICES_POLLING_INTERVAL,
7883
});
7984

80-
const total =
81-
invoices?.reduce((acc, inv) => acc + Number(inv.amount), 0) || 0;
85+
const invoiceItems =
86+
invoices?.map((invoice) => ({
87+
amount: invoice.amount,
88+
currency: invoice.paymentCurrency,
89+
})) || [];
90+
91+
const totalsByCurrency = calculateTotalsByCurrency(invoiceItems);
92+
const totalValues = formatCurrencyTotals(totalsByCurrency);
8293
const outstanding =
8394
invoices?.filter((inv) => inv.status !== "paid").length || 0;
8495

@@ -223,10 +234,10 @@ export const InvoicesReceived = ({
223234
value={outstanding}
224235
icon={<AlertCircle className="h-4 w-4 text-zinc-600" />}
225236
/>
226-
<StatCard
237+
<MultiCurrencyStatCard
227238
title="Total Due"
228-
value={`$${total.toLocaleString()}`}
229239
icon={<DollarSign className="h-4 w-4 text-zinc-600" />}
240+
values={totalValues}
230241
/>
231242
</div>
232243

src/components/dashboard/invoices-sent.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ import {
99
TableHeader,
1010
TableRow,
1111
} from "@/components/ui/table/table";
12+
import {
13+
calculateTotalsByCurrency,
14+
formatCurrencyTotals,
15+
} from "@/lib/helpers/currency";
1216
import type { Request } from "@/server/db/schema";
1317
import { api } from "@/trpc/react";
1418
import { AlertCircle, DollarSign, FileText, Plus } from "lucide-react";
1519
import Link from "next/link";
1620
import { useState } from "react";
21+
import { MultiCurrencyStatCard } from "../multi-currency-stat-card";
1722
import { StatCard } from "../stat-card";
1823
import { EmptyState } from "../ui/table/empty-state";
1924
import { Pagination } from "../ui/table/pagination";
@@ -49,8 +54,15 @@ export const InvoicesSent = ({ initialSentInvoices }: InvoicesSentProps) => {
4954
refetchInterval: RETRIEVE_ALL_INVOICES_POLLING_INTERVAL,
5055
});
5156

52-
const total =
53-
invoices?.reduce((acc, inv) => acc + Number(inv.amount), 0) || 0;
57+
const invoiceItems =
58+
invoices?.map((invoice) => ({
59+
amount: invoice.amount,
60+
currency: invoice.paymentCurrency,
61+
})) || [];
62+
63+
const totalsByCurrency = calculateTotalsByCurrency(invoiceItems);
64+
const totalValues = formatCurrencyTotals(totalsByCurrency);
65+
5466
const outstanding =
5567
invoices?.filter((inv) => inv.status !== "paid").length || 0;
5668

@@ -67,10 +79,10 @@ export const InvoicesSent = ({ initialSentInvoices }: InvoicesSentProps) => {
6779
value={outstanding}
6880
icon={<AlertCircle className="h-4 w-4 text-zinc-600" />}
6981
/>
70-
<StatCard
82+
<MultiCurrencyStatCard
7183
title="Total Payments"
72-
value={`$${total.toLocaleString()}`}
7384
icon={<DollarSign className="h-4 w-4 text-zinc-600" />}
85+
values={totalValues}
7486
/>
7587
</div>
7688

src/components/dashboard/subscriptions.tsx

Lines changed: 22 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ import {
2222
import { CompletedPayments } from "@/components/view-recurring-payments/blocks/completed-payments";
2323
import { FrequencyBadge } from "@/components/view-recurring-payments/blocks/frequency-badge";
2424
import { formatCurrencyLabel } from "@/lib/constants/currencies";
25+
import {
26+
calculateTotalsByCurrency,
27+
formatCurrencyTotals,
28+
} from "@/lib/helpers/currency";
2529
import { useCancelRecurringPayment } from "@/lib/hooks/use-cancel-recurring-payment";
2630
import type { SubscriptionWithDetails } from "@/lib/types";
2731
import { getCanCancelPayment } from "@/lib/utils";
2832
import { api } from "@/trpc/react";
2933
import { addDays, format } from "date-fns";
30-
import { BigNumber, utils } from "ethers";
3134
import { Ban, CreditCard, DollarSign, Loader2 } from "lucide-react";
3235
import { useState } from "react";
3336
import { MultiCurrencyStatCard } from "../multi-currency-stat-card";
@@ -206,64 +209,27 @@ export const Subscriptions = ({ initialSubscriptions }: SubscriptionProps) => {
206209
initialData: initialSubscriptions,
207210
});
208211

209-
const commitmentsByCurrency =
210-
subscriptions?.reduce(
211-
(acc, sub) => {
212-
if (ACTIVE_STATUSES.includes(sub.status) && sub.paymentCurrency) {
213-
const currency = sub.paymentCurrency;
214-
try {
215-
const nextPaymentAmount = utils.parseUnits(sub.totalAmount, 18);
216-
if (!acc[currency]) {
217-
acc[currency] = BigNumber.from("0");
218-
}
219-
acc[currency] = acc[currency].add(nextPaymentAmount);
220-
} catch (error) {
221-
console.error("Error calculating commitment amount:", error);
222-
}
223-
}
224-
return acc;
225-
},
226-
{} as Record<string, BigNumber>,
227-
) || {};
228-
229-
const totalSpentByCurrency =
230-
subscriptions?.reduce(
231-
(acc, sub) => {
232-
if (sub.payments && sub.payments.length > 0 && sub.paymentCurrency) {
233-
const currency = sub.paymentCurrency;
234-
try {
235-
const paymentAmount = utils.parseUnits(sub.totalAmount, 18);
236-
const completedPayments = BigNumber.from(
237-
sub.payments.length.toString(),
238-
);
239-
const totalSpent = paymentAmount.mul(completedPayments);
212+
const commitmentItems =
213+
subscriptions
214+
?.filter((sub) => ACTIVE_STATUSES.includes(sub.status))
215+
.map((sub) => ({
216+
amount: sub.totalAmount,
217+
currency: sub.paymentCurrency,
218+
})) || [];
240219

241-
if (!acc[currency]) {
242-
acc[currency] = BigNumber.from("0");
243-
}
244-
acc[currency] = acc[currency].add(totalSpent);
245-
} catch (error) {
246-
console.error("Error calculating total spent:", error);
247-
}
248-
}
249-
return acc;
250-
},
251-
{} as Record<string, BigNumber>,
252-
) || {};
220+
const spentItems =
221+
subscriptions
222+
?.filter((sub) => (sub?.payments ? sub.payments.length > 0 : false))
223+
.flatMap((sub) => ({
224+
amount: sub.totalAmount,
225+
currency: sub.paymentCurrency,
226+
})) || [];
253227

254-
const commitmentValues = Object.entries(commitmentsByCurrency).map(
255-
([currency, amount]) => ({
256-
amount: utils.formatUnits(amount, 18),
257-
currency,
258-
}),
259-
);
228+
const commitmentTotals = calculateTotalsByCurrency(commitmentItems);
229+
const spentTotals = calculateTotalsByCurrency(spentItems);
260230

261-
const spentValues = Object.entries(totalSpentByCurrency).map(
262-
([currency, amount]) => ({
263-
amount: utils.formatUnits(amount, 18),
264-
currency,
265-
}),
266-
);
231+
const commitmentValues = formatCurrencyTotals(commitmentTotals);
232+
const spentValues = formatCurrencyTotals(spentTotals);
267233

268234
return (
269235
<div className="space-y-6">

src/components/invoice-creator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { InvoiceForm } from "@/components/invoice-form";
44
import { InvoicePreview } from "@/components/invoice-preview";
55
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6-
import { generateInvoiceNumber } from "@/lib/invoice/client";
6+
import { generateInvoiceNumber } from "@/lib/helpers/client";
77
import {
88
type InvoiceFormValues,
99
invoiceFormSchema,

src/components/subscription-plans/blocks/payments-table.tsx

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ import {
1919
TableRow,
2020
} from "@/components/ui/table/table";
2121
import { formatCurrencyLabel } from "@/lib/constants/currencies";
22+
import {
23+
calculateTotalsByCurrency,
24+
formatCurrencyTotals,
25+
} from "@/lib/helpers/currency";
2226
import type { SubscriptionPayment } from "@/lib/types";
2327
import type { SubscriptionPlan } from "@/server/db/schema";
2428
import { api } from "@/trpc/react";
2529
import { format } from "date-fns";
26-
import { BigNumber, utils } from "ethers";
30+
import { utils } from "ethers";
2731
import {
2832
CreditCard,
2933
DollarSign,
@@ -148,29 +152,13 @@ export function PaymentsTable({
148152
? payments.filter((payment) => payment.planId === activePlan)
149153
: payments;
150154

151-
const revenuesByCurrency = filteredPayments.reduce(
152-
(acc, payment) => {
153-
const currency = payment.currency;
154-
try {
155-
const amount = utils.parseUnits(payment.amount || "0", 18);
156-
if (!acc[currency]) {
157-
acc[currency] = BigNumber.from("0");
158-
}
159-
acc[currency] = acc[currency].add(amount);
160-
} catch (error) {
161-
console.error("Error calculating payment revenue:", error);
162-
}
163-
return acc;
164-
},
165-
{} as Record<string, BigNumber>,
166-
);
155+
const paymentItems = filteredPayments.map((payment) => ({
156+
amount: payment.amount,
157+
currency: payment.currency,
158+
}));
167159

168-
const revenueValues = Object.entries(revenuesByCurrency).map(
169-
([currency, amount]) => ({
170-
amount: utils.formatUnits(amount, 18),
171-
currency,
172-
}),
173-
);
160+
const revenueTotal = calculateTotalsByCurrency(paymentItems);
161+
const revenueValues = formatCurrencyTotals(revenueTotal);
174162

175163
const paginatedPayments = filteredPayments.slice(
176164
(page - 1) * ITEMS_PER_PAGE,

src/components/subscription-plans/blocks/subscribers-table.tsx

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ import {
2121
import { CompletedPayments } from "@/components/view-recurring-payments/blocks/completed-payments";
2222
import { FrequencyBadge } from "@/components/view-recurring-payments/blocks/frequency-badge";
2323
import { formatCurrencyLabel } from "@/lib/constants/currencies";
24+
import {
25+
calculateTotalsByCurrency,
26+
formatCurrencyTotals,
27+
} from "@/lib/helpers/currency";
2428
import type { SubscriptionWithDetails } from "@/lib/types";
2529
import type { SubscriptionPlan } from "@/server/db/schema";
2630
import { api } from "@/trpc/react";
2731
import { addDays, format } from "date-fns";
28-
import { BigNumber, utils } from "ethers";
32+
import { utils } from "ethers";
2933
import { CreditCard, DollarSign, Filter } from "lucide-react";
3034
import { useState } from "react";
3135
import { StatCard } from "../../stat-card";
@@ -178,38 +182,17 @@ export function SubscribersTable({
178182
ACTIVE_STATUSES.includes(sub.status),
179183
).length;
180184

181-
const revenuesByCurrency = filteredSubscribers.reduce(
182-
(acc, sub) => {
183-
if (
184-
ACTIVE_STATUSES.includes(sub.status) &&
185-
sub.payments &&
186-
sub.paymentCurrency
187-
) {
188-
const currency = sub.paymentCurrency;
189-
try {
190-
const totalAmount = utils.parseUnits(sub.totalAmount || "0", 18);
191-
const paymentsCount = BigNumber.from(sub.payments.length.toString());
192-
const revenue = totalAmount.mul(paymentsCount);
193-
194-
if (!acc[currency]) {
195-
acc[currency] = BigNumber.from("0");
196-
}
197-
acc[currency] = acc[currency].add(revenue);
198-
} catch (error) {
199-
console.error("Error calculating revenue:", error);
200-
}
201-
}
202-
return acc;
203-
},
204-
{} as Record<string, BigNumber>,
205-
);
185+
const revenueItems = filteredSubscribers
186+
.filter(
187+
(sub) => ACTIVE_STATUSES.includes(sub.status) && sub.payments?.length,
188+
)
189+
.flatMap((sub) => ({
190+
amount: sub.totalAmount,
191+
currency: sub.paymentCurrency,
192+
}));
206193

207-
const revenueValues = Object.entries(revenuesByCurrency)
208-
.map(([currency, amount]) => ({
209-
amount: utils.formatUnits(amount, 18),
210-
currency,
211-
}))
212-
.filter((value) => utils.parseUnits(value.amount, 18).gt(0));
194+
const revenueTotal = calculateTotalsByCurrency(revenueItems);
195+
const revenueValues = formatCurrencyTotals(revenueTotal);
213196

214197
return (
215198
<div className="space-y-6">

0 commit comments

Comments
 (0)