mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +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>
|
||||
</div>
|
||||
<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}>
|
||||
<SelectTrigger className="h-8 w-[190px]" aria-label="Analytics profile">
|
||||
<SelectValue placeholder="All profiles" />
|
||||
<SelectTrigger className="h-8 w-[170px]" aria-label="Analytics profile">
|
||||
<SelectValue placeholder="All profiles">
|
||||
{profileOptions.find((o) => o.value === selectedProfile)?.label ?? 'All profiles'}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{profileOptions.map((option) => (
|
||||
@@ -114,8 +116,11 @@ export function AnalyticsHeader({
|
||||
/>
|
||||
|
||||
{lastUpdatedText && (
|
||||
<span className="text-xs text-muted-foreground whitespace-nowrap">
|
||||
{t('analytics.updated', { value: lastUpdatedText })}
|
||||
<span
|
||||
className="text-xs text-muted-foreground whitespace-nowrap font-mono"
|
||||
title={t('analytics.updated', { value: lastUpdatedText })}
|
||||
>
|
||||
{lastUpdatedText}
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
|
||||
@@ -6,7 +6,18 @@
|
||||
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
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 {
|
||||
useUsageSummary,
|
||||
useUsageTrends,
|
||||
@@ -151,10 +162,10 @@ export function useAnalyticsPage() {
|
||||
persistSelectedProfile(profile);
|
||||
}, []);
|
||||
|
||||
// Format "Last updated" text
|
||||
// Compact "Last updated" text (e.g. "1m ago", "2h ago")
|
||||
const lastUpdatedText = useMemo(() => {
|
||||
if (!status?.lastFetch) return null;
|
||||
return formatDistanceToNow(new Date(status.lastFetch), { addSuffix: true });
|
||||
return formatCompactRelativeTime(new Date(status.lastFetch));
|
||||
}, [status?.lastFetch]);
|
||||
|
||||
// Handle model click for popover
|
||||
|
||||
Reference in New Issue
Block a user