Merge pull request #23399 from BerriAI/revert-23174-feat_adding_timezone_to_usage_page

Revert "feat: adding a timezone picker to the usage page"
This commit is contained in:
ryan-crabbe
2026-03-11 16:34:14 -07:00
committed by GitHub
5 changed files with 15 additions and 126 deletions
@@ -85,10 +85,9 @@ interface EntityUsageProps {
entityList: EntityList[] | null;
premiumUser: boolean;
dateValue: DateRangePickerValue;
timezoneOffset?: number;
}
const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, entityId, entityList, dateValue, timezoneOffset }) => {
const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, entityId, entityList, dateValue }) => {
const [spendData, setSpendData] = useState<EntitySpendData>({
results: [],
metadata: {
@@ -120,7 +119,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
timezoneOffset,
);
setSpendData(data);
} else if (entityType === "team") {
@@ -130,7 +128,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
timezoneOffset,
);
setSpendData(data);
} else if (entityType === "organization") {
@@ -140,7 +137,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
timezoneOffset,
);
setSpendData(data);
} else if (entityType === "customer") {
@@ -150,7 +146,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
timezoneOffset,
);
setSpendData(data);
} else if (entityType === "agent") {
@@ -160,7 +155,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags : null,
timezoneOffset,
);
setSpendData(data);
} else if (entityType === "user") {
@@ -170,7 +164,6 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
endTime,
1,
selectedTags.length > 0 ? selectedTags[0] : null,
timezoneOffset,
);
setSpendData(data);
} else {
@@ -180,7 +173,7 @@ const EntityUsage: React.FC<EntityUsageProps> = ({ accessToken, entityType, enti
useEffect(() => {
fetchSpendData();
}, [accessToken, dateValue, entityId, selectedTags, timezoneOffset]);
}, [accessToken, dateValue, entityId, selectedTags]);
const getTopModels = () => {
const modelSpend: { [key: string]: any } = {};
@@ -78,8 +78,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
to: initialToDate,
});
const [timezoneOffset, setTimezoneOffset] = useState<number>(() => new Date().getTimezoneOffset());
const [allTags, setAllTags] = useState<EntityList[]>([]);
const { data: customers = [] } = useCustomers();
const { data: agentsResponse } = useAgents();
@@ -378,14 +376,14 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
try {
// Prefer aggregated endpoint to avoid many page requests
try {
const aggregated = await userDailyActivityAggregatedCall(accessToken, startTime, endTime, effectiveUserId, timezoneOffset);
const aggregated = await userDailyActivityAggregatedCall(accessToken, startTime, endTime, effectiveUserId);
setUserSpendData(aggregated);
return;
} catch (e) {
// Fallback to paginated calls if aggregated endpoint is unavailable
}
const firstPageData = await userDailyActivityCall(accessToken, startTime, endTime, 1, effectiveUserId, timezoneOffset);
const firstPageData = await userDailyActivityCall(accessToken, startTime, endTime, 1, effectiveUserId);
if (firstPageData.metadata.total_pages <= 1) {
setUserSpendData(firstPageData);
@@ -396,7 +394,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
const aggregatedMetadata = { ...firstPageData.metadata };
for (let page = 2; page <= firstPageData.metadata.total_pages; page++) {
const pageData = await userDailyActivityCall(accessToken, startTime, endTime, page, effectiveUserId, timezoneOffset);
const pageData = await userDailyActivityCall(accessToken, startTime, endTime, page, effectiveUserId);
allResults.push(...pageData.results);
if (pageData.metadata) {
aggregatedMetadata.total_spend += pageData.metadata.total_spend || 0;
@@ -417,7 +415,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
setLoading(false);
setIsDateChanging(false);
}
}, [accessToken, dateValue.from, dateValue.to, selectedUserId, isAdmin, userID, timezoneOffset]);
}, [accessToken, dateValue.from, dateValue.to, selectedUserId, isAdmin, userID]);
// Super responsive date change handler
const handleDateChange = useCallback((newValue: DateRangePickerValue) => {
@@ -497,7 +495,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
onChange={(value) => setUsageView(value)}
isAdmin={isAdmin}
/>
<AdvancedDatePicker value={dateValue} onValueChange={handleDateChange} timezoneOffset={timezoneOffset} onTimezoneChange={setTimezoneOffset} />
<AdvancedDatePicker value={dateValue} onValueChange={handleDateChange} />
</div>
{/* Your Usage Panel */}
{usageView === "global" && (
@@ -827,7 +825,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
})) || null
}
premiumUser={premiumUser}
timezoneOffset={timezoneOffset}
/>
)}
@@ -846,7 +843,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
}
premiumUser={premiumUser}
dateValue={dateValue}
timezoneOffset={timezoneOffset}
/>
)}
@@ -865,7 +861,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
}
premiumUser={premiumUser}
dateValue={dateValue}
timezoneOffset={timezoneOffset}
/>
)}
{/* Tag Usage Panel */}
@@ -896,7 +891,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
entityList={allTags}
premiumUser={premiumUser}
dateValue={dateValue}
timezoneOffset={timezoneOffset}
/>
</>
)}
@@ -911,7 +905,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
}
premiumUser={premiumUser}
dateValue={dateValue}
timezoneOffset={timezoneOffset}
/>
)}
{/* User Usage Panel */}
@@ -924,7 +917,6 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
entityList={userOptions.length > 0 ? userOptions : null}
premiumUser={premiumUser}
dateValue={dateValue}
timezoneOffset={timezoneOffset}
/>
)}
{/* User Agent Activity Panel */}
@@ -1698,7 +1698,6 @@ const buildDailyActivityUrl = (
endTime: Date,
page: number,
extraQueryParams?: Record<string, DailyActivityQueryValue>,
timezoneOffset?: number,
) => {
const resolvedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
const baseUrl = proxyBaseUrl ? `${proxyBaseUrl}${resolvedEndpoint}` : resolvedEndpoint;
@@ -1709,8 +1708,7 @@ const buildDailyActivityUrl = (
params.append("page_size", DEFAULT_DAILY_ACTIVITY_PAGE_SIZE);
params.append("page", page.toString());
// Send timezone offset so backend can adjust date range for UTC storage
const tz = timezoneOffset !== undefined ? timezoneOffset : new Date().getTimezoneOffset();
params.append("timezone", tz.toString());
params.append("timezone", new Date().getTimezoneOffset().toString());
if (extraQueryParams) {
Object.entries(extraQueryParams).forEach(([key, value]) => {
@@ -1729,7 +1727,6 @@ type DailyActivityCallOptions = {
endTime: Date;
page?: number;
extraQueryParams?: Record<string, DailyActivityQueryValue>;
timezoneOffset?: number;
};
const fetchDailyActivity = async ({
@@ -1739,10 +1736,9 @@ const fetchDailyActivity = async ({
endTime,
page = 1,
extraQueryParams,
timezoneOffset,
}: DailyActivityCallOptions) => {
try {
const url = buildDailyActivityUrl(endpoint, startTime, endTime, page, extraQueryParams, timezoneOffset);
const url = buildDailyActivityUrl(endpoint, startTime, endTime, page, extraQueryParams);
const response = await fetch(url, {
method: "GET",
@@ -1767,7 +1763,7 @@ const fetchDailyActivity = async ({
}
};
export const userDailyActivityCall = async (accessToken: string, startTime: Date, endTime: Date, page: number = 1, userId: string | null = null, timezoneOffset?: number) => {
export const userDailyActivityCall = async (accessToken: string, startTime: Date, endTime: Date, page: number = 1, userId: string | null = null) => {
/**
* Get daily user activity on proxy
*/
@@ -1780,7 +1776,6 @@ export const userDailyActivityCall = async (accessToken: string, startTime: Date
extraQueryParams: {
user_id: userId,
},
timezoneOffset,
});
};
@@ -1790,7 +1785,6 @@ export const tagDailyActivityCall = async (
endTime: Date,
page: number = 1,
tags: string[] | null = null,
timezoneOffset?: number,
) => {
/**
* Get daily user activity on proxy
@@ -1804,7 +1798,6 @@ export const tagDailyActivityCall = async (
extraQueryParams: {
tags,
},
timezoneOffset,
});
};
@@ -1814,7 +1807,6 @@ export const teamDailyActivityCall = async (
endTime: Date,
page: number = 1,
teamIds: string[] | null = null,
timezoneOffset?: number,
) => {
/**
* Get daily user activity on proxy
@@ -1829,7 +1821,6 @@ export const teamDailyActivityCall = async (
team_ids: teamIds,
exclude_team_ids: "litellm-dashboard",
},
timezoneOffset,
});
};
@@ -1839,7 +1830,6 @@ export const organizationDailyActivityCall = async (
endTime: Date,
page: number = 1,
organizationIds: string[] | null = null,
timezoneOffset?: number,
) => {
return fetchDailyActivity({
accessToken,
@@ -1850,7 +1840,6 @@ export const organizationDailyActivityCall = async (
extraQueryParams: {
organization_ids: organizationIds,
},
timezoneOffset,
});
};
@@ -1860,7 +1849,6 @@ export const customerDailyActivityCall = async (
endTime: Date,
page: number = 1,
customerIds: string[] | null = null,
timezoneOffset?: number,
) => {
return fetchDailyActivity({
accessToken,
@@ -1871,7 +1859,6 @@ export const customerDailyActivityCall = async (
extraQueryParams: {
end_user_ids: customerIds,
},
timezoneOffset,
});
};
@@ -1881,7 +1868,6 @@ export const agentDailyActivityCall = async (
endTime: Date,
page: number = 1,
agentIds: string[] | null = null,
timezoneOffset?: number,
) => {
return fetchDailyActivity({
accessToken,
@@ -1892,7 +1878,6 @@ export const agentDailyActivityCall = async (
extraQueryParams: {
agent_ids: agentIds,
},
timezoneOffset,
});
};
@@ -3196,7 +3181,7 @@ export const keyAliasesCall = async (
}
};
export const userDailyActivityAggregatedCall = async (accessToken: string, startTime: Date, endTime: Date, userId: string | null = null, timezoneOffset?: number) => {
export const userDailyActivityAggregatedCall = async (accessToken: string, startTime: Date, endTime: Date, userId: string | null = null) => {
/**
* Get aggregated daily user activity (no pagination)
*/
@@ -3213,8 +3198,7 @@ export const userDailyActivityAggregatedCall = async (accessToken: string, start
queryParams.append("start_date", formatDate(startTime));
queryParams.append("end_date", formatDate(endTime));
// Send timezone offset so backend can adjust date range for UTC storage
const tz = timezoneOffset !== undefined ? timezoneOffset : new Date().getTimezoneOffset();
queryParams.append("timezone", tz.toString());
queryParams.append("timezone", new Date().getTimezoneOffset().toString());
if (userId) {
queryParams.append("user_id", userId);
}
@@ -1,8 +1,7 @@
import { CalendarOutlined, ClockCircleOutlined, SearchOutlined } from "@ant-design/icons";
import { Select } from "antd";
import { CalendarOutlined, ClockCircleOutlined } from "@ant-design/icons";
import { Button, DateRangePickerValue, Text } from "@tremor/react";
import moment from "moment";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
interface AdvancedDatePickerProps {
value: DateRangePickerValue;
@@ -10,48 +9,8 @@ interface AdvancedDatePickerProps {
label?: string;
className?: string;
showTimeRange?: boolean;
timezoneOffset?: number;
onTimezoneChange?: (offset: number) => void;
}
const getLocalTimezoneOffset = (): number => new Date().getTimezoneOffset();
const formatTimezoneLabel = (offsetMinutes: number): string => {
if (offsetMinutes === 0) return "UTC";
const sign = offsetMinutes > 0 ? "-" : "+";
const absMinutes = Math.abs(offsetMinutes);
const hours = Math.floor(absMinutes / 60);
const minutes = absMinutes % 60;
const formatted = minutes > 0 ? `${hours}:${String(minutes).padStart(2, "0")}` : `${hours}`;
return `UTC${sign}${formatted}`;
};
// Common timezone options: offset in minutes (JS getTimezoneOffset convention: positive = west of UTC)
const TIMEZONE_OPTIONS: { label: string; offset: number }[] = [
{ label: "UTC-10 (Hawaii)", offset: 600 },
{ label: "UTC-9 (Alaska)", offset: 540 },
{ label: "UTC-8 (Pacific)", offset: 480 },
{ label: "UTC-7 (Mountain)", offset: 420 },
{ label: "UTC-6 (Central)", offset: 360 },
{ label: "UTC-5 (Eastern)", offset: 300 },
{ label: "UTC-4 (Atlantic)", offset: 240 },
{ label: "UTC-3 (Buenos Aires)", offset: 180 },
{ label: "UTC (London/UTC)", offset: 0 },
{ label: "UTC+1 (Central Europe)", offset: -60 },
{ label: "UTC+2 (Eastern Europe)", offset: -120 },
{ label: "UTC+3 (Moscow)", offset: -180 },
{ label: "UTC+4 (Dubai)", offset: -240 },
{ label: "UTC+5 (Pakistan)", offset: -300 },
{ label: "UTC+5:30 (India)", offset: -330 },
{ label: "UTC+5:45 (Nepal)", offset: -345 },
{ label: "UTC+6 (Bangladesh)", offset: -360 },
{ label: "UTC+7 (Bangkok)", offset: -420 },
{ label: "UTC+8 (Singapore)", offset: -480 },
{ label: "UTC+9 (Japan/Korea)", offset: -540 },
{ label: "UTC+10 (Sydney)", offset: -600 },
{ label: "UTC+12 (Auckland)", offset: -720 },
];
interface RelativeTimeOption {
label: string;
shortLabel: string;
@@ -109,22 +68,7 @@ const AdvancedDatePicker: React.FC<AdvancedDatePickerProps> = ({
onValueChange,
label = "Select Time Range",
showTimeRange = true,
timezoneOffset,
onTimezoneChange,
}) => {
const timezoneOptions = useMemo(() => {
const localOffset = getLocalTimezoneOffset();
const hasLocalInList = TIMEZONE_OPTIONS.some((tz) => tz.offset === localOffset);
const opts = TIMEZONE_OPTIONS.map((tz) => ({
value: tz.offset,
label: tz.label + (tz.offset === localOffset ? " (Local)" : ""),
}));
if (!hasLocalInList) {
opts.unshift({ value: localOffset, label: `Local (${formatTimezoneLabel(localOffset)})` });
}
return opts;
}, []);
const [isOpen, setIsOpen] = useState(false);
const [tempValue, setTempValue] = useState<DateRangePickerValue>(value);
const [selectedOption, setSelectedOption] = useState<string | null>(null);
@@ -134,7 +78,6 @@ const AdvancedDatePicker: React.FC<AdvancedDatePickerProps> = ({
const [endDate, setEndDate] = useState("");
const dropdownRef = useRef<HTMLDivElement>(null);
const settingsPanelRef = useRef<HTMLDivElement>(null);
// Function to check if current value matches a relative time option
const getMatchingOption = useCallback((currentValue: DateRangePickerValue): string | null => {
@@ -400,29 +343,6 @@ const AdvancedDatePicker: React.FC<AdvancedDatePickerProps> = ({
</div>
<div className="p-6 space-y-6 pb-20">
{/* Timezone selector */}
{onTimezoneChange && (
<div ref={settingsPanelRef}>
<label className="text-sm text-gray-700 mb-1 block">Timezone</label>
<Select
showSearch
value={timezoneOffset !== undefined ? timezoneOffset : getLocalTimezoneOffset()}
onChange={(val) => onTimezoneChange(val)}
options={timezoneOptions}
popupMatchSelectWidth={false}
listHeight={200}
filterOption={(input, option) =>
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
}
getPopupContainer={() => settingsPanelRef.current!}
suffixIcon={null}
prefix={<SearchOutlined className="text-gray-400" />}
className="w-full"
size="small"
/>
</div>
)}
{/* Start date */}
<div>
<label className="text-sm text-gray-700 mb-1 block">Start date</label>
File diff suppressed because one or more lines are too long