mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-17 00:24:57 +00:00
fix(analytics): tighten top-bar layout (single-line trigger, grouped cluster, compact timestamp)
Three design taste fixes that ride on top of the disclaimer collapse: - Single-line profile trigger: render only `option.label` in the Select trigger; keep label+description in the open menu where the description earns its keep. Removes the two-line trigger that broke the row baseline (taller pill, vertical-center cascade). - Group Select + Info icon in an `inline-flex … shrink-0` cluster so they wrap as a single unit and the icon never orphans onto a second row under the refresh button. - Compact timestamp: "Updated less than a minute ago" → "1m ago" rendered in font-mono. Full phrase preserved via `title` attribute. Drops the localized prefix into a tooltip-only context. - Trigger width trimmed 190px → 170px since it no longer needs to fit a description line.
This commit is contained in:
@@ -54,10 +54,12 @@ export function AnalyticsHeader({
|
|||||||
<p className="text-sm text-muted-foreground">{t('analytics.subtitle')}</p>
|
<p className="text-sm text-muted-foreground">{t('analytics.subtitle')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-2 xl:justify-end">
|
<div className="flex flex-wrap items-center gap-2 xl:justify-end">
|
||||||
<div className="flex items-center gap-1">
|
<div className="inline-flex items-center gap-1 shrink-0">
|
||||||
<Select value={selectedProfile} onValueChange={onProfileChange}>
|
<Select value={selectedProfile} onValueChange={onProfileChange}>
|
||||||
<SelectTrigger className="h-8 w-[190px]" aria-label="Analytics profile">
|
<SelectTrigger className="h-8 w-[170px]" aria-label="Analytics profile">
|
||||||
<SelectValue placeholder="All profiles" />
|
<SelectValue placeholder="All profiles">
|
||||||
|
{profileOptions.find((o) => o.value === selectedProfile)?.label ?? 'All profiles'}
|
||||||
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{profileOptions.map((option) => (
|
{profileOptions.map((option) => (
|
||||||
@@ -114,8 +116,11 @@ export function AnalyticsHeader({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{lastUpdatedText && (
|
{lastUpdatedText && (
|
||||||
<span className="text-xs text-muted-foreground whitespace-nowrap">
|
<span
|
||||||
{t('analytics.updated', { value: lastUpdatedText })}
|
className="text-xs text-muted-foreground whitespace-nowrap font-mono"
|
||||||
|
title={t('analytics.updated', { value: lastUpdatedText })}
|
||||||
|
>
|
||||||
|
{lastUpdatedText}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -6,7 +6,18 @@
|
|||||||
|
|
||||||
import { useState, useMemo, useCallback } from 'react';
|
import { useState, useMemo, useCallback } from 'react';
|
||||||
import type { DateRange } from 'react-day-picker';
|
import type { DateRange } from 'react-day-picker';
|
||||||
import { subDays, formatDistanceToNow } from 'date-fns';
|
import { subDays } from 'date-fns';
|
||||||
|
|
||||||
|
function formatCompactRelativeTime(date: Date): string {
|
||||||
|
const seconds = Math.max(1, Math.floor((Date.now() - date.getTime()) / 1000));
|
||||||
|
if (seconds < 60) return `${seconds}s ago`;
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
if (minutes < 60) return `${minutes}m ago`;
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
if (hours < 24) return `${hours}h ago`;
|
||||||
|
const days = Math.floor(hours / 24);
|
||||||
|
return `${days}d ago`;
|
||||||
|
}
|
||||||
import {
|
import {
|
||||||
useUsageSummary,
|
useUsageSummary,
|
||||||
useUsageTrends,
|
useUsageTrends,
|
||||||
@@ -151,10 +162,10 @@ export function useAnalyticsPage() {
|
|||||||
persistSelectedProfile(profile);
|
persistSelectedProfile(profile);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Format "Last updated" text
|
// Compact "Last updated" text (e.g. "1m ago", "2h ago")
|
||||||
const lastUpdatedText = useMemo(() => {
|
const lastUpdatedText = useMemo(() => {
|
||||||
if (!status?.lastFetch) return null;
|
if (!status?.lastFetch) return null;
|
||||||
return formatDistanceToNow(new Date(status.lastFetch), { addSuffix: true });
|
return formatCompactRelativeTime(new Date(status.lastFetch));
|
||||||
}, [status?.lastFetch]);
|
}, [status?.lastFetch]);
|
||||||
|
|
||||||
// Handle model click for popover
|
// Handle model click for popover
|
||||||
|
|||||||
Reference in New Issue
Block a user