'use client'; import { useState, useEffect } from 'react'; import { X, RotateCcw, Pencil, Trash2, FileText, AlertCircle, CheckCircle, Check, SkipForward, Copy } from 'lucide-react'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Textarea } from '@/components/ui/textarea'; import { CodeBlock } from './code-block'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; // Copy button with subtle feedback function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); const handleCopy = async () => { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1500); }; return ( ); } interface StepDetailPanelProps { step: any; template: any; isOpen: boolean; onClose: () => void; onMarkDone: (id: number, notes?: string) => Promise; onSkip: (id: number, reason: string) => Promise; onRevert: (id: number, reason?: string) => Promise; onOverride: (id: number, content: string) => Promise; onResetToTemplate: (id: number) => Promise; onEditCustom?: (id: number, data: any) => Promise; onDeleteCustom?: (id: number) => Promise; } const statusIcons = { pending:
, done: , skipped: , reverted: , }; const statusLabels = { pending: 'Pending', done: 'Done', skipped: 'Skipped', reverted: 'Reverted', }; const typeLabels = { bash: 'Bash Script', sql: 'SQL', text: 'Text', }; export function StepDetailPanel({ step, template, isOpen, onClose, onMarkDone, onSkip, onRevert, onOverride, onResetToTemplate, onEditCustom, onDeleteCustom, }: StepDetailPanelProps) { const [notes, setNotes] = useState(''); const [isEditing, setIsEditing] = useState(false); const [editContent, setEditContent] = useState(''); const [showOriginal, setShowOriginal] = useState(false); const [skipReason, setSkipReason] = useState(''); const [isSkipping, setIsSkipping] = useState(false); // Sync state when step changes useEffect(() => { if (step) { setNotes(step.notes || ''); setEditContent(step.content || ''); setIsEditing(false); setShowOriginal(false); setSkipReason(''); setIsSkipping(false); } }, [step?.id]); if (!step) return null; const isCustom = step.isCustom; const isOverridden = step.isOverridden; const hasTemplate = !!step.templateId; const handleMarkDone = async () => { await onMarkDone(step.id, notes); onClose(); }; const handleSkip = async () => { if (!skipReason.trim()) return; await onSkip(step.id, skipReason); onClose(); }; const handleRevert = async () => { await onRevert(step.id, notes); onClose(); }; const handleOverride = async () => { await onOverride(step.id, editContent); setIsEditing(false); }; const handleResetToTemplate = async () => { await onResetToTemplate(step.id); onClose(); }; const handleDeleteCustom = async () => { if (!onDeleteCustom) return; if (confirm('Are you sure you want to delete this custom step?')) { await onDeleteCustom(step.id); onClose(); } }; const renderSourceBadge = () => { if (isCustom) { return (
Custom Step {onEditCustom && onDeleteCustom && (
)}
); } if (isOverridden) { return (
Overridden from Template
); } return (
From Template
); }; return (
{step.name}
{step.customer?.name} {step.customer?.cluster?.name} {step.customer?.namespace}
{/* Status & Type */}
{statusIcons[step.status as keyof typeof statusIcons]} {statusLabels[step.status as keyof typeof statusLabels]}
{typeLabels[step.type as keyof typeof typeLabels]} {step.category}
{/* Source Info */}
{renderSourceBadge()}
{/* Content */}
{!isEditing && ( )}
{isEditing ? (