/** * AutomationCard - Unified Card for Hands and Workflows * * Displays automation items with status, parameters, and actions. * Supports both grid and list view modes. * * @module components/Automation/AutomationCard */ import { useState, useCallback } from 'react'; import type { AutomationItem, AutomationStatus } from '../../types/automation'; import { CATEGORY_CONFIGS } from '../../types/automation'; import type { HandParameter } from '../../types/hands'; import { HandParamsForm } from '../HandParamsForm'; import { Zap, Clock, CheckCircle, XCircle, AlertTriangle, Loader2, Settings, Play, MoreVertical, } from 'lucide-react'; // === Status Config === const STATUS_CONFIG: Record = { idle: { label: '就绪', className: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', dotClass: 'bg-green-500', }, running: { label: '运行中', className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400', dotClass: 'bg-blue-500 animate-pulse', icon: Loader2, }, needs_approval: { label: '待审批', className: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400', dotClass: 'bg-yellow-500', icon: AlertTriangle, }, error: { label: '错误', className: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', dotClass: 'bg-red-500', icon: XCircle, }, unavailable: { label: '不可用', className: 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400', dotClass: 'bg-gray-400', }, setup_needed: { label: '需配置', className: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400', dotClass: 'bg-orange-500', icon: Settings, }, completed: { label: '已完成', className: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', dotClass: 'bg-green-500', icon: CheckCircle, }, paused: { label: '已暂停', className: 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400', dotClass: 'bg-gray-400', }, }; // === Component Props === interface AutomationCardProps { item: AutomationItem; viewMode?: 'grid' | 'list'; isSelected?: boolean; isExecuting?: boolean; onSelect?: (selected: boolean) => void; onExecute?: (params?: Record) => void; onClick?: () => void; } // === Status Badge Component === function StatusBadge({ status }: { status: AutomationStatus }) { const config = STATUS_CONFIG[status] || STATUS_CONFIG.unavailable; const Icon = config.icon; return ( {Icon ? ( ) : ( )} {config.label} ); } // === Type Badge Component === function TypeBadge({ type }: { type: 'hand' | 'workflow' }) { const isHand = type === 'hand'; return ( {isHand ? '自主能力' : '工作流'} ); } // === Category Badge Component === function CategoryBadge({ category }: { category: string }) { const config = CATEGORY_CONFIGS[category as keyof typeof CATEGORY_CONFIGS]; if (!config) return null; return ( {config.label} ); } // === Main Component === export function AutomationCard({ item, viewMode = 'grid', isSelected = false, isExecuting = false, onSelect, onExecute, onClick, }: AutomationCardProps) { const [showParams, setShowParams] = useState(false); const [paramValues, setParamValues] = useState>({}); const [paramErrors, setParamErrors] = useState>({}); const hasParameters = item.parameters && item.parameters.length > 0; const canActivate = item.status === 'idle' || item.status === 'setup_needed'; // Initialize default parameter values const initializeDefaults = useCallback(() => { if (item.parameters) { const defaults: Record = {}; item.parameters.forEach(p => { if (p.defaultValue !== undefined) { defaults[p.name] = p.defaultValue; } }); setParamValues(defaults); } }, [item.parameters]); // Handle execute click const handleExecuteClick = useCallback(() => { if (hasParameters && !showParams) { initializeDefaults(); setShowParams(true); return; } // Validate parameters if (showParams && item.parameters) { const errors: Record = {}; item.parameters.forEach(param => { if (param.required) { const value = paramValues[param.name]; if (value === undefined || value === null || value === '') { errors[param.name] = `${param.label} is required`; } } }); if (Object.keys(errors).length > 0) { setParamErrors(errors); return; } } onExecute?.(showParams ? paramValues : undefined); setShowParams(false); setParamErrors({}); }, [hasParameters, showParams, initializeDefaults, item.parameters, paramValues, onExecute]); // Handle checkbox change const handleCheckboxChange = useCallback((e: React.ChangeEvent) => { e.stopPropagation(); onSelect?.(e.target.checked); }, [onSelect]); // Get icon for item const getItemIcon = () => { if (item.icon) { // Map string icon names to components const iconMap: Record = { Video: '🎬', UserPlus: '👤', Database: '🗄️', TrendingUp: '📈', Search: '🔍', Twitter: '🐦', Globe: '🌐', Zap: '⚡', }; return iconMap[item.icon] || '🤖'; } return item.type === 'hand' ? '🤖' : '📋'; }; if (viewMode === 'list') { return (
{/* Checkbox */} e.stopPropagation()} /> {/* Icon */} {getItemIcon()} {/* Info */}

{item.name}

{item.description}

{/* Status */} {/* Actions */}
); } // Grid view return (
{/* Selection checkbox */}
e.stopPropagation()} />
{/* Content */}
{/* Header */}
{getItemIcon()}

{item.name}

{/* Description */}

{item.description}

{/* Meta */}
{item.schedule?.enabled && ( 已调度 )}
{/* Parameters Form (shown when activating) */} {showParams && item.parameters && item.parameters.length > 0 && (
)} {/* Actions */}
{/* Schedule indicator */} {item.schedule?.nextRun && (
下次运行: {new Date(item.schedule.nextRun).toLocaleString('zh-CN')}
)}
); } export default AutomationCard;