Files
aiops/frontend/src/components/automation/AnsiblePlaybookCard.tsx
T

82 lines
3.4 KiB
TypeScript

// Ansible Playbook 卡片(复选框 + 名称 + 分类徽章 + 描述 + 标签 + 元信息 + 5 行操作按钮) // Logan.Li
import React from 'react';
import { FileCode, Eye, Edit, Copy, Trash2, Play, Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Checkbox } from '@/components/ui/checkbox';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { categories, type Playbook } from './ansible-helpers';
interface AnsiblePlaybookCardProps {
playbook: Playbook;
selected: boolean;
executing: boolean;
onToggleSelect: (id: number) => void;
onView: (pb: Playbook) => void;
onExecute: (pb: Playbook) => void;
onEdit: (pb: Playbook) => void;
onClone: (pb: Playbook) => void;
onDelete: (pb: Playbook) => void;
}
const AnsiblePlaybookCard: React.FC<AnsiblePlaybookCardProps> = ({
playbook, selected, executing,
onToggleSelect, onView, onExecute, onEdit, onClone, onDelete,
}) => {
return (
<Card className="hover:shadow-md transition-shadow">
<CardHeader className="pb-3">
<div className="flex items-start justify-between gap-2">
<div className="flex items-center gap-2 min-w-0">
<Checkbox
checked={selected}
onCheckedChange={() => onToggleSelect(playbook.id)}
className="shrink-0"
/>
<FileCode className="w-5 h-5 text-blue-500 shrink-0" />
<CardTitle className="text-base truncate">{playbook.name}</CardTitle>
</div>
<Badge variant="outline" className="shrink-0">
{categories.find(c => c.value === playbook.category)?.label || playbook.category}
</Badge>
</div>
<CardDescription className="line-clamp-2">{playbook.description || '暂无描述'}</CardDescription>
</CardHeader>
<CardContent className="pt-0">
{playbook.tags.length > 0 && (
<div className="flex flex-wrap gap-1 mb-3">
{playbook.tags.map((tag, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{tag}</Badge>
))}
</div>
)}
<div className="flex items-center justify-between text-xs text-muted-foreground mb-3">
<span>执行 {playbook.run_count} 次</span>
<span>超时 {playbook.timeout}s</span>
</div>
<div className="flex items-center gap-1">
<Button variant="ghost" size="sm" title="查看内容" onClick={() => onView(playbook)}>
<Eye className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm" title="执行" disabled={executing} onClick={() => onExecute(playbook)}>
{executing
? <Loader2 className="w-4 h-4 animate-spin text-blue-600" />
: <Play className="w-4 h-4 text-green-600" />}
</Button>
<Button variant="ghost" size="sm" title="编辑" onClick={() => onEdit(playbook)}>
<Edit className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm" title="复制" onClick={() => onClone(playbook)}>
<Copy className="w-4 h-4" />
</Button>
<Button variant="ghost" size="sm" title="删除" onClick={() => onDelete(playbook)}>
<Trash2 className="w-4 h-4 text-red-600" />
</Button>
</div>
</CardContent>
</Card>
);
};
export default AnsiblePlaybookCard;