refactor(ui): Improve usage log filter responsiveness and mobile UX

Refactor the usage log filter toolbar into a shared reusable component for common, drawing, and task logs. Optimize desktop filters with a responsive grid, move secondary filters into a mobile drawer, standardize filter typography, remove redundant filter icons, and add the missing i18n translations for the new drawer description.
This commit is contained in:
t0ng7u
2026-05-25 05:35:44 +08:00
parent b302be30e3
commit 583da45296
79 changed files with 1879 additions and 1262 deletions
@@ -30,7 +30,6 @@ import {
FileText,
KeyRound,
ListChecks,
Play,
RadioTower,
ShieldCheck,
TerminalSquare,
@@ -505,10 +504,10 @@ export function OverviewDashboard() {
const quickActions = useMemo<QuickAction[]>(
() => [
{
title: t('Playground'),
description: t('Test models and prompts from the browser'),
to: '/playground',
icon: Play,
title: t('API Keys'),
description: t('Create a key for your app or service'),
to: '/keys',
icon: KeyRound,
},
{
title: t('Channels'),
@@ -26,6 +26,7 @@ import { VCHART_OPTION } from '@/lib/vchart'
import { useThemeCustomization } from '@/context/theme-customization-provider'
import { useTheme } from '@/context/theme-provider'
import { Skeleton } from '@/components/ui/skeleton'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { getUserQuotaDataByUsers } from '@/features/dashboard/api'
import {
TIME_GRANULARITY_OPTIONS,
@@ -154,61 +155,64 @@ export function UserCharts() {
return (
<div className='space-y-3'>
<div className='flex items-center gap-1.5 overflow-x-auto pb-1 sm:gap-2'>
<div className='flex shrink-0 items-center gap-1.5 rounded-lg border p-0.5'>
{TIME_RANGE_PRESETS.map((preset) => (
<button
key={preset.days}
type='button'
onClick={() => handleRangeChange(preset.days)}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
selectedRange === preset.days
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t(preset.label)}
</button>
))}
</div>
<Tabs
value={String(selectedRange)}
onValueChange={(value) => handleRangeChange(Number(value))}
className='shrink-0'
>
<TabsList>
{TIME_RANGE_PRESETS.map((preset) => (
<TabsTrigger
key={preset.days}
value={String(preset.days)}
className='px-2.5 text-xs'
>
{t(preset.label)}
</TabsTrigger>
))}
</TabsList>
</Tabs>
<div className='flex shrink-0 items-center gap-1.5 rounded-lg border p-0.5'>
{TIME_GRANULARITY_OPTIONS.map((opt) => (
<button
key={opt.value}
type='button'
onClick={() =>
handleGranularityChange(opt.value as TimeGranularity)
}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
timeGranularity === opt.value
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t(opt.label)}
</button>
))}
</div>
<Tabs
value={timeGranularity}
onValueChange={(value) =>
handleGranularityChange(value as TimeGranularity)
}
className='shrink-0'
>
<TabsList>
{TIME_GRANULARITY_OPTIONS.map((opt) => (
<TabsTrigger
key={opt.value}
value={opt.value}
className='px-2.5 text-xs'
>
{t(opt.label)}
</TabsTrigger>
))}
</TabsList>
</Tabs>
<div className='flex shrink-0 items-center gap-1.5 rounded-lg border p-0.5'>
<span className='text-muted-foreground px-2 text-xs font-medium'>
{t('Top Users')}
</span>
{TOP_USER_LIMIT_OPTIONS.map((limit) => (
<button
key={limit}
type='button'
onClick={() => setTopUserLimit(limit)}
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
topUserLimit === limit
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
}`}
>
{t('Top {{count}}', { count: limit })}
</button>
))}
</div>
<Tabs
value={String(topUserLimit)}
onValueChange={(value) => setTopUserLimit(Number(value))}
className='shrink-0'
>
<TabsList>
<span className='text-muted-foreground px-2 text-xs font-medium whitespace-nowrap'>
{t('Top Users')}
</span>
{TOP_USER_LIMIT_OPTIONS.map((limit) => (
<TabsTrigger
key={limit}
value={String(limit)}
className='px-2.5 text-xs'
>
{t('Top {{count}}', { count: limit })}
</TabsTrigger>
))}
</TabsList>
</Tabs>
{isLoading && (
<Loader2 className='text-muted-foreground size-4 animate-spin' />