Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions site/src/components/Avatar/AvatarData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ export const AvatarData: FC<AvatarDataProps> = ({
}

return (
<div className="flex items-center w-full gap-3">
<div className="flex items-center w-full min-w-0 gap-3">
{avatar}

<div className="flex flex-col w-full">
<div className="flex flex-col w-full min-w-0">
<span className="text-sm font-semibold text-content-primary">
{title}
</span>
Expand Down
56 changes: 46 additions & 10 deletions site/src/pages/AgentsPage/AgentAnalyticsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import dayjs, { type Dayjs } from "dayjs";
import { type FC, useState } from "react";
import { useQuery } from "react-query";
import { useSearchParams } from "react-router";
import { chatCostSummary } from "#/api/queries/chats";
import type { DateRangeValue } from "#/components/DateRangePicker/DateRangePicker";
import { ScrollArea } from "#/components/ScrollArea/ScrollArea";
import { useAuthContext } from "#/contexts/auth/AuthProvider";
import { AgentAnalyticsPageView } from "./AgentAnalyticsPageView";
import { AgentPageHeader } from "./components/AgentPageHeader";

const createDateRange = (now?: Dayjs) => {
const startDateSearchParam = "startDate";
const endDateSearchParam = "endDate";

const getDefaultDateRange = (now?: Dayjs): DateRangeValue => {
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")}`,
startDate: end.subtract(30, "day").toDate(),
endDate: end.toDate(),
};
};

Expand All @@ -24,13 +27,44 @@ interface AgentAnalyticsPageProps {

const AgentAnalyticsPage: FC<AgentAnalyticsPageProps> = ({ now }) => {
const { user } = useAuthContext();
const [anchor] = useState<Dayjs>(() => dayjs());
const dateRange = createDateRange(now ?? anchor);

const [searchParams, setSearchParams] = useSearchParams();
const startDateParam = searchParams.get(startDateSearchParam)?.trim() ?? "";
const endDateParam = searchParams.get(endDateSearchParam)?.trim() ?? "";
const [defaultDateRange] = useState(() => getDefaultDateRange(now));
let dateRange = defaultDateRange;
let hasExplicitDateRange = false;

if (startDateParam && endDateParam) {
const parsedStartDate = new Date(startDateParam);
const parsedEndDate = new Date(endDateParam);

if (
!Number.isNaN(parsedStartDate.getTime()) &&
!Number.isNaN(parsedEndDate.getTime()) &&
parsedStartDate.getTime() <= parsedEndDate.getTime()
) {
dateRange = {
startDate: parsedStartDate,
endDate: parsedEndDate,
};
hasExplicitDateRange = true;
}
}

const onDateRangeChange = (value: DateRangeValue) => {
setSearchParams((prev) => {
const next = new URLSearchParams(prev);
next.set(startDateSearchParam, value.startDate.toISOString());
next.set(endDateSearchParam, value.endDate.toISOString());
return next;
});
};

const summaryQuery = useQuery({
...chatCostSummary(user?.id ?? "me", {
start_date: dateRange.startDate,
end_date: dateRange.endDate,
start_date: dateRange.startDate.toISOString(),
end_date: dateRange.endDate.toISOString(),
}),
enabled: Boolean(user?.id),
});
Expand All @@ -43,7 +77,9 @@ const AgentAnalyticsPage: FC<AgentAnalyticsPageProps> = ({ now }) => {
isLoading={summaryQuery.isLoading}
error={summaryQuery.error}
onRetry={() => void summaryQuery.refetch()}
rangeLabel={dateRange.rangeLabel}
dateRange={dateRange}
onDateRangeChange={onDateRangeChange}
hasExplicitDateRange={hasExplicitDateRange}
/>
</ScrollArea>
);
Expand Down
27 changes: 20 additions & 7 deletions site/src/pages/AgentsPage/AgentAnalyticsPageView.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,48 @@
import { BarChart3Icon } from "lucide-react";
import type { FC } from "react";
import type { ChatCostSummary } from "#/api/typesGenerated";
import {
DateRangePicker,
type DateRangeValue,
} from "#/components/DateRangePicker/DateRangePicker";
import { ChatCostSummaryView } from "./components/ChatCostSummaryView";
import { SectionHeader } from "./components/SectionHeader";
import { toInclusiveDateRange } from "./utils/dateRange";

interface AgentAnalyticsPageViewProps {
summary: ChatCostSummary | undefined;
isLoading: boolean;
error: unknown;
onRetry: () => void;
rangeLabel: string;
dateRange: DateRangeValue;
onDateRangeChange: (value: DateRangeValue) => void;
hasExplicitDateRange: boolean;
}

export const AgentAnalyticsPageView: FC<AgentAnalyticsPageViewProps> = ({
summary,
isLoading,
error,
onRetry,
rangeLabel,
dateRange,
onDateRangeChange,
hasExplicitDateRange,
}) => {
const displayDateRange = toInclusiveDateRange(
dateRange,
hasExplicitDateRange,
);

return (
<div className="flex flex-col p-4 pt-8">
<div className="mx-auto w-full max-w-3xl">
<SectionHeader
label="Analytics"
description="Review your personal Coder Agents usage and cost breakdowns."
action={
<div className="flex items-center gap-2 text-xs text-content-secondary">
<BarChart3Icon className="h-4 w-4" />
<span>{rangeLabel}</span>
</div>
<DateRangePicker
value={displayDateRange}
onChange={onDateRangeChange}
/>
}
/>

Expand Down
Loading
Loading