gflow 0.4.18

A lightweight, single-node job scheduler written in Rust.
import type { UsageStats } from "@/api"
import { formatSeconds } from "@/lib/format"
import { cn } from "@/lib/utils"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"
import { EmptyRow } from "@/components/dashboard/StatePanels"

export function StatsView({ stats }: { stats: UsageStats }) {
  const maxCount = Math.max(
    stats.completed_jobs,
    stats.failed_jobs,
    stats.cancelled_jobs,
    stats.timeout_jobs,
    stats.running_jobs,
    stats.queued_jobs,
    1,
  )
  const jobMix = [
    ["Completed", stats.completed_jobs, "bg-emerald-500"],
    ["Failed", stats.failed_jobs, "bg-rose-500"],
    ["Cancelled", stats.cancelled_jobs, "bg-amber-500"],
    ["Timeout", stats.timeout_jobs, "bg-orange-500"],
    ["Running", stats.running_jobs, "bg-sky-500"],
    ["Queued", stats.queued_jobs, "bg-zinc-500"],
  ] as const

  return (
    <div className="grid gap-4 lg:grid-cols-[1fr_380px]">
      <Card className="rounded-lg">
        <CardHeader className="border-b">
          <CardTitle>Job Mix</CardTitle>
          <CardDescription>{stats.total_jobs} jobs included</CardDescription>
        </CardHeader>
        <CardContent className="space-y-3">
          {jobMix.map(([label, value, color]) => (
            <div key={label} className="grid grid-cols-[100px_1fr_48px] items-center gap-3">
              <div className="text-sm text-muted-foreground">{label}</div>
              <div className="h-2 overflow-hidden rounded-full bg-muted">
                <div
                  className={cn("h-full rounded-full", color)}
                  style={{ width: `${(Number(value) / maxCount) * 100}%` }}
                />
              </div>
              <div className="text-right font-mono text-xs">{value}</div>
            </div>
          ))}
        </CardContent>
      </Card>

      <Card className="rounded-lg">
        <CardHeader className="border-b">
          <CardTitle>Runtime</CardTitle>
          <CardDescription>Aggregate timing</CardDescription>
        </CardHeader>
        <CardContent className="space-y-4">
          <StatLine label="Average wait" value={formatSeconds(stats.avg_wait_secs)} />
          <StatLine
            label="Average runtime"
            value={formatSeconds(stats.avg_runtime_secs)}
          />
          <StatLine label="Average GPUs/job" value={stats.avg_gpus_per_job.toFixed(2)} />
          <StatLine label="Peak GPU request" value={stats.peak_gpu_usage} />
        </CardContent>
      </Card>

      <Card className="rounded-lg lg:col-span-2">
        <CardHeader className="border-b">
          <CardTitle>Top Runtime Jobs</CardTitle>
          <CardDescription>{stats.top_jobs.length} longest completed runs</CardDescription>
        </CardHeader>
        <CardContent>
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead>ID</TableHead>
                <TableHead>Name</TableHead>
                <TableHead>Runtime</TableHead>
                <TableHead>GPUs</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {stats.top_jobs.length ? (
                stats.top_jobs.map((job) => (
                  <TableRow key={job.id}>
                    <TableCell className="font-mono text-xs">{job.id}</TableCell>
                    <TableCell>{job.name ?? "unnamed"}</TableCell>
                    <TableCell>{formatSeconds(job.runtime_secs)}</TableCell>
                    <TableCell>{job.gpus}</TableCell>
                  </TableRow>
                ))
              ) : (
                <EmptyRow columns={4} label="No runtime data" />
              )}
            </TableBody>
          </Table>
        </CardContent>
      </Card>
    </div>
  )
}

function StatLine({ label, value }: { label: string; value: string | number }) {
  return (
    <div className="flex items-center justify-between gap-4 border-b pb-3 last:border-b-0 last:pb-0">
      <span className="text-sm text-muted-foreground">{label}</span>
      <span className="font-mono text-sm">{value}</span>
    </div>
  )
}