refactor(ui): colocate the usage view, keeping the shared usage components (#32952)

Split for the usage (UsagePage) segment. Most of the folder is the usage page's
own view, but four pieces are reused elsewhere and stay in @/components/UsagePage:
TopKeyView (old-usage), KeyModelUsageView and value_formatters (activity_metrics),
and the shared types (activity_metrics, chartUtils). The other 21 files move into
usage/_components, preserving the folder structure.

The external consumers import only the retained files, so they are untouched. The
moved files' imports of the retained files become @/components/UsagePage paths,
other escaping relative imports are absolutized, and lint suppressions are re-keyed
for moved files only. No behavior change.
This commit is contained in:
ryan-crabbe-berri 2026-07-11 16:51:44 -07:00 • committed by GitHub
parent cb24864a7f
commit ca877c78c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 75 additions and 64 deletions

View file

@ -1502,32 +1502,32 @@
"count": 1
}
},
"src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.tsx": {
"src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageBarChart.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.tsx": {
"src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageLineChart.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx": {
"src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx": {
"no-nested-ternary": {
"count": 1
}
},
"src/components/UsagePage/components/EntityUsage/EntityUsage.tsx": {
"src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx": {
"src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/UsagePage/components/UsageAIChatPanel.tsx": {
"src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx": {
"no-nested-ternary": {
"count": 1
},
@ -1535,7 +1535,7 @@
"count": 1
}
},
"src/components/UsagePage/components/UsagePageView.tsx": {
"src/app/(dashboard)/usage/_components/components/UsagePageView.tsx": {
"no-nested-ternary": {
"count": 1
},
@ -1549,7 +1549,7 @@
"count": 3
}
},
"src/components/UsagePage/hooks/usePaginatedDailyActivity.ts": {
"src/app/(dashboard)/usage/_components/hooks/usePaginatedDailyActivity.ts": {
"react-hooks/refs": {
"count": 1
},

View file

@ -2,7 +2,7 @@ import React, { useMemo } from "react";
import EndpointUsageBarChart from "./components/EndpointUsageBarChart";
import EndpointUsageLineChart from "./components/EndpointUsageLineChart";
import EndpointUsageTable from "./components/EndpointUsageTable";
import { DailyData, MetricWithMetadata } from "../../types";
import { DailyData, MetricWithMetadata } from "@/components/UsagePage/types";
interface EndpointUsageProps {
userSpendData?: {

View file

@ -1,7 +1,7 @@
import React from "react";
import { BarChart, Card, Title } from "@tremor/react";
import { CustomLegend, CustomTooltip } from "@/components/common_components/chartUtils";
import { MetricWithMetadata } from "../../../types";
import { MetricWithMetadata } from "@/components/UsagePage/types";
interface EndpointUsageBarChartProps {
endpointData?: Record<string, MetricWithMetadata>;

View file

@ -1,6 +1,6 @@
import { Card, LineChart, Title } from "@tremor/react";
import { useMemo } from "react";
import { DailyData } from "../../../types";
import { DailyData } from "@/components/UsagePage/types";
interface EndpointUsageLineChartProps {
dailyData?: { results: DailyData[] };

View file

@ -2,7 +2,7 @@ import React from "react";
import { Table, Progress } from "antd";
import type { ColumnsType } from "antd/es/table";
import { MoneyCell } from "@/components/shared/table_cells";
import { MetricWithMetadata } from "../../../types";
import { MetricWithMetadata } from "@/components/UsagePage/types";
interface EndpointUsageTableProps {
endpointData: Record<string, MetricWithMetadata>;

View file

@ -1,6 +1,6 @@
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import * as networking from "../../../networking";
import * as networking from "@/components/networking";
import EntityUsage from "./EntityUsage";
beforeAll(() => {
@ -14,7 +14,7 @@ beforeAll(() => {
});
// Mock the networking module
vi.mock("../../../networking", () => ({
vi.mock("@/components/networking", () => ({
tagDailyActivityCall: vi.fn(),
teamDailyActivityCall: vi.fn(),
organizationDailyActivityCall: vi.fn(),
@ -24,12 +24,12 @@ vi.mock("../../../networking", () => ({
}));
// Mock the child components to simplify testing
vi.mock("../../../activity_metrics", () => ({
vi.mock("@/components/activity_metrics", () => ({
ActivityMetrics: () => <div>Activity Metrics</div>,
processActivityData: () => ({ data: [], metadata: {} }),
}));
vi.mock("./TopKeyView", () => ({
vi.mock("@/components/UsagePage/components/EntityUsage/TopKeyView", () => ({
default: () => <div>Top Keys</div>,
}));
@ -37,15 +37,15 @@ vi.mock("./TopModelView", () => ({
default: () => <div>Top Models</div>,
}));
vi.mock("../../../EntityUsageExport/EntityUsageExportModal", () => ({
vi.mock("@/components/EntityUsageExport/EntityUsageExportModal", () => ({
default: () => <div>Entity Usage Export Modal</div>,
}));
vi.mock("../../../EntityUsageExport", () => ({
vi.mock("@/components/EntityUsageExport", () => ({
UsageExportHeader: () => <div>Usage Export Header</div>,
}));
vi.mock("../../../common_components/team_multi_select", () => ({
vi.mock("@/components/common_components/team_multi_select", () => ({
default: () => <div>Team Multi Select</div>,
}));

View file

@ -26,10 +26,10 @@ import {
import { ExportOutlined, LoadingOutlined } from "@ant-design/icons";
import { Alert, Button } from "antd";
import React, { useMemo, useState } from "react";
import TeamMultiSelect from "../../../common_components/team_multi_select";
import { ActivityMetrics, processActivityData } from "../../../activity_metrics";
import { UsageExportHeader } from "../../../EntityUsageExport";
import type { EntityType } from "../../../EntityUsageExport/types";
import TeamMultiSelect from "@/components/common_components/team_multi_select";
import { ActivityMetrics, processActivityData } from "@/components/activity_metrics";
import { UsageExportHeader } from "@/components/EntityUsageExport";
import type { EntityType } from "@/components/EntityUsageExport/types";
import {
agentDailyActivityCall,
customerDailyActivityCall,
@ -37,13 +37,19 @@ import {
tagDailyActivityCall,
teamDailyActivityCall,
userDailyActivityCall,
} from "../../../networking";
import { getProviderLogoAndName } from "../../../provider_info_helpers";
} from "@/components/networking";
import { getProviderLogoAndName } from "@/components/provider_info_helpers";
import { usePaginatedDailyActivity } from "../../hooks/usePaginatedDailyActivity";
import { BreakdownMetrics, DailyData, EntityMetricWithMetadata, KeyMetricWithMetadata, TagUsage } from "../../types";
import { valueFormatterSpend } from "../../utils/value_formatters";
import {
BreakdownMetrics,
DailyData,
EntityMetricWithMetadata,
KeyMetricWithMetadata,
TagUsage,
} from "@/components/UsagePage/types";
import { valueFormatterSpend } from "@/components/UsagePage/utils/value_formatters";
import EndpointUsage from "../EndpointUsage/EndpointUsage";
import TopKeyView from "./TopKeyView";
import TopKeyView from "@/components/UsagePage/components/EntityUsage/TopKeyView";
import TopModelView from "./TopModelView";
interface EntityMetrics {

View file

@ -2,13 +2,13 @@ import { fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi, beforeEach } from "vitest";
import SpendByProvider from "./SpendByProvider";
vi.mock("../../../shared/chart_loader", () => ({
vi.mock("@/components/shared/chart_loader", () => ({
ChartLoader: ({ isDateChanging }: { isDateChanging: boolean }) => (
<div data-testid="chart-loader">{isDateChanging ? "Processing date selection..." : "Loading chart data..."}</div>
),
}));
vi.mock("../../../molecules/models/ProviderLogo", () => ({
vi.mock("@/components/molecules/models/ProviderLogo", () => ({
ProviderLogo: ({ provider }: { provider: string }) => <div data-testid={`provider-logo-${provider}`}>{provider}</div>,
}));

View file

@ -17,8 +17,8 @@ import {
} from "@tremor/react";
import { Tooltip } from "antd";
import React, { useState } from "react";
import { ProviderLogo } from "../../../molecules/models/ProviderLogo";
import { ChartLoader } from "../../../shared/chart_loader";
import { ProviderLogo } from "@/components/molecules/models/ProviderLogo";
import { ChartLoader } from "@/components/shared/chart_loader";
type ProviderSpendData = {
provider: string;

View file

@ -2,8 +2,8 @@ import { BarChart } from "@/components/shared/charts";
import { MoneyCell } from "@/components/shared/table_cells";
import { Segmented } from "antd";
import { useState } from "react";
import { formatNumberWithCommas } from "../../../../utils/dataUtils";
import { DataTable } from "../../../view_logs/table";
import { formatNumberWithCommas } from "@/utils/dataUtils";
import { DataTable } from "@/components/view_logs/table";
type TopModel = {
key: string;

View file

@ -1,6 +1,6 @@
import { screen } from "@testing-library/react";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../../../../tests/test-utils";
import { renderWithProviders } from "@/../tests/test-utils";
import UsageAIChatPanel from "./UsageAIChatPanel";
beforeAll(() => {
@ -13,7 +13,7 @@ beforeAll(() => {
}
});
vi.mock("../../networking", () => ({
vi.mock("@/components/networking", () => ({
modelHubCall: vi.fn().mockResolvedValue({
data: [{ model_group: "gpt-4" }, { model_group: "claude-3-opus" }],
}),

View file

@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from "react";
import { Button, Select, Input, Spin } from "antd";
import ReactMarkdown from "react-markdown";
import { modelHubCall, usageAiChatStream, UsageAiToolCallEvent } from "../../networking";
import { modelHubCall, usageAiChatStream, UsageAiToolCallEvent } from "@/components/networking";
const { TextArea } = Input;

View file

@ -5,9 +5,9 @@ import { useCurrentUser } from "@/app/(dashboard)/hooks/users/useCurrentUser";
import { useInfiniteUsers } from "@/app/(dashboard)/hooks/users/useUsers";
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../../../../tests/test-utils";
import type { Organization } from "../../networking";
import * as networking from "../../networking";
import { renderWithProviders } from "@/../tests/test-utils";
import type { Organization } from "@/components/networking";
import * as networking from "@/components/networking";
import UsagePage from "./UsagePageView";
// Polyfill ResizeObserver for test environment
@ -22,23 +22,23 @@ beforeAll(() => {
});
// Mock the networking module
vi.mock("../../networking", () => ({
vi.mock("@/components/networking", () => ({
userDailyActivityCall: vi.fn(),
userDailyActivityAggregatedCall: vi.fn(),
tagListCall: vi.fn(),
}));
// Mock child components to simplify testing
vi.mock("../../activity_metrics", () => ({
vi.mock("@/components/activity_metrics", () => ({
ActivityMetrics: () => <div>Activity Metrics</div>,
processActivityData: () => ({ data: [], metadata: {} }),
}));
vi.mock("../../view_user_spend", () => ({
vi.mock("@/components/view_user_spend", () => ({
default: () => <div>View User Spend</div>,
}));
vi.mock("./EntityUsage/TopKeyView", () => ({
vi.mock("@/components/UsagePage/components/EntityUsage/TopKeyView", () => ({
default: () => <div>Top Keys</div>,
}));
@ -80,7 +80,7 @@ vi.mock("./UsageViewSelect/UsageViewSelect", async () => {
return { UsageViewSelect };
});
vi.mock("../../shared/advanced_date_picker", async () => {
vi.mock("@/components/shared/advanced_date_picker", async () => {
const React = await import("react");
const AdvancedDatePicker = () => {
return React.createElement("div", { "data-testid": "advanced-date-picker" }, "Date Picker");
@ -89,15 +89,15 @@ vi.mock("../../shared/advanced_date_picker", async () => {
return { default: AdvancedDatePicker };
});
vi.mock("../../user_agent_activity", () => ({
vi.mock("@/components/user_agent_activity", () => ({
default: () => <div>User Agent Activity</div>,
}));
vi.mock("../../cloudzero_export_modal", () => ({
vi.mock("@/components/cloudzero_export_modal", () => ({
default: () => <div>CloudZero Export Modal</div>,
}));
vi.mock("../../EntityUsageExport", () => ({
vi.mock("@/components/EntityUsageExport", () => ({
default: () => <div>Entity Usage Export Modal</div>,
}));

View file

@ -33,24 +33,29 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useCurrentUser } from "@/app/(dashboard)/hooks/users/useCurrentUser";
import { useInfiniteUsers } from "@/app/(dashboard)/hooks/users/useUsers";
import { formatNumberWithCommas } from "@/utils/dataUtils";
import { all_admin_roles, internalUserRoles } from "../../../utils/roles";
import { ActivityMetrics, processActivityData } from "../../activity_metrics";
import CloudZeroExportModal from "../../cloudzero_export_modal";
import EntityUsageExportModal from "../../EntityUsageExport";
import { Team } from "../../key_team_helpers/key_list";
import { Organization, tagListCall, userDailyActivityAggregatedCall, userDailyActivityCall } from "../../networking";
import AdvancedDatePicker from "../../shared/advanced_date_picker";
import { ChartLoader } from "../../shared/chart_loader";
import { Tag } from "../../tag_management/types";
import UserAgentActivity from "../../user_agent_activity";
import ViewUserSpend from "../../view_user_spend";
import { all_admin_roles, internalUserRoles } from "@/utils/roles";
import { ActivityMetrics, processActivityData } from "@/components/activity_metrics";
import CloudZeroExportModal from "@/components/cloudzero_export_modal";
import EntityUsageExportModal from "@/components/EntityUsageExport";
import { Team } from "@/components/key_team_helpers/key_list";
import {
Organization,
tagListCall,
userDailyActivityAggregatedCall,
userDailyActivityCall,
} from "@/components/networking";
import AdvancedDatePicker from "@/components/shared/advanced_date_picker";
import { ChartLoader } from "@/components/shared/chart_loader";
import { Tag } from "@/components/tag_management/types";
import UserAgentActivity from "@/components/user_agent_activity";
import ViewUserSpend from "@/components/view_user_spend";
import { usePaginatedDailyActivity } from "../hooks/usePaginatedDailyActivity";
import { DailyData, KeyMetricWithMetadata, MetricWithMetadata } from "../types";
import { valueFormatterSpend } from "../utils/value_formatters";
import { DailyData, KeyMetricWithMetadata, MetricWithMetadata } from "@/components/UsagePage/types";
import { valueFormatterSpend } from "@/components/UsagePage/utils/value_formatters";
import EndpointUsage from "./EndpointUsage/EndpointUsage";
import EntityUsage, { EntityList } from "./EntityUsage/EntityUsage";
import SpendByProvider from "./EntityUsage/SpendByProvider";
import TopKeyView from "./EntityUsage/TopKeyView";
import TopKeyView from "@/components/UsagePage/components/EntityUsage/TopKeyView";
import UsageAIChatPanel from "./UsageAIChatPanel";
import { UsageOption, UsageViewSelect } from "./UsageViewSelect/UsageViewSelect";

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { DailyData } from "../types";
import { DailyData } from "@/components/UsagePage/types";
export interface PaginationProgress {
currentPage: number;

View file

@ -1,6 +1,6 @@
"use client";
import NewUsagePage from "@/components/UsagePage/components/UsagePageView";
import NewUsagePage from "./_components/components/UsagePageView";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations";