({ benchmark, onDelete, onRun, isRunning }: BenchmarkCardProps)
| 153 | } |
| 154 | |
| 155 | function BenchmarkCard({ benchmark, onDelete, onRun, isRunning }: BenchmarkCardProps) { |
| 156 | const [showMenu, setShowMenu] = useState(false); |
| 157 | |
| 158 | const summary = benchmark.summary; |
| 159 | const hasResults = summary && summary.total_runs > 0; |
| 160 | |
| 161 | const successRate = hasResults |
| 162 | ? (summary.successful_runs / summary.total_runs) * 100 |
| 163 | : 0; |
| 164 | |
| 165 | const successRateColor = successRate >= 80 |
| 166 | ? 'text-[var(--color-success)]' |
| 167 | : successRate >= 50 |
| 168 | ? 'text-[var(--color-warning)]' |
| 169 | : 'text-[var(--color-error)]'; |
| 170 | |
| 171 | return ( |
| 172 | <div className="bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 hover:border-[var(--color-primary)]/50 transition-colors"> |
| 173 | <div className="flex items-start justify-between"> |
| 174 | {/* Left */} |
| 175 | <div className="flex items-start gap-4 flex-1 min-w-0"> |
| 176 | {/* Icon */} |
| 177 | <div className="w-12 h-12 rounded-lg bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-secondary)] flex items-center justify-center flex-shrink-0"> |
| 178 | <Trophy className="w-6 h-6 text-white" /> |
| 179 | </div> |
| 180 | |
| 181 | {/* Info */} |
| 182 | <div className="flex-1 min-w-0"> |
| 183 | <h3 className="font-semibold text-[var(--color-text-primary)] truncate text-lg"> |
| 184 | {benchmark.name} |
| 185 | </h3> |
| 186 | <p className="text-xs text-[var(--color-text-muted)] font-mono mt-1"> |
| 187 | ID: {benchmark.id.slice(0, 16)}... |
| 188 | </p> |
| 189 | <p className="text-sm text-[var(--color-text-muted)] mt-1 flex items-center gap-1"> |
| 190 | <Clock className="w-3 h-3" /> |
| 191 | 创建于 {formatRelativeTime(benchmark.created_at)} |
| 192 | </p> |
| 193 | </div> |
| 194 | </div> |
| 195 | |
| 196 | {/* Right - Actions */} |
| 197 | <div className="flex items-center gap-2"> |
| 198 | <button |
| 199 | onClick={() => onRun(benchmark)} |
| 200 | disabled={isRunning} |
| 201 | className="flex items-center gap-2 px-3 py-1.5 bg-[var(--color-success)]/10 text-[var(--color-success)] rounded-lg text-sm font-medium hover:bg-[var(--color-success)]/20 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" |
| 202 | > |
| 203 | {isRunning ? ( |
| 204 | <Loader2 className="w-4 h-4 animate-spin" /> |
| 205 | ) : ( |
| 206 | <Play className="w-4 h-4" /> |
| 207 | )} |
| 208 | 运行 |
| 209 | </button> |
| 210 | |
| 211 | <div className="relative"> |
| 212 | <button |
nothing calls this directly
no test coverage detected