111 lines
4.6 KiB
TypeScript
111 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import {
|
|
ArrowLeft,
|
|
Bot,
|
|
Clock3,
|
|
Coins,
|
|
GitBranch,
|
|
MessagesSquare,
|
|
} from "lucide-react";
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { type ClaudeSession } from "@/lib/api/claude-code";
|
|
import { formatTimestamp, truncateText } from "@/lib/formatters";
|
|
|
|
type SessionHeroHeaderProps = {
|
|
session: ClaudeSession;
|
|
};
|
|
|
|
function formatCost(value: number) {
|
|
return new Intl.NumberFormat(undefined, {
|
|
style: "currency",
|
|
currency: "USD",
|
|
maximumFractionDigits: value < 1 ? 4 : 2,
|
|
}).format(value);
|
|
}
|
|
|
|
export function SessionHeroHeader({ session }: SessionHeroHeaderProps) {
|
|
const title = session.title?.trim() || truncateText(session.session_id, 18);
|
|
const model = session.models[0] ?? "Model unavailable";
|
|
|
|
return (
|
|
<section className="relative overflow-hidden border-b border-[color:var(--border)] bg-[color:var(--surface)]">
|
|
<div className="absolute inset-x-0 top-0 h-1 bg-[linear-gradient(90deg,#06b6d4,#8b5cf6,#22c55e)]" />
|
|
<div className="px-4 py-5 md:px-8 md:py-7">
|
|
<div className="flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
|
|
<div className="min-w-0">
|
|
<Link href="/claude-code">
|
|
<Button variant="ghost" size="sm" className="mb-4 px-2">
|
|
<ArrowLeft className="h-4 w-4" />
|
|
Back to Claude Code
|
|
</Button>
|
|
</Link>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Badge variant={session.is_active ? "success" : "outline"}>
|
|
{session.is_active ? "Active" : "Complete"}
|
|
</Badge>
|
|
{session.git_branch ? (
|
|
<Badge variant="outline" className="normal-case tracking-normal">
|
|
<GitBranch className="mr-1 h-3 w-3" />
|
|
{session.git_branch}
|
|
</Badge>
|
|
) : null}
|
|
<Badge variant="accent" className="normal-case tracking-normal">
|
|
{model}
|
|
</Badge>
|
|
</div>
|
|
<h1 className="mt-4 max-w-4xl break-words font-heading text-3xl font-semibold tracking-tight text-[color:var(--text)] md:text-4xl">
|
|
{title}
|
|
</h1>
|
|
<p className="mt-2 max-w-4xl break-words text-sm text-[color:var(--text-muted)]">
|
|
{session.cwd ?? session.project_dir}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid min-w-0 grid-cols-2 gap-3 sm:grid-cols-4 lg:min-w-[520px]">
|
|
<div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--bg)] p-3">
|
|
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--text-muted)]">
|
|
<Coins className="h-3.5 w-3.5" />
|
|
Cost
|
|
</div>
|
|
<p className="mt-2 truncate text-lg font-semibold text-[color:var(--text)]">
|
|
{formatCost(session.cost_usd)}
|
|
</p>
|
|
</div>
|
|
<div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--bg)] p-3">
|
|
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--text-muted)]">
|
|
<Bot className="h-3.5 w-3.5" />
|
|
Tokens
|
|
</div>
|
|
<p className="mt-2 truncate text-lg font-semibold text-[color:var(--text)]">
|
|
{session.tokens.total.toLocaleString()}
|
|
</p>
|
|
</div>
|
|
<div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--bg)] p-3">
|
|
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--text-muted)]">
|
|
<MessagesSquare className="h-3.5 w-3.5" />
|
|
Turns
|
|
</div>
|
|
<p className="mt-2 truncate text-lg font-semibold text-[color:var(--text)]">
|
|
{session.message_count.toLocaleString()}
|
|
</p>
|
|
</div>
|
|
<div className="rounded-xl border border-[color:var(--border)] bg-[color:var(--bg)] p-3">
|
|
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-[color:var(--text-muted)]">
|
|
<Clock3 className="h-3.5 w-3.5" />
|
|
Last seen
|
|
</div>
|
|
<p className="mt-2 truncate text-sm font-semibold text-[color:var(--text)]">
|
|
{formatTimestamp(session.last_message_at)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|