fix(panel): compact task-status tiles beside the donut; journal ids copyable + task-linked

Five near-empty stat cards become a 2x3 tile band sharing the row with
the status donut. Journal entry cards and the entry page render full
ids with the shared CopyButton and a /tasks quick-link badge, with the
entry-card anchor restructured so links no longer nest.
This commit is contained in:
Renn F
2026-07-15 16:03:11 +02:00
parent 614c22ed70
commit 2efc2f9542
8 changed files with 318 additions and 69 deletions
@@ -0,0 +1,37 @@
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { TaskStatusTiles } from "../task-status-tiles";
describe("TaskStatusTiles", () => {
it("renders a tile per status with its label and value", () => {
render(
<TaskStatusTiles
tiles={[
{ label: "Pending", value: 3, icon: <span>icon</span> },
{ label: "Completed", value: 12, icon: <span>icon</span> },
]}
/>,
);
expect(screen.getByText("Pending")).toBeInTheDocument();
expect(screen.getByText("3")).toBeInTheDocument();
expect(screen.getByText("Completed")).toBeInTheDocument();
expect(screen.getByText("12")).toBeInTheDocument();
});
it("renders one tile for every entry passed", () => {
render(
<TaskStatusTiles
tiles={[
{ label: "A", value: 1, icon: <span>icon</span> },
{ label: "B", value: 2, icon: <span>icon</span> },
{ label: "C", value: 3, icon: <span>icon</span> },
{ label: "D", value: 4, icon: <span>icon</span> },
{ label: "E", value: 5, icon: <span>icon</span> },
]}
/>,
);
["A", "B", "C", "D", "E"].forEach((label) => {
expect(screen.getByText(label)).toBeInTheDocument();
});
});
});
+2
View File
@@ -3,4 +3,6 @@ export { ModelUsageDonut } from "./model-usage-donut";
export { AgentUsageChart } from "./agent-usage-chart";
export { TeamUsageChart } from "./team-usage-chart";
export { TaskStatusChart } from "./task-status-chart";
export { TaskStatusTiles } from "./task-status-tiles";
export type { TaskStatusTileData } from "./task-status-tiles";
export { SessionsTable } from "./sessions-table";
@@ -0,0 +1,36 @@
"use client";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
export interface TaskStatusTileData {
label: string;
value: number;
icon: React.ReactNode;
}
interface TaskStatusTilesProps {
tiles: TaskStatusTileData[];
className?: string;
}
/**
* Compact grid of task-status counts (label + number + status-colored icon
* per tile) — replaces five full-size stat cards that sat mostly empty next
* to the status donut (CEO feedback: "much smaller cards 2x3 or something").
*/
export function TaskStatusTiles({ tiles, className }: TaskStatusTilesProps) {
return (
<div className={cn("grid grid-cols-2 sm:grid-cols-3 gap-2", className)}>
{tiles.map((tile) => (
<Card key={tile.label} className="gap-1 py-3">
<div className="flex items-center gap-1.5 px-3 text-xs text-muted-foreground">
{tile.icon}
<span className="truncate">{tile.label}</span>
</div>
<div className="px-3 text-xl font-bold">{tile.value}</div>
</Card>
))}
</div>
);
}