All files / features / runs RunDetailPanels.tsx

0% Statements 0/12
0% Branches 0/16
0% Functions 0/8
0% Lines 0/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123                                                                                                                                                                                                                                                     
import { useState } from 'react'
import { ChevronDown, ChevronRight, Copy } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { TokenDisplay } from '@/shared/components/TokenDisplay'
import { toast } from 'sonner'
 
export interface RunError {
  message: string
  code?: string
  step?: string
  details?: string
}
 
interface ErrorPanelProps {
  error: RunError
}
 
export function ErrorPanel({ error }: ErrorPanelProps) {
  const [showTechDetails, setShowTechDetails] = useState(false)
  return (
    <Card className="border-l-4 border-l-red-500" data-testid="run-error-panel">
      <CardContent className="p-4">
        <h3 className="mb-2 font-semibold text-destructive">Error</h3>
        <p className="text-sm">{error.message}</p>
        {error.code && (
          <p className="mt-1 text-xs text-muted-foreground">Code: {error.code}</p>
        )}
        {error.step && (
          <p className="mt-1 text-xs text-muted-foreground">Step: {error.step}</p>
        )}
        {error.details && (
          <div className="mt-2">
            <button
              type="button"
              onClick={() => setShowTechDetails((v) => !v)}
              className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
              data-testid="toggle-error-details"
            >
              {showTechDetails ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />}
              {showTechDetails ? 'Hide Technical Details' : 'Show Technical Details'}
            </button>
            {showTechDetails && (
              <pre className="mt-2 overflow-auto rounded bg-muted p-2 text-xs" data-testid="error-details">
                {error.details}
              </pre>
            )}
          </div>
        )}
      </CardContent>
    </Card>
  )
}
 
interface OutputPanelProps {
  output: string
}
 
export function OutputPanel({ output }: OutputPanelProps) {
  return (
    <Card className="border-l-4 border-l-green-500" data-testid="run-output-panel">
      <CardContent className="p-4">
        <h3 className="mb-2 font-semibold text-green-600">Output</h3>
        <pre className="overflow-auto whitespace-pre-wrap rounded bg-muted p-3 text-xs leading-relaxed" data-testid="run-output">
          {output}
        </pre>
      </CardContent>
    </Card>
  )
}
 
interface TokenSummaryCardsProps {
  tokenInput: number | null | undefined
  tokenOutput: number | null | undefined
}
 
export function TokenSummaryCards({ tokenInput, tokenOutput }: TokenSummaryCardsProps) {
  const input = tokenInput ?? 0
  const output = tokenOutput ?? 0
  const total = input + output
  return (
    <div className="grid grid-cols-1 gap-4 md:grid-cols-3" data-testid="run-token-summary">
      <Card>
        <CardContent className="flex flex-col gap-1 p-4">
          <span className="text-xs text-muted-foreground">Token Input</span>
          <TokenDisplay value={input} />
        </CardContent>
      </Card>
      <Card>
        <CardContent className="flex flex-col gap-1 p-4">
          <span className="text-xs text-muted-foreground">Token Output</span>
          <TokenDisplay value={output} />
        </CardContent>
      </Card>
      <Card>
        <CardContent className="flex flex-col gap-1 p-4">
          <span className="text-xs text-muted-foreground">Total</span>
          <TokenDisplay value={total} />
        </CardContent>
      </Card>
    </div>
  )
}
 
export function CopyRunIdButton({ runId }: { runId: string }) {
  return (
    <Button
      variant="ghost"
      size="icon"
      type="button"
      aria-label="Copy Run ID"
      data-testid="copy-run-id"
      onClick={() => {
        void navigator.clipboard.writeText(runId).then(() => {
          toast.success('Run ID copied')
        })
      }}
    >
      <Copy className="size-4" />
    </Button>
  )
}