feat: enhance UI and functionality in various components
This commit is contained in:
@@ -24,10 +24,8 @@ let themeManagerPromise: Promise<
|
||||
(typeof import('@visactor/vchart'))['ThemeManager']
|
||||
> | null = null
|
||||
|
||||
type UserChartTab = 'rank' | 'trend'
|
||||
|
||||
const CHART_TABS: {
|
||||
value: UserChartTab
|
||||
const USER_CHARTS: {
|
||||
value: string
|
||||
labelKey: string
|
||||
specKey: keyof ProcessedUserChartData
|
||||
}[] = [
|
||||
@@ -46,7 +44,6 @@ const CHART_TABS: {
|
||||
export function UserCharts() {
|
||||
const { t } = useTranslation()
|
||||
const { resolvedTheme } = useTheme()
|
||||
const [activeTab, setActiveTab] = useState<UserChartTab>('rank')
|
||||
const [themeReady, setThemeReady] = useState(false)
|
||||
const themeManagerRef = useRef<
|
||||
(typeof import('@visactor/vchart'))['ThemeManager'] | null
|
||||
@@ -121,9 +118,6 @@ export function UserCharts() {
|
||||
[userData, isLoading, timeGranularity, t]
|
||||
)
|
||||
|
||||
const activeSpec = CHART_TABS.find((tab) => tab.value === activeTab)
|
||||
const spec = activeSpec ? chartData[activeSpec.specKey] : null
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
{/* Toolbar: time range presets + granularity */}
|
||||
@@ -169,50 +163,41 @@ export function UserCharts() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Chart card */}
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<div className='flex w-full flex-col gap-3 border-b px-4 py-3 sm:px-5 lg:flex-row lg:items-center lg:justify-between'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Users className='text-muted-foreground/60 size-4' />
|
||||
<div className='text-sm font-semibold'>{t('User Analytics')}</div>
|
||||
</div>
|
||||
<div className='grid gap-4'>
|
||||
{USER_CHARTS.map((chart) => {
|
||||
const spec = chartData[chart.specKey]
|
||||
|
||||
<div className='bg-muted/60 inline-flex h-8 rounded-md border p-0.5'>
|
||||
{CHART_TABS.map((tab) => (
|
||||
<button
|
||||
key={tab.value}
|
||||
type='button'
|
||||
onClick={() => setActiveTab(tab.value)}
|
||||
className={`rounded-[5px] px-3 text-xs font-medium transition-colors ${
|
||||
activeTab === tab.value
|
||||
? 'bg-background text-foreground shadow-sm'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{t(tab.labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div
|
||||
key={chart.value}
|
||||
className='overflow-hidden rounded-lg border'
|
||||
>
|
||||
<div className='flex w-full items-center gap-2 border-b px-4 py-3 sm:px-5'>
|
||||
<Users className='text-muted-foreground/60 size-4' />
|
||||
<div className='text-sm font-semibold'>{t(chart.labelKey)}</div>
|
||||
</div>
|
||||
|
||||
<div className='h-96 p-2'>
|
||||
{isLoading ? (
|
||||
<Skeleton className='h-full w-full' />
|
||||
) : (
|
||||
themeReady &&
|
||||
spec && (
|
||||
<VChart
|
||||
key={`user-${activeTab}-${resolvedTheme}`}
|
||||
spec={{
|
||||
...spec,
|
||||
theme: resolvedTheme === 'dark' ? 'dark' : 'light',
|
||||
background: 'transparent',
|
||||
}}
|
||||
option={VCHART_OPTION}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className='h-96 p-2'>
|
||||
{isLoading ? (
|
||||
<Skeleton className='h-full w-full' />
|
||||
) : (
|
||||
themeReady &&
|
||||
spec && (
|
||||
<VChart
|
||||
key={`user-${chart.value}-${resolvedTheme}`}
|
||||
spec={{
|
||||
...spec,
|
||||
theme: resolvedTheme === 'dark' ? 'dark' : 'light',
|
||||
background: 'transparent',
|
||||
}}
|
||||
option={VCHART_OPTION}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user