diff --git a/site/src/pages/AgentsPage/ConfigureAgentsDialog.tsx b/site/src/pages/AgentsPage/ConfigureAgentsDialog.tsx index 6077008c97..6635692786 100644 --- a/site/src/pages/AgentsPage/ConfigureAgentsDialog.tsx +++ b/site/src/pages/AgentsPage/ConfigureAgentsDialog.tsx @@ -139,21 +139,25 @@ const UserRow: FC<{ ); }; -const UsageContent: FC = () => { +interface UsageContentProps { + now?: dayjs.Dayjs; +} + +const UsageContent: FC = ({ now }) => { const [selectedUser, setSelectedUser] = useState(null); const [usernameFilter, setUsernameFilter] = useState(""); const debouncedUsername = useDebouncedValue(usernameFilter, 300); const [page, setPage] = useState(1); const dateRange = useMemo(() => { - const end = dayjs(); + const end = now ?? dayjs(); const start = end.subtract(30, "day"); return { startDate: start.toISOString(), endDate: end.toISOString(), rangeLabel: `${start.format("MMM D")} – ${end.format("MMM D, YYYY")}`, }; - }, []); + }, [now]); const offset = (page - 1) * pageSize; const usersQuery = useQuery({ @@ -354,6 +358,8 @@ interface ConfigureAgentsDialogProps { canManageChatModelConfigs: boolean; canSetSystemPrompt: boolean; initialSection?: ConfigureAgentsSection; + /** Override the current time for date range calculation. Used for deterministic Storybook snapshots. */ + now?: dayjs.Dayjs; } export const ConfigureAgentsDialog: FC = ({ @@ -362,6 +368,7 @@ export const ConfigureAgentsDialog: FC = ({ canManageChatModelConfigs, canSetSystemPrompt, initialSection = "behavior", + now, }) => { const queryClient = useQueryClient(); @@ -635,7 +642,7 @@ export const ConfigureAgentsDialog: FC = ({ /> )} {activeSection === "usage" && canManageChatModelConfigs && ( - + )} diff --git a/site/src/pages/AgentsPage/UserAnalyticsDialog.stories.tsx b/site/src/pages/AgentsPage/UserAnalyticsDialog.stories.tsx index 314adf0e6d..5d4753a5f5 100644 --- a/site/src/pages/AgentsPage/UserAnalyticsDialog.stories.tsx +++ b/site/src/pages/AgentsPage/UserAnalyticsDialog.stories.tsx @@ -3,7 +3,8 @@ import { withAuthProvider } from "testHelpers/storybook"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { API } from "api/api"; import type * as TypesGen from "api/typesGenerated"; -import { fn, spyOn } from "storybook/test"; +import dayjs from "dayjs"; +import { expect, fn, screen, spyOn, waitFor } from "storybook/test"; import { UserAnalyticsDialog } from "./UserAnalyticsDialog"; const mockSummary: TypesGen.ChatCostSummary = { @@ -63,5 +64,11 @@ export const Default: Story = { args: { open: true, onOpenChange: fn(), + now: dayjs("2026-03-12T12:00:00Z"), + }, + play: async () => { + await waitFor(() => { + expect(screen.getByText(/Feb 10\s*–\s*Mar 12, 2026/)).toBeInTheDocument(); + }); }, }; diff --git a/site/src/pages/AgentsPage/UserAnalyticsDialog.tsx b/site/src/pages/AgentsPage/UserAnalyticsDialog.tsx index 9794b5c0d5..09e457cf24 100644 --- a/site/src/pages/AgentsPage/UserAnalyticsDialog.tsx +++ b/site/src/pages/AgentsPage/UserAnalyticsDialog.tsx @@ -16,8 +16,8 @@ import { useQuery } from "react-query"; import { ChatCostSummaryView } from "./ChatCostSummaryView"; import { SectionHeader } from "./SectionHeader"; -const createDateRange = () => { - const end = dayjs(); +const createDateRange = (now?: dayjs.Dayjs) => { + const end = now ?? dayjs(); const start = end.subtract(30, "day"); return { startDate: start.toISOString(), @@ -29,14 +29,17 @@ const createDateRange = () => { interface UserAnalyticsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; + /** Override the current time for date range calculation. Used for deterministic Storybook snapshots. */ + now?: dayjs.Dayjs; } export const UserAnalyticsDialog: FC = ({ open, onOpenChange, + now, }) => { const { user } = useAuthContext(); - const dateRange = useMemo(createDateRange, []); + const dateRange = useMemo(() => createDateRange(now), [now]); const summaryQuery = useQuery({ ...chatCostSummary(user?.id ?? "me", {