Files
FlowStack/frontend/src/components/TaskCard.tsx
T
Logan.Li 04d1342b27 feat: FlowStack 发布系统首个可运行版本 (gin + React)
- Go gin 后端:流水线/节点/连线/运行/日志/GitOps/插件 REST API,
  SQLite(GORM) 自动建表 + seed,模拟执行引擎 + SSE 实时日志推送,
  前端产物 go:embed 嵌入单二进制,一个端口同时服务 API 与页面
- React 19 前端:画布式流水线编辑器(React Flow + Tekton 任务库)、
  GitOps 部署、批量发布、发布记录等 14 个模块页面,
  tRPC 全部替换为 REST(TanStack Query)
- 首次启动生成随机密码管理员账号(日志 + data/initial-admin-password.txt)
- run.sh 一键构建启动(默认端口 9527,支持 start/stop/restart/build/dev)
2026-10-27 02:44:10 +08:00

89 lines
3.0 KiB
TypeScript

import { Pencil, Trash2 } from 'lucide-react';
import { iconMap } from '@/data/pipelineData';
import type { PipelineTask } from '@/types/pipeline';
interface TaskCardProps {
task: PipelineTask;
isSelected: boolean;
onSelect: () => void;
onDelete: () => void;
}
// Subtle type indicator colors — just for the small dot/icon, NOT background
const typeConfig: Record<string, { color: string; label: string }> = {
build: { color: '#9a6700', label: '构建' },
deploy: { color: '#1a7f37', label: '部署' },
test: { color: '#8250df', label: '测试' },
custom: { color: '#6e7781', label: '自定义' },
};
export default function TaskCard({ task, isSelected, onSelect, onDelete }: TaskCardProps) {
const config = typeConfig[task.type] || typeConfig.custom;
const IconComponent = iconMap[task.icon] || iconMap.Settings;
const handleDelete = (e: React.MouseEvent) => {
e.stopPropagation();
onDelete();
};
return (
<div
onClick={onSelect}
className="flex items-center gap-2.5 px-3 py-[10px] rounded-md cursor-pointer transition-all duration-150 group mb-[2px] last:mb-0"
style={{
background: isSelected ? '#f0f5ff' : 'transparent',
border: isSelected ? '1px solid #b7d4f0' : '1px solid transparent',
...(isSelected
? { boxShadow: '0 0 0 1px rgba(9,105,218,0.08)' }
: {}),
}}
>
{/* Type icon — small colored dot with icon inside */}
<div
className="w-6 h-6 rounded-[6px] flex items-center justify-center flex-shrink-0"
style={{
background: `${config.color}10`,
border: `1px solid ${config.color}25`,
}}
>
<IconComponent className="w-3 h-3" style={{ color: config.color }} />
</div>
{/* Task info */}
<div className="flex-1 min-w-0">
<div
className="text-[13px] font-medium truncate leading-tight"
style={{ color: isSelected ? 'var(--text-primary)' : 'var(--text-primary)' }}
>
{task.name}
</div>
<div
className="text-[11px] truncate leading-tight mt-[1px]"
style={{ color: 'var(--text-tertiary)' }}
>
{config.label}
{task.scriptType && ` · ${task.scriptType}`}
</div>
</div>
{/* Actions — hover reveal */}
<div className="flex items-center gap-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-150 flex-shrink-0">
<button
onClick={(e) => { e.stopPropagation(); onSelect(); }}
className="p-1 rounded transition-colors hover:bg-black/5"
title="编辑"
>
<Pencil className="w-3 h-3" style={{ color: 'var(--text-tertiary)' }} />
</button>
<button
onClick={handleDelete}
className="p-1 rounded transition-colors hover:bg-red-50"
title="删除"
>
<Trash2 className="w-3 h-3" style={{ color: 'var(--text-tertiary)' }} />
</button>
</div>
</div>
);
}