Skip to content

Commit 5c2da5f

Browse files
committed
[TOOL-3485] Dashboard: Improve USD currency formatting (#6304)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on improving the formatting of monetary values across various components by replacing manual formatting with a utility function `toUSD`. This enhances consistency and readability in displaying currency values. ### Detailed summary - Added `toUSD` utility function for consistent currency formatting. - Replaced manual currency formatting in `PaymentsSuccessRate.tsx`, `Payouts.tsx`, `PayCustomersTable.tsx`, `TotalVolumePieChart.tsx`, and `TotalSponsoredChartCard.tsx`. - Modified random number generation in `storyUtils.ts` to use non-integer values. - Updated `format-utils.ts` to use a new compact number formatter. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 846a5b5 commit 5c2da5f

File tree

8 files changed

+41
-26
lines changed

8 files changed

+41
-26
lines changed

apps/dashboard/src/components/pay/PayAnalytics/components/PayCustomersTable.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "@/components/ui/select";
1212
import { SkeletonContainer } from "@/components/ui/skeleton";
1313
import { useState } from "react";
14+
import { toUSD } from "../../../../utils/number";
1415
import {
1516
type PayTopCustomersData,
1617
usePayCustomers,
@@ -249,7 +250,7 @@ function getCSVData(data: PayTopCustomersData["customers"]) {
249250
const header = ["Wallet Address", "Total spend"];
250251
const rows = data.map((customer) => [
251252
customer.walletAddress,
252-
`$${(customer.totalSpendUSDCents / 100).toLocaleString()}`,
253+
toUSD(customer.totalSpendUSDCents / 100),
253254
]);
254255

255256
return { header, rows };

apps/dashboard/src/components/pay/PayAnalytics/components/PaymentsSuccessRate.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SkeletonContainer } from "@/components/ui/skeleton";
99
import { ToolTipLabel } from "@/components/ui/tooltip";
1010
import { cn } from "@/lib/utils";
1111
import { useState } from "react";
12+
import { toUSD } from "../../../../utils/number";
1213
import { usePayVolume } from "../hooks/usePayVolume";
1314
import { CardHeading, FailedToLoad } from "./common";
1415

@@ -229,7 +230,7 @@ function InfoRow(props: {
229230
props.isEmpty
230231
? "$-"
231232
: props.amount !== undefined
232-
? `$${props.amount.toLocaleString()}`
233+
? toUSD(props.amount)
233234
: undefined
234235
}
235236
skeletonData="$50"

apps/dashboard/src/components/pay/PayAnalytics/components/Payouts.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SkeletonContainer } from "@/components/ui/skeleton";
22
import { format } from "date-fns";
33
import { useEffect, useState } from "react";
44
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis } from "recharts";
5+
import { toUSD } from "../../../../utils/number";
56
import { AreaChartLoadingState } from "../../../analytics/area-chart";
67
import { usePayVolume } from "../hooks/usePayVolume";
78
import {
@@ -144,7 +145,7 @@ function RenderData(props: {
144145
props.query.isEmpty
145146
? "$-"
146147
: props.query.data
147-
? `$${props.query.data?.totalPayoutsUSD}`
148+
? toUSD(props.query.data.totalPayoutsUSD)
148149
: undefined
149150
}
150151
skeletonData="$20"

apps/dashboard/src/components/pay/PayAnalytics/components/TotalVolumePieChart.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { SkeletonContainer } from "@/components/ui/skeleton";
22
import { cn } from "@/lib/utils";
33
import { Cell, Pie, PieChart } from "recharts";
4+
import { toUSD } from "../../../../utils/number";
45
import { usePayVolume } from "../hooks/usePayVolume";
56
import { FailedToLoad, chartHeight } from "./common";
67

@@ -155,7 +156,7 @@ function RenderData(props: { query: ProcessedQuery }) {
155156
<SkeletonContainer
156157
loadedData={
157158
queryData
158-
? `$${queryData?.totalAmount.toLocaleString()}`
159+
? toUSD(queryData.totalAmount)
159160
: props.query.isEmpty
160161
? "NA"
161162
: undefined
@@ -187,7 +188,7 @@ function RenderData(props: { query: ProcessedQuery }) {
187188
label={v.name}
188189
amount={
189190
queryData
190-
? `$${v.amount.toLocaleString()}`
191+
? toUSD(v.amount)
191192
: props.query.isEmpty
192193
? "$-"
193194
: undefined

apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/TotalSponsoredChartCard.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { useMemo } from "react";
2424
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
2525
import type { UserOpStats } from "types/analytics";
2626
import { useAllChainsData } from "../../../hooks/chains/allChains";
27-
import { formatTickerNumber } from "../../../lib/format-utils";
27+
import { toUSD } from "../../../utils/number";
2828

2929
type ChartData = Record<string, number> & {
3030
time: string; // human readable date
@@ -210,13 +210,22 @@ export function TotalSponsoredChartCard(props: {
210210
}}
211211
tickLine={false}
212212
axisLine={false}
213-
tickFormatter={(value) => `$${formatTickerNumber(value)}`}
213+
tickFormatter={(value) => toUSD(value)}
214214
/>
215215

216216
<ChartTooltip
217217
cursor={true}
218218
content={
219-
<ChartTooltipContent valueFormatter={(value) => `$${value}`} />
219+
<ChartTooltipContent
220+
valueFormatter={(value) => {
221+
// typeguard
222+
if (typeof value !== "number") {
223+
return "";
224+
}
225+
226+
return toUSD(value);
227+
}}
228+
/>
220229
}
221230
/>
222231
<ChartLegend content={<ChartLegendContent />} />

apps/dashboard/src/components/smart-wallets/AccountAbstractionAnalytics/storyUtils.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ export function createUserOpStatsStub(days: number): UserOpStats[] {
55

66
let d = days;
77
while (d !== 0) {
8-
const successful = Math.floor(Math.random() * 100);
9-
const failed = Math.floor(Math.random() * 100);
10-
const sponsoredUsd = Math.floor(Math.random() * 100);
8+
// don't use Math.floor because real data doesn't not have integer values
9+
const successful = Math.random() * 100;
10+
const failed = Math.random() * 100;
11+
const sponsoredUsd = Math.random() * 100;
12+
1113
stubbedData.push({
1214
date: new Date(2024, 1, d).toLocaleString(),
1315
successful,

apps/dashboard/src/lib/format-utils.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1+
const compactNumberFormatter = new Intl.NumberFormat("en-US", {
2+
notation: "compact",
3+
});
4+
15
export const formatTickerNumber = (value: number) => {
2-
if (value >= 1000000) {
3-
const millions = value / 1000000;
4-
// Only show decimal if not a whole number, up to 2 decimals with no trailing zeros
5-
return `${millions % 1 === 0 ? millions.toFixed(0) : Number(millions.toFixed(2)).toString()}M`;
6-
}
7-
if (value >= 1000) {
8-
const thousands = value / 1000;
9-
// Only show decimal if not a whole number
10-
return `${thousands % 1 === 0 ? thousands.toFixed(0) : thousands.toFixed(1)}k`;
11-
}
12-
return value.toString();
6+
return compactNumberFormatter.format(value);
137
};
148

159
export const formatWalletType = (walletType: string) => {

apps/dashboard/src/utils/number.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
const usdCurrencyFormatter = new Intl.NumberFormat("en-US", {
2+
style: "currency",
3+
currency: "USD", // prefix with $
4+
minimumFractionDigits: 0, // don't show decimal places if value is a whole number
5+
maximumFractionDigits: 2, // at max 2 decimal places
6+
roundingMode: "halfEven", // round to nearest even number, standard practice for financial calculations
7+
notation: "compact", // shows 1.2M instead of 1,200,000, 1.2B instead of 1,200,000,000
8+
});
9+
110
export const toUSD = (value: number) => {
2-
return new Intl.NumberFormat(undefined, {
3-
style: "currency",
4-
currency: "USD",
5-
}).format(value);
11+
return usdCurrencyFormatter.format(value);
612
};
713

814
export const toSize = (value: number | bigint, defaultUnit?: string) => {

0 commit comments

Comments
 (0)