harn-cli 0.5.83

CLI for the Harn programming language — run, test, REPL, format, and lint
import { NavLink } from "react-router-dom"
import { defineMessages, useIntl } from "react-intl"

import { formatDuration } from "../lib/format"
import type { PortalStats } from "../types"

const messages = defineMessages({
  title: {
    id: "portal.sidebar.title",
    defaultMessage: "Portal",
  },
  intro: {
    id: "portal.sidebar.intro",
    defaultMessage: "Local observability and launch control for persisted Harn workflows.",
  },
  launch: {
    id: "portal.sidebar.launch",
    defaultMessage: "Launch",
  },
  runs: {
    id: "portal.sidebar.runs",
    defaultMessage: "Runs",
  },
  refreshNow: {
    id: "portal.sidebar.refreshNow",
    defaultMessage: "Refresh stats",
  },
  waiting: {
    id: "portal.sidebar.waiting",
    defaultMessage: "Waiting for first refresh…",
  },
  refreshing: {
    id: "portal.sidebar.refreshing",
    defaultMessage: "Refreshing…",
  },
  lastRefresh: {
    id: "portal.sidebar.lastRefresh",
    defaultMessage: "Last refresh {time}",
  },
  error: {
    id: "portal.sidebar.error",
    defaultMessage: "Error: {message}",
  },
  runsStat: { id: "portal.sidebar.runsStat", defaultMessage: "Runs" },
  complete: { id: "portal.sidebar.complete", defaultMessage: "Complete" },
  active: { id: "portal.sidebar.active", defaultMessage: "Active" },
  failed: { id: "portal.sidebar.failed", defaultMessage: "Failed" },
  avgRun: { id: "portal.sidebar.avgRun", defaultMessage: "Avg run" },
})

type SidebarProps = {
  stats: PortalStats | null
  loading: boolean
  lastRefreshAt: number | null
  lastError: string | null
  onRefresh: () => void
}

export function Sidebar({ stats, loading, lastRefreshAt, lastError, onRefresh }: SidebarProps) {
  const intl = useIntl()

  const statCards = stats
    ? [
        [intl.formatMessage(messages.runsStat), stats.total_runs],
        [intl.formatMessage(messages.complete), stats.completed_runs],
        [intl.formatMessage(messages.active), stats.active_runs],
        [intl.formatMessage(messages.failed), stats.failed_runs],
        [intl.formatMessage(messages.avgRun), formatDuration(stats.avg_duration_ms)],
      ]
    : []

  const statusCopy = loading
    ? intl.formatMessage(messages.refreshing)
    : lastRefreshAt
      ? intl.formatMessage(messages.lastRefresh, {
          time: new Date(lastRefreshAt).toLocaleTimeString([], {
            hour: "numeric",
            minute: "2-digit",
          }),
        })
      : intl.formatMessage(messages.waiting)

  return (
    <aside className="sidebar">
      <div className="sidebar-header">
        <div className="eyebrow">Harn</div>
        <h1>{intl.formatMessage(messages.title)}</h1>
        <p>{intl.formatMessage(messages.intro)}</p>
      </div>

      <nav className="sidebar-nav" aria-label="Portal navigation">
        <NavLink className={({ isActive }) => `nav-link ${isActive ? "active" : ""}`} to="/launch">
          {intl.formatMessage(messages.launch)}
        </NavLink>
        <NavLink className={({ isActive }) => `nav-link ${isActive ? "active" : ""}`} to="/runs">
          {intl.formatMessage(messages.runs)}
        </NavLink>
      </nav>

      <div className="stats-grid">
        {statCards.map(([label, value]) => (
          <div className="card" key={label}>
            <div className="eyebrow">{label}</div>
            <div className="value">{value}</div>
          </div>
        ))}
      </div>

      <div className="sidebar-tools">
        <button className="action-button" disabled={loading} onClick={onRefresh} type="button">
          {intl.formatMessage(messages.refreshNow)}
        </button>
        <div className="muted">{statusCopy}</div>
        {lastError ? <div className="muted">{intl.formatMessage(messages.error, { message: lastError })}</div> : null}
      </div>
    </aside>
  )
}