|
|
@@ -3,7 +3,7 @@ import { Link, useNavigate, useSearchParams } from 'react-router-dom'
|
|
|
import { workflowsApi, nodesApi, Workflow } from '../api/workflows'
|
|
|
import { usersApi } from '../api/users'
|
|
|
import { workflowGroupsApi, WorkflowGroup } from '../api/workflowGroups'
|
|
|
-import { Plus, Play, Pause, Trash2, MoreVertical, AlertTriangle, X, FolderPlus, FolderInput, Clock, User, Zap } from 'lucide-react'
|
|
|
+import { Plus, Play, Pause, Trash2, MoreVertical, AlertTriangle, X, FolderPlus, FolderInput, Clock, User, Zap, Copy } from 'lucide-react'
|
|
|
import { useState } from 'react'
|
|
|
import { formatDistanceToNow, format } from 'date-fns'
|
|
|
import { GroupBreadcrumb } from '../components/GroupBreadcrumb'
|
|
|
@@ -216,6 +216,16 @@ export default function WorkflowsPage() {
|
|
|
},
|
|
|
})
|
|
|
|
|
|
+ const duplicateWorkflowMutation = useMutation({
|
|
|
+ mutationFn: (id: string) => workflowsApi.duplicate(id),
|
|
|
+ onSuccess: (copy) => {
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['workflows'] })
|
|
|
+ // Straight into the copy: duplicating is almost always the first step of
|
|
|
+ // editing it, and the copy is inactive so nothing runs meanwhile.
|
|
|
+ navigate(`/workflows/${copy.id}`)
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
const moveWorkflowMutation = useMutation({
|
|
|
mutationFn: ({ id, groupId }: { id: string; groupId: string | null }) =>
|
|
|
workflowsApi.moveToGroup(id, groupId),
|
|
|
@@ -346,6 +356,11 @@ export default function WorkflowsPage() {
|
|
|
toggleActiveMutation.mutate({ id: workflow.id, active: workflow.active })
|
|
|
}
|
|
|
}}
|
|
|
+ onDuplicate={() => {
|
|
|
+ if (workflow.id) {
|
|
|
+ duplicateWorkflowMutation.mutate(workflow.id)
|
|
|
+ }
|
|
|
+ }}
|
|
|
onMove={() => setWorkflowToMove(workflow)}
|
|
|
onDelete={() => setWorkflowToDelete(workflow)}
|
|
|
/>
|
|
|
@@ -472,6 +487,7 @@ function WorkflowCard({
|
|
|
ownerName,
|
|
|
triggers,
|
|
|
onToggleActive,
|
|
|
+ onDuplicate,
|
|
|
onMove,
|
|
|
onDelete,
|
|
|
}: {
|
|
|
@@ -480,6 +496,7 @@ function WorkflowCard({
|
|
|
ownerName?: string
|
|
|
triggers: TriggerSummary[]
|
|
|
onToggleActive: () => void
|
|
|
+ onDuplicate: () => void
|
|
|
onMove: () => void
|
|
|
onDelete: () => void
|
|
|
}) {
|
|
|
@@ -534,6 +551,16 @@ function WorkflowCard({
|
|
|
</>
|
|
|
)}
|
|
|
</button>
|
|
|
+ <button
|
|
|
+ onClick={() => {
|
|
|
+ onDuplicate()
|
|
|
+ setShowMenu(false)
|
|
|
+ }}
|
|
|
+ className="w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-slate-700 flex items-center gap-2"
|
|
|
+ title="Create an inactive copy of this workflow"
|
|
|
+ >
|
|
|
+ <Copy className="w-4 h-4" /> Duplicate
|
|
|
+ </button>
|
|
|
<button
|
|
|
onClick={() => {
|
|
|
onMove()
|