mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-16 13:34:04 +08:00
feat(usage): turn refresh interval into a select and align control widths
Replace the click-to-cycle refresh button with a Select matching the source/model filters. The "off" option now shows a localized label (zh/en/ja/zh-TW) instead of the cryptic "--", and changing the interval still invalidates all usage queries for an immediate refresh. Align the top-bar controls into two width groups: source and model selects at 120px, refresh select and date range trigger at 150px. The date range button moves from auto width to fixed, so its long custom range label gets truncate + hover title, and the calendar icon is shrink-proofed.
This commit is contained in:
@@ -21,7 +21,6 @@ import {
|
||||
LayoutGrid,
|
||||
} from "lucide-react";
|
||||
import { ProviderIcon } from "@/components/ProviderIcon";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -47,6 +46,9 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
const APP_FILTER_OPTIONS: AppTypeFilter[] = ["all", ...KNOWN_APP_TYPES];
|
||||
|
||||
// 0 表示关闭自动刷新(refetchInterval=false)
|
||||
const REFRESH_INTERVAL_OPTIONS_MS = [0, 5000, 10000, 30000, 60000] as const;
|
||||
|
||||
// 与 AppSwitcher 的 appIconName 保持一致(codex 复用 openai 图标)
|
||||
const APP_FILTER_ICON: Record<AppType, string> = {
|
||||
claude: "claude",
|
||||
@@ -93,14 +95,7 @@ export function UsageDashboard() {
|
||||
// usage 查询,实现实时刷新(仅在 Dashboard 挂载时生效,离开页面自动取消监听)
|
||||
useUsageEventBridge();
|
||||
|
||||
const refreshIntervalOptionsMs = [0, 5000, 10000, 30000, 60000] as const;
|
||||
const changeRefreshInterval = () => {
|
||||
const currentIndex = refreshIntervalOptionsMs.indexOf(
|
||||
refreshIntervalMs as (typeof refreshIntervalOptionsMs)[number],
|
||||
);
|
||||
const safeIndex = currentIndex >= 0 ? currentIndex : 3;
|
||||
const nextIndex = (safeIndex + 1) % refreshIntervalOptionsMs.length;
|
||||
const next = refreshIntervalOptionsMs[nextIndex];
|
||||
const changeRefreshInterval = (next: number) => {
|
||||
setRefreshIntervalMs(next);
|
||||
queryClient.invalidateQueries({ queryKey: usageKeys.all });
|
||||
};
|
||||
@@ -211,7 +206,7 @@ export function UsageDashboard() {
|
||||
onValueChange={(v) => changeProviderName(decodeOptionValue(v))}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="h-9 w-[110px] bg-background text-xs [&>span]:min-w-0 [&>span]:truncate"
|
||||
className="h-9 w-[120px] bg-background text-xs [&>span]:min-w-0 [&>span]:truncate"
|
||||
title={providerName ?? t("usage.filterBySource")}
|
||||
>
|
||||
<SelectValue />
|
||||
@@ -257,17 +252,28 @@ export function UsageDashboard() {
|
||||
</Select>
|
||||
|
||||
<div className="flex items-center gap-2 ml-auto lg:ml-0">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-9 px-3 text-xs"
|
||||
title={t("common.refresh", "刷新")}
|
||||
onClick={changeRefreshInterval}
|
||||
<Select
|
||||
value={String(refreshIntervalMs)}
|
||||
onValueChange={(v) => changeRefreshInterval(Number(v))}
|
||||
>
|
||||
<RefreshCw className="mr-2 h-3.5 w-3.5" />
|
||||
{refreshIntervalMs > 0 ? `${refreshIntervalMs / 1000}s` : "--"}
|
||||
</Button>
|
||||
<SelectTrigger
|
||||
className="h-9 w-[150px] bg-background text-xs"
|
||||
title={t("usage.refreshInterval")}
|
||||
aria-label={t("usage.refreshInterval")}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<RefreshCw className="h-3.5 w-3.5 shrink-0" />
|
||||
<SelectValue />
|
||||
</span>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{REFRESH_INTERVAL_OPTIONS_MS.map((ms) => (
|
||||
<SelectItem key={ms} value={String(ms)}>
|
||||
{ms > 0 ? `${ms / 1000}s` : t("usage.refreshOff")}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<UsageDateRangePicker
|
||||
selection={range}
|
||||
|
||||
@@ -273,9 +273,10 @@ export function UsageDateRangePicker({
|
||||
<Button
|
||||
type="button"
|
||||
variant={selection.preset === "custom" ? "default" : "outline"}
|
||||
className="justify-start gap-2"
|
||||
className="w-[150px] justify-start gap-2"
|
||||
title={triggerLabel}
|
||||
>
|
||||
<CalendarDays className="h-4 w-4" />
|
||||
<CalendarDays className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate">{triggerLabel}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
@@ -1446,6 +1446,8 @@
|
||||
"allModels": "All Models",
|
||||
"filterBySource": "Filter by source",
|
||||
"filterByModel": "Filter by model",
|
||||
"refreshInterval": "Auto-refresh interval",
|
||||
"refreshOff": "Off",
|
||||
"timeRange": "Time Range",
|
||||
"customRange": "Calendar Filter",
|
||||
"customRangeHint": "Supports both date and time",
|
||||
|
||||
@@ -1446,6 +1446,8 @@
|
||||
"allModels": "すべてのモデル",
|
||||
"filterBySource": "ソースで絞り込む",
|
||||
"filterByModel": "モデルで絞り込む",
|
||||
"refreshInterval": "自動更新間隔",
|
||||
"refreshOff": "オフ",
|
||||
"timeRange": "期間",
|
||||
"customRange": "カレンダーフィルター",
|
||||
"customRangeHint": "日付と時刻の両方に対応",
|
||||
|
||||
@@ -1418,6 +1418,8 @@
|
||||
"allModels": "全部模型",
|
||||
"filterBySource": "依來源篩選",
|
||||
"filterByModel": "依模型篩選",
|
||||
"refreshInterval": "自動重新整理間隔",
|
||||
"refreshOff": "關閉",
|
||||
"timeRange": "時間範圍",
|
||||
"customRange": "日曆篩選",
|
||||
"customRangeHint": "支援日期與時間",
|
||||
|
||||
@@ -1446,6 +1446,8 @@
|
||||
"allModels": "全部模型",
|
||||
"filterBySource": "按来源筛选",
|
||||
"filterByModel": "按模型筛选",
|
||||
"refreshInterval": "自动刷新间隔",
|
||||
"refreshOff": "关闭",
|
||||
"timeRange": "时间范围",
|
||||
"customRange": "日历筛选",
|
||||
"customRangeHint": "支持日期与时间",
|
||||
|
||||
Reference in New Issue
Block a user