import type { listAccountContainers } from "~/fns/containers"; import { Button } from "~/ui/Button"; import { ErrorBanner } from "~/ui/Callout"; import { Badge } from "~/ui/Badge"; import { EmptyState } from "~/ui/EmptyState"; import { Panel } from "~/ui/Panel"; import { RelativeTime } from "~/ui/RelativeTime"; import { cn } from "~/utils/cn"; import { TextLink } from "~/ui/TextLink"; import { ProgressTrack, quotaTone } from "~/ui/ProgressTrack"; type Overview = Awaited>; type Session = Overview["sessions"][number]; type Usage = NonNullable; const SECONDS_PER_MINUTE = 60; const FULL_PERCENT = 100; const STOP_LABELS: Record = { idle: "slept after idling", cap: "hit the session cap", quota: "hit the monthly quota", exited: "exited", restarted: "restarted", failed: "failed to start", stopped: "stopped", "stopped by admin": "stopped by an admin", }; function minutesLabel(minutes: number): string { return minutes < 1 ? "<1 min" : `${Math.round(minutes)} min`; } export function ContainerUsageBar({ usage }: { usage: Usage }) { const quota = usage.hardCapSeconds ?? usage.includedSeconds; const used = Math.round(usage.usedSeconds / SECONDS_PER_MINUTE); const percent = quota > 0 ? Math.min(FULL_PERCENT, (usage.usedSeconds / quota) * FULL_PERCENT) : 0; return (
Container minutes this month {used} / {Math.round(quota / SECONDS_PER_MINUTE)} min {usage.hardCapSeconds === null && included, overage billed}
); } function SessionRow({ session, showAccount, canStop, pending, onStop, }: { session: Session; showAccount: boolean; canStop: boolean; pending: boolean; onStop: (id: string) => void; }) { const running = session.stoppedAt === null; return (
  • {session.repoOwner && session.repoName ? ( {session.repoOwner}/{session.repoName} ) : ( deleted repository )} @ {session.branch} {showAccount && session.accountName && {session.accountName}} {running ? "running" : (STOP_LABELS[session.stopReason ?? ""] ?? "stopped")} started {running && session.lastActivity !== null && ( idle since )} {minutesLabel(session.minutes)} {running && canStop && ( )}
  • ); } type Props = { overview: Overview | Omit; showAccount?: boolean; canStop: boolean; error: string | null; pending: boolean; onStop: (sessionId: string) => void; }; /** Running and recent editor containers for one account or one repository, with Stop for admins. */ export function ContainerSessionList({ overview, showAccount = false, canStop, error, pending, onStop }: Props) { const running = overview.sessions.filter((session) => session.stoppedAt === null); const recent = overview.sessions.filter((session) => session.stoppedAt !== null); return (
    {!overview.enabled && (

    Containers aren't enabled on this forge; nothing new can start.

    )} {overview.enabled && !overview.allowed && (

    Editor containers are available on paid plans. Upgrade on the{" "} billing page to run a terminal, tasks and port previews against a branch.

    )} {overview.usage && ( )} {running.length === 0 && !overview.allowed ? ( Available on paid plans. ) : running.length === 0 ? ( Containers sleep after {overview.idleMinutes} idle minutes and stop after {overview.capHours} hours. ) : (
      {running.map((session) => ( ))}
    )}
    {recent.length === 0 ? ( ) : (
      {recent.map((session) => ( ))}
    )}
    ); }