mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(panel): dense tooltip pass — org surfaces + layout chrome (253 tips)
Every sidebar destination, metric derivation, sortable column, queue action, and dialog control explains itself; mobile tab bar shares the sidebar's descriptions so the two nav surfaces can't drift.
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from "recharts";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { HelpTip } from "@/components/ui/help-tip";
|
||||
import { useIsMobile } from "@/hooks/use-is-mobile";
|
||||
import type { ModelUsageSlice } from "@/types";
|
||||
|
||||
@@ -39,7 +40,9 @@ export function ModelUsageDonut({ data, isLoading }: ModelUsageDonutProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">By Model</CardTitle>
|
||||
<HelpTip label="Share of total tokens consumed per model in the selected window — hover a slice for exact tokens and cost">
|
||||
<CardTitle className="text-base">By Model</CardTitle>
|
||||
</HelpTip>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -19,6 +19,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { HelpTip } from "@/components/ui/help-tip";
|
||||
import { ChevronUp, ChevronDown } from "lucide-react";
|
||||
import type { UsageSession } from "@/types";
|
||||
|
||||
@@ -41,17 +42,42 @@ type SortDir = "asc" | "desc";
|
||||
interface Column {
|
||||
key: SortKey;
|
||||
label: string;
|
||||
tip: string;
|
||||
}
|
||||
|
||||
const COLUMNS: Column[] = [
|
||||
{ key: "agent_slug", label: "Agent" },
|
||||
{ key: "model", label: "Model" },
|
||||
{ key: "started_at", label: "Started" },
|
||||
{ key: "total_tokens", label: "Total" },
|
||||
{ key: "tokens_input", label: "Input" },
|
||||
{ key: "tokens_output", label: "Output" },
|
||||
{ key: "tokens_cache", label: "Cache" },
|
||||
{ key: "cost", label: "Cost" },
|
||||
{ key: "agent_slug", label: "Agent", tip: "The agent slug that ran this session — click to sort" },
|
||||
{ key: "model", label: "Model", tip: "Claude/Grok model used for this session — click to sort" },
|
||||
{
|
||||
key: "started_at",
|
||||
label: "Started",
|
||||
tip: "Session start time, local timezone — click to sort oldest/newest",
|
||||
},
|
||||
{
|
||||
key: "total_tokens",
|
||||
label: "Total",
|
||||
tip: "Input + output + cache tokens combined — click to sort",
|
||||
},
|
||||
{
|
||||
key: "tokens_input",
|
||||
label: "Input",
|
||||
tip: "Prompt tokens sent to the model — click to sort",
|
||||
},
|
||||
{
|
||||
key: "tokens_output",
|
||||
label: "Output",
|
||||
tip: "Completion tokens returned by the model — click to sort",
|
||||
},
|
||||
{
|
||||
key: "tokens_cache",
|
||||
label: "Cache",
|
||||
tip: "Tokens served from Anthropic's prompt cache — click to sort",
|
||||
},
|
||||
{
|
||||
key: "cost",
|
||||
label: "Cost",
|
||||
tip: "Provider-priced dollar cost (local/Ollama sessions are $0) — click to sort",
|
||||
},
|
||||
];
|
||||
|
||||
function formatTime(ts: string): string {
|
||||
@@ -116,7 +142,9 @@ export function SessionsTable({ data, isLoading }: SessionsTableProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">Recent Sessions</CardTitle>
|
||||
<HelpTip label="The most recent agent spawn sessions with their token/cost breakdown — sortable, 10 per page">
|
||||
<CardTitle className="text-base">Recent Sessions</CardTitle>
|
||||
</HelpTip>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
@@ -139,8 +167,12 @@ export function SessionsTable({ data, isLoading }: SessionsTableProps) {
|
||||
className="cursor-pointer select-none text-xs whitespace-nowrap"
|
||||
onClick={() => toggleSort(col.key)}
|
||||
>
|
||||
{col.label}
|
||||
<SortIcon col={col.key} />
|
||||
<HelpTip label={col.tip}>
|
||||
<span>
|
||||
{col.label}
|
||||
<SortIcon col={col.key} />
|
||||
</span>
|
||||
</HelpTip>
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
@@ -239,24 +271,32 @@ export function SessionsTable({ data, isLoading }: SessionsTableProps) {
|
||||
: `${page * PAGE_SIZE + 1}–${Math.min((page + 1) * PAGE_SIZE, sorted.length)} of ${sorted.length}`}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPage((p) => Math.max(0, p - 1))}
|
||||
disabled={page === 0}
|
||||
>
|
||||
Prev
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setPage((p) => Math.min(totalPages - 1, p + 1))
|
||||
}
|
||||
disabled={page >= totalPages - 1}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<HelpTip label={`Show the previous ${PAGE_SIZE} sessions`}>
|
||||
<span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPage((p) => Math.max(0, p - 1))}
|
||||
disabled={page === 0}
|
||||
>
|
||||
Prev
|
||||
</Button>
|
||||
</span>
|
||||
</HelpTip>
|
||||
<HelpTip label={`Show the next ${PAGE_SIZE} sessions`}>
|
||||
<span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setPage((p) => Math.min(totalPages - 1, p + 1))
|
||||
}
|
||||
disabled={page >= totalPages - 1}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</span>
|
||||
</HelpTip>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "recharts";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { HelpTip } from "@/components/ui/help-tip";
|
||||
|
||||
interface TaskStatusSlice {
|
||||
name: string;
|
||||
@@ -41,7 +42,9 @@ export function TaskStatusChart({ slices, isLoading }: TaskStatusChartProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">Task Status Distribution</CardTitle>
|
||||
<HelpTip label="Live snapshot of how many tasks currently sit in each lifecycle status">
|
||||
<CardTitle className="text-base">Task Status Distribution</CardTitle>
|
||||
</HelpTip>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "recharts";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { HelpTip } from "@/components/ui/help-tip";
|
||||
import { useIsMobile } from "@/hooks/use-is-mobile";
|
||||
import type { UsageTimePoint } from "@/types";
|
||||
|
||||
@@ -51,7 +52,9 @@ export function UsageTimeSeriesChart({
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">Token Usage Over Time</CardTitle>
|
||||
<HelpTip label="Input (prompt) vs. output (completion) tokens per time bucket — hourly or daily, depending on the selected window">
|
||||
<CardTitle className="text-base">Token Usage Over Time</CardTitle>
|
||||
</HelpTip>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading ? (
|
||||
|
||||
Reference in New Issue
Block a user