Spend logs spend column enhancement

This commit is contained in:
yuneng-jiang
2025-12-06 16:14:33 -08:00
parent 324ce58003
commit 4e4a2e7ca2
4 changed files with 68 additions and 16 deletions
@@ -424,7 +424,7 @@ const NewUsagePage: React.FC<NewUsagePageProps> = ({
<div className="flex items-end justify-between gap-6 mb-6">
<div className="flex-1">
<TabGroup>
<div className="flex items-end justify-start gap-6 mb-6">
<div className="flex items-end justify-between gap-6 mb-6 w-full">
<TabList variant="solid">
{all_admin_roles.includes(userRole || "") ? <Tab>Global Usage</Tab> : <Tab>Your Usage</Tab>}
{all_admin_roles.includes(userRole || "") ? (
@@ -1,10 +1,10 @@
import { getSpendString } from "@/utils/dataUtils";
import type { ColumnDef } from "@tanstack/react-table";
import { Badge, Button } from "@tremor/react";
import { Tooltip } from "antd";
import React, { useState } from "react";
import { getProviderLogoAndName } from "../provider_info_helpers";
import { Tooltip } from "antd";
import { TimeCell } from "./time_cell";
import { Button, Badge } from "@tremor/react";
import { formatNumberWithCommas } from "@/utils/dataUtils";
// Helper to get the appropriate logo URL
const getLogoUrl = (row: LogEntry, provider: string) => {
@@ -145,7 +145,11 @@ export const columns: ColumnDef<LogEntry>[] = [
{
header: "Cost",
accessorKey: "spend",
cell: (info: any) => <span>${formatNumberWithCommas(info.getValue() || 0, 6)}</span>,
cell: (info: any) => (
<Tooltip title={`$${String(info.getValue() || 0)} `}>
<span>{getSpendString(info.getValue() || 0)}</span>
</Tooltip>
),
},
{
header: "Duration (s)",
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
import { copyToClipboard, formatNumberWithCommas, updateExistingKeys } from "../../src/utils/dataUtils";
import { copyToClipboard, formatNumberWithCommas, getSpendString, updateExistingKeys } from "./dataUtils";
// Mock NotificationsManager
vi.mock("../../src/components/molecules/notifications_manager", () => ({
@@ -10,7 +10,7 @@ vi.mock("../../src/components/molecules/notifications_manager", () => ({
}));
// Import the mocked module
import NotificationsManager from "../../src/components/molecules/notifications_manager";
import NotificationsManager from "../components/molecules/notifications_manager";
const mockNotificationsManager = vi.mocked(NotificationsManager);
describe("dataUtils", () => {
@@ -71,9 +71,41 @@ describe("dataUtils", () => {
expect(formatNumberWithCommas(undefined)).toBe("-");
});
it("should handle zero", () => {
expect(formatNumberWithCommas(0)).toBe("0");
expect(formatNumberWithCommas(0, 2)).toBe("0.00");
it("should handle zero and non-finite values", () => {
expect(formatNumberWithCommas(0)).toBe("-");
expect(formatNumberWithCommas(0, 2)).toBe("-");
expect(formatNumberWithCommas(Infinity)).toBe("-");
expect(formatNumberWithCommas(Number.NaN)).toBe("-");
});
it("should abbreviate large numbers", () => {
expect(formatNumberWithCommas(1_234_567, 1, true)).toBe("1.2M");
expect(formatNumberWithCommas(12_345, 2, true)).toBe("12.35K");
expect(formatNumberWithCommas(-1_200, 2, true)).toBe("-1.20K");
});
});
describe("getSpendString", () => {
it("should return '-' for null, undefined, zero, or non-finite", () => {
expect(getSpendString(null)).toBe("-");
expect(getSpendString(undefined)).toBe("-");
expect(getSpendString(0)).toBe("-");
expect(getSpendString(Number.NaN)).toBe("-");
});
it("should format spend with dollar sign", () => {
expect(getSpendString(1234.5, 2)).toBe("$1,234.50");
expect(getSpendString(-2500, 0)).toBe("$-2,500");
});
it("should return threshold string for very small values", () => {
expect(getSpendString(0.0000004, 6)).toBe("< $0.000001");
expect(getSpendString(-0.0000004, 6)).toBe("< $0.000001");
});
it("should respect custom decimals", () => {
expect(getSpendString(0.01234, 3)).toBe("$0.012");
expect(getSpendString(999.9999, 1)).toBe("$1,000.0");
});
});
@@ -118,7 +150,7 @@ describe("dataUtils", () => {
// Mock DOM methods
const mockTextArea = {
value: "",
style: {},
style: {} as Record<string, string>,
setAttribute: vi.fn(),
focus: vi.fn(),
select: vi.fn(),
@@ -149,7 +181,7 @@ describe("dataUtils", () => {
// Mock DOM methods
const mockTextArea = {
value: "",
style: {},
style: {} as Record<string, string>,
setAttribute: vi.fn(),
focus: vi.fn(),
select: vi.fn(),
@@ -171,7 +203,7 @@ describe("dataUtils", () => {
it("should set textarea properties correctly", async () => {
const mockTextArea = {
value: "",
style: {},
style: {} as Record<string, string>,
setAttribute: vi.fn(),
focus: vi.fn(),
select: vi.fn(),
@@ -213,7 +245,7 @@ describe("dataUtils", () => {
it("should clean up textarea element after successful copy", async () => {
const mockTextArea = {
value: "",
style: {},
style: {} as Record<string, string>,
setAttribute: vi.fn(),
focus: vi.fn(),
select: vi.fn(),
@@ -237,7 +269,7 @@ describe("dataUtils", () => {
document.execCommand = vi.fn().mockReturnValue(true);
const mockTextArea = {
value: "",
style: {},
style: {} as Record<string, string>,
setAttribute: vi.fn(),
focus: vi.fn(),
select: vi.fn(),
+17 -1
View File
@@ -17,7 +17,7 @@ export const formatNumberWithCommas = (
decimals: number = 0,
abbreviate: boolean = false,
): string => {
if (value === null || value === undefined || !Number.isFinite(value)) {
if (value === null || value === undefined || !Number.isFinite(value) || value === 0) {
return "-";
}
@@ -46,6 +46,22 @@ export const formatNumberWithCommas = (
return `${sign}${scaled.toLocaleString("en-US", opts)}${suffix}`;
};
export const getSpendString = (value: number | null | undefined, decimals: number = 6): string => {
if (value === null || value === undefined || !Number.isFinite(value) || value === 0) {
return "-";
}
const formatted = formatNumberWithCommas(value, decimals);
const numericFormatted = Number(formatted.replace(/,/g, ""));
if (numericFormatted === 0) {
const threshold = (1 / 10 ** decimals).toFixed(decimals);
return `< $${threshold}`;
}
return `$${formatted}`;
};
export const copyToClipboard = async (
text: string | null | undefined,
messageText: string = "Copied to clipboard",