From b6e3efd30a93518556ee4a8237f5b12c75d7c271 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Wed, 25 Feb 2026 00:11:46 -0500 Subject: [PATCH] feat: add ForkModal component with name and reason fields Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/library/ForkModal.tsx | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 frontend/src/components/library/ForkModal.tsx diff --git a/frontend/src/components/library/ForkModal.tsx b/frontend/src/components/library/ForkModal.tsx new file mode 100644 index 00000000..fc191396 --- /dev/null +++ b/frontend/src/components/library/ForkModal.tsx @@ -0,0 +1,120 @@ +import { useState } from 'react' +import { GitBranch, X } from 'lucide-react' +import { treesApi } from '@/api/trees' +import { toast } from '@/lib/toast' +import { cn } from '@/lib/utils' +import { useNavigate } from 'react-router-dom' + +interface ForkModalProps { + treeId: string + treeName: string + onClose: () => void +} + +export function ForkModal({ treeId, treeName, onClose }: ForkModalProps) { + const navigate = useNavigate() + const [name, setName] = useState(`Copy of ${treeName}`) + const [forkReason, setForkReason] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState(null) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!name.trim()) return + setIsSubmitting(true) + setError(null) + try { + await treesApi.fork(treeId, { + name: name.trim(), + fork_reason: forkReason.trim() || undefined, + }) + toast.success('Flow forked successfully') + onClose() + navigate('/my-trees') + } catch (err) { + console.error('Failed to fork flow:', err) + setError('Failed to fork flow. Please try again.') + } finally { + setIsSubmitting(false) + } + } + + return ( +
+
+ {/* Header */} +
+
+ +

Fork Flow

+
+ +
+ + {/* Body */} +
+
+ + setName(e.target.value)} + required + autoFocus + className={cn( + 'w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground', + 'placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20' + )} + /> +
+ +
+ +