mirror of
				https://github.com/bunny-lab-io/Borealis.git
				synced 2025-10-27 07:21:58 -06:00 
			
		
		
		
	feat: replace workflow ellipsis with context menu
This commit is contained in:
		| @@ -231,29 +231,33 @@ export default function App() { | ||||
|     [setTabs] | ||||
|   ); | ||||
|  | ||||
|   const handleSaveFlow = useCallback(async () => { | ||||
|     const tab = tabs.find((t) => t.id === activeTabId); | ||||
|     if (!tab) return; | ||||
|     const name = window.prompt("Enter workflow name", tab.tab_name || "workflow"); | ||||
|     if (!name) return; | ||||
|     const payload = { | ||||
|       name, | ||||
|       workflow: { | ||||
|         tab_name: tab.tab_name, | ||||
|         nodes: tab.nodes, | ||||
|         edges: tab.edges | ||||
|   const handleSaveFlow = useCallback( | ||||
|     async (name) => { | ||||
|       const tab = tabs.find((t) => t.id === activeTabId); | ||||
|       if (!tab || !name) return; | ||||
|       const payload = { | ||||
|         path: tab.folderPath ? `${tab.folderPath}/${name}` : name, | ||||
|         workflow: { | ||||
|           tab_name: tab.tab_name, | ||||
|           nodes: tab.nodes, | ||||
|           edges: tab.edges | ||||
|         } | ||||
|       }; | ||||
|       try { | ||||
|         await fetch("/api/storage/save_workflow", { | ||||
|           method: "POST", | ||||
|           headers: { "Content-Type": "application/json" }, | ||||
|           body: JSON.stringify(payload) | ||||
|         }); | ||||
|         setTabs((prev) => | ||||
|           prev.map((t) => (t.id === activeTabId ? { ...t, tab_name: name } : t)) | ||||
|         ); | ||||
|       } catch (err) { | ||||
|         console.error("Failed to save workflow:", err); | ||||
|       } | ||||
|     }; | ||||
|     try { | ||||
|       await fetch("/api/storage/save_workflow", { | ||||
|         method: "POST", | ||||
|         headers: { "Content-Type": "application/json" }, | ||||
|         body: JSON.stringify(payload) | ||||
|       }); | ||||
|     } catch (err) { | ||||
|       console.error("Failed to save workflow:", err); | ||||
|     } | ||||
|   }, [tabs, activeTabId]); | ||||
|     }, | ||||
|     [tabs, activeTabId] | ||||
|   ); | ||||
|  | ||||
|   const renderMainContent = () => { | ||||
|     switch (currentPage) { | ||||
| @@ -266,9 +270,13 @@ export default function App() { | ||||
|       case "workflows": | ||||
|         return ( | ||||
|           <WorkflowList | ||||
|             onOpenWorkflow={async (workflow) => { | ||||
|             onOpenWorkflow={async (workflow, folderPath, name) => { | ||||
|               const newId = "flow_" + Date.now(); | ||||
|               if (workflow && workflow.rel_path) { | ||||
|                 const folder = workflow.rel_path | ||||
|                   .split("/") | ||||
|                   .slice(0, -1) | ||||
|                   .join("/"); | ||||
|                 try { | ||||
|                   const resp = await fetch( | ||||
|                     `/api/storage/load_workflow?path=${encodeURIComponent( | ||||
| @@ -283,17 +291,32 @@ export default function App() { | ||||
|                       tab_name: | ||||
|                         data.tab_name || workflow.name || workflow.file_name || "Workflow", | ||||
|                       nodes: data.nodes || [], | ||||
|                       edges: data.edges || [] | ||||
|                       edges: data.edges || [], | ||||
|                       folderPath: folder | ||||
|                     } | ||||
|                   ]); | ||||
|                 } catch (err) { | ||||
|                   console.error("Failed to load workflow:", err); | ||||
|                   setTabs([ | ||||
|                     { id: newId, tab_name: workflow?.name || "Workflow", nodes: [], edges: [] } | ||||
|                     { | ||||
|                       id: newId, | ||||
|                       tab_name: workflow?.name || "Workflow", | ||||
|                       nodes: [], | ||||
|                       edges: [], | ||||
|                       folderPath: folder | ||||
|                     } | ||||
|                   ]); | ||||
|                 } | ||||
|               } else { | ||||
|                 setTabs([{ id: newId, tab_name: `Flow`, nodes: [], edges: [] }]); | ||||
|                 setTabs([ | ||||
|                   { | ||||
|                     id: newId, | ||||
|                     tab_name: name || "Flow", | ||||
|                     nodes: [], | ||||
|                     edges: [], | ||||
|                     folderPath: folderPath || "" | ||||
|                   } | ||||
|                 ]); | ||||
|               } | ||||
|               setActiveTabId(newId); | ||||
|               setCurrentPage("workflow-editor"); | ||||
| @@ -316,6 +339,7 @@ export default function App() { | ||||
|                 handleOpenCloseAllDialog={() => setConfirmCloseOpen(true)} | ||||
|                 fileInputRef={fileInputRef} | ||||
|                 onFileInputChange={onFileInputChange} | ||||
|                 currentTabName={tabs.find((t) => t.id === activeTabId)?.tab_name} | ||||
|               /> | ||||
|               <Box sx={{ display: "flex", flexDirection: "column", flexGrow: 1, overflow: "hidden" }}> | ||||
|                 <FlowTabs | ||||
|   | ||||
| @@ -132,10 +132,18 @@ export function RenameWorkflowDialog({ open, value, onChange, onCancel, onSave } | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function RenameFolderDialog({ open, value, onChange, onCancel, onSave }) { | ||||
| export function RenameFolderDialog({ | ||||
|   open, | ||||
|   value, | ||||
|   onChange, | ||||
|   onCancel, | ||||
|   onSave, | ||||
|   title = "Folder Name", | ||||
|   confirmText = "Save" | ||||
| }) { | ||||
|   return ( | ||||
|     <Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> | ||||
|       <DialogTitle>Folder Name</DialogTitle> | ||||
|       <DialogTitle>{title}</DialogTitle> | ||||
|       <DialogContent> | ||||
|         <TextField | ||||
|           autoFocus | ||||
| @@ -157,6 +165,72 @@ export function RenameFolderDialog({ open, value, onChange, onCancel, onSave }) | ||||
|           }} | ||||
|         /> | ||||
|       </DialogContent> | ||||
|       <DialogActions> | ||||
|         <Button onClick={onCancel} sx={{ color: "#58a6ff" }}>Cancel</Button> | ||||
|         <Button onClick={onSave} sx={{ color: "#58a6ff" }}>{confirmText}</Button> | ||||
|       </DialogActions> | ||||
|     </Dialog> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function NewWorkflowDialog({ open, value, onChange, onCancel, onCreate }) { | ||||
|   return ( | ||||
|     <Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> | ||||
|       <DialogTitle>New Workflow</DialogTitle> | ||||
|       <DialogContent> | ||||
|         <TextField | ||||
|           autoFocus | ||||
|           margin="dense" | ||||
|           label="Workflow Name" | ||||
|           fullWidth | ||||
|           variant="outlined" | ||||
|           value={value} | ||||
|           onChange={(e) => onChange(e.target.value)} | ||||
|           sx={{ | ||||
|             "& .MuiOutlinedInput-root": { | ||||
|               backgroundColor: "#2a2a2a", | ||||
|               color: "#ccc", | ||||
|               "& fieldset": { borderColor: "#444" }, | ||||
|               "&:hover fieldset": { borderColor: "#666" } | ||||
|             }, | ||||
|             label: { color: "#aaa" }, | ||||
|             mt: 1 | ||||
|           }} | ||||
|         /> | ||||
|       </DialogContent> | ||||
|       <DialogActions> | ||||
|         <Button onClick={onCancel} sx={{ color: "#58a6ff" }}>Cancel</Button> | ||||
|         <Button onClick={onCreate} sx={{ color: "#58a6ff" }}>Create</Button> | ||||
|       </DialogActions> | ||||
|     </Dialog> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function SaveWorkflowDialog({ open, value, onChange, onCancel, onSave }) { | ||||
|   return ( | ||||
|     <Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> | ||||
|       <DialogTitle>Save Workflow</DialogTitle> | ||||
|       <DialogContent> | ||||
|         <TextField | ||||
|           autoFocus | ||||
|           margin="dense" | ||||
|           label="Workflow Name" | ||||
|           fullWidth | ||||
|           variant="outlined" | ||||
|           value={value} | ||||
|           onChange={(e) => onChange(e.target.value)} | ||||
|           sx={{ | ||||
|             "& .MuiOutlinedInput-root": { | ||||
|               backgroundColor: "#2a2a2a", | ||||
|               color: "#ccc", | ||||
|               "& fieldset": { borderColor: "#444" }, | ||||
|               "&:hover fieldset": { borderColor: "#666" } | ||||
|             }, | ||||
|             label: { color: "#aaa" }, | ||||
|             mt: 1 | ||||
|           }} | ||||
|         /> | ||||
|       </DialogContent> | ||||
|       <DialogActions> | ||||
|         <Button onClick={onCancel} sx={{ color: "#58a6ff" }}>Cancel</Button> | ||||
|         <Button onClick={onSave} sx={{ color: "#58a6ff" }}>Save</Button> | ||||
| @@ -165,6 +239,21 @@ export function RenameFolderDialog({ open, value, onChange, onCancel, onSave }) | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function ConfirmDeleteDialog({ open, message, onCancel, onConfirm }) { | ||||
|   return ( | ||||
|     <Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> | ||||
|       <DialogTitle>Confirm Delete</DialogTitle> | ||||
|       <DialogContent> | ||||
|         <DialogContentText sx={{ color: "#ccc" }}>{message}</DialogContentText> | ||||
|       </DialogContent> | ||||
|       <DialogActions> | ||||
|         <Button onClick={onCancel} sx={{ color: "#58a6ff" }}>Cancel</Button> | ||||
|         <Button onClick={onConfirm} sx={{ color: "#58a6ff" }}>Confirm</Button> | ||||
|       </DialogActions> | ||||
|     </Dialog> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function DeleteDeviceDialog({ open, onCancel, onConfirm }) { | ||||
|   return ( | ||||
|     <Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}> | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import { | ||||
|   ChevronLeft as ChevronLeftIcon, | ||||
|   ChevronRight as ChevronRightIcon | ||||
| } from "@mui/icons-material"; | ||||
| import { SaveWorkflowDialog } from "./Dialogs"; | ||||
|  | ||||
| export default function NodeSidebar({ | ||||
|   categorizedNodes, | ||||
| @@ -29,10 +30,13 @@ export default function NodeSidebar({ | ||||
|   handleSaveFlow, | ||||
|   handleOpenCloseAllDialog, | ||||
|   fileInputRef, | ||||
|   onFileInputChange | ||||
|   onFileInputChange, | ||||
|   currentTabName | ||||
| }) { | ||||
|   const [expandedCategory, setExpandedCategory] = useState(null); | ||||
|   const [collapsed, setCollapsed] = useState(false); | ||||
|   const [saveOpen, setSaveOpen] = useState(false); | ||||
|   const [saveName, setSaveName] = useState(""); | ||||
|  | ||||
|   const handleAccordionChange = (category) => (_, isExpanded) => { | ||||
|     setExpandedCategory(isExpanded ? category : null); | ||||
| @@ -79,7 +83,15 @@ export default function NodeSidebar({ | ||||
|                   </Button> | ||||
|                 </Tooltip> | ||||
|                 <Tooltip title="Save Current Flow to Workflows Folder" placement="right" arrow> | ||||
|                   <Button fullWidth startIcon={<SaveIcon />} onClick={handleSaveFlow} sx={buttonStyle}> | ||||
|                   <Button | ||||
|                     fullWidth | ||||
|                     startIcon={<SaveIcon />} | ||||
|                     onClick={() => { | ||||
|                       setSaveName(currentTabName || "workflow"); | ||||
|                       setSaveOpen(true); | ||||
|                     }} | ||||
|                     sx={buttonStyle} | ||||
|                   > | ||||
|                     Save Workflow | ||||
|                   </Button> | ||||
|                 </Tooltip> | ||||
| @@ -213,6 +225,16 @@ export default function NodeSidebar({ | ||||
|           {collapsed ? <ChevronRightIcon /> : <ChevronLeftIcon />} | ||||
|         </Box> | ||||
|       </Tooltip> | ||||
|       <SaveWorkflowDialog | ||||
|         open={saveOpen} | ||||
|         value={saveName} | ||||
|         onChange={setSaveName} | ||||
|         onCancel={() => setSaveOpen(false)} | ||||
|         onSave={() => { | ||||
|           setSaveOpen(false); | ||||
|           handleSaveFlow(saveName); | ||||
|         }} | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -1,26 +1,17 @@ | ||||
| import React, { useState, useEffect, useCallback } from "react"; | ||||
| import { | ||||
|   Paper, | ||||
|   Box, | ||||
|   Typography, | ||||
|   Button, | ||||
|   IconButton, | ||||
|   Menu, | ||||
|   MenuItem | ||||
| } from "@mui/material"; | ||||
| import { | ||||
|   PlayCircle as PlayCircleIcon, | ||||
|   MoreVert as MoreVertIcon, | ||||
|   Folder as FolderIcon, | ||||
|   Description as DescriptionIcon, | ||||
|   CreateNewFolder as CreateNewFolderIcon | ||||
| } from "@mui/icons-material"; | ||||
| import { Paper, Box, Typography, Menu, MenuItem } from "@mui/material"; | ||||
| import { Folder as FolderIcon, Description as DescriptionIcon } from "@mui/icons-material"; | ||||
| import { | ||||
|   SimpleTreeView, | ||||
|   TreeItem, | ||||
|   useTreeViewApiRef | ||||
| } from "@mui/x-tree-view"; | ||||
| import { RenameWorkflowDialog, RenameFolderDialog } from "./Dialogs"; | ||||
| import { | ||||
|   RenameWorkflowDialog, | ||||
|   RenameFolderDialog, | ||||
|   NewWorkflowDialog, | ||||
|   ConfirmDeleteDialog | ||||
| } from "./Dialogs"; | ||||
|  | ||||
| function buildTree(workflows, folders) { | ||||
|   const map = {}; | ||||
| @@ -88,11 +79,15 @@ function buildTree(workflows, folders) { | ||||
| export default function WorkflowList({ onOpenWorkflow }) { | ||||
|   const [tree, setTree] = useState([]); | ||||
|   const [nodeMap, setNodeMap] = useState({}); | ||||
|   const [menuAnchor, setMenuAnchor] = useState(null); | ||||
|   const [contextMenu, setContextMenu] = useState(null); | ||||
|   const [selectedNode, setSelectedNode] = useState(null); | ||||
|   const [renameValue, setRenameValue] = useState(""); | ||||
|   const [renameOpen, setRenameOpen] = useState(false); | ||||
|   const [renameFolderOpen, setRenameFolderOpen] = useState(false); | ||||
|   const [folderDialogMode, setFolderDialogMode] = useState("rename"); | ||||
|   const [newWorkflowOpen, setNewWorkflowOpen] = useState(false); | ||||
|   const [newWorkflowName, setNewWorkflowName] = useState(""); | ||||
|   const [deleteOpen, setDeleteOpen] = useState(false); | ||||
|   const apiRef = useTreeViewApiRef(); | ||||
|   const [dragNode, setDragNode] = useState(null); | ||||
|  | ||||
| @@ -136,50 +131,57 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|     loadTree(); | ||||
|   }, [loadTree]); | ||||
|  | ||||
|   const openMenu = (e, node) => { | ||||
|     e.stopPropagation(); | ||||
|     setMenuAnchor(e.currentTarget); | ||||
|   const handleContextMenu = (e, node) => { | ||||
|     e.preventDefault(); | ||||
|     setSelectedNode(node); | ||||
|     setContextMenu( | ||||
|       contextMenu === null | ||||
|         ? { | ||||
|             mouseX: e.clientX - 2, | ||||
|             mouseY: e.clientY - 4 | ||||
|           } | ||||
|         : null | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   const closeMenu = () => setMenuAnchor(null); | ||||
|  | ||||
|   const handleRename = () => { | ||||
|     closeMenu(); | ||||
|     setContextMenu(null); | ||||
|     if (!selectedNode) return; | ||||
|     setRenameValue(selectedNode.label); | ||||
|     if (selectedNode.isFolder) setRenameFolderOpen(true); | ||||
|     else setRenameOpen(true); | ||||
|     if (selectedNode.isFolder) { | ||||
|       setFolderDialogMode("rename"); | ||||
|       setRenameFolderOpen(true); | ||||
|     } else setRenameOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleEdit = () => { | ||||
|     closeMenu(); | ||||
|     setContextMenu(null); | ||||
|     if (selectedNode && !selectedNode.isFolder && onOpenWorkflow) { | ||||
|       onOpenWorkflow(selectedNode.workflow); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const handleDeleteWorkflow = async () => { | ||||
|     closeMenu(); | ||||
|   const handleDelete = () => { | ||||
|     setContextMenu(null); | ||||
|     if (!selectedNode) return; | ||||
|     try { | ||||
|       await fetch("/api/storage/delete_workflow", { | ||||
|         method: "POST", | ||||
|         headers: { "Content-Type": "application/json" }, | ||||
|         body: JSON.stringify({ path: selectedNode.path }) | ||||
|       }); | ||||
|       loadTree(); | ||||
|     } catch (err) { | ||||
|       console.error("Failed to delete workflow:", err); | ||||
|     } | ||||
|     setDeleteOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleCreateFolder = () => { | ||||
|     closeMenu(); | ||||
|   const handleNewFolder = () => { | ||||
|     if (!selectedNode) return; | ||||
|     setContextMenu(null); | ||||
|     setFolderDialogMode("create"); | ||||
|     setRenameValue(""); | ||||
|     setRenameFolderOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const handleNewWorkflow = () => { | ||||
|     if (!selectedNode) return; | ||||
|     setContextMenu(null); | ||||
|     setNewWorkflowName(""); | ||||
|     setNewWorkflowOpen(true); | ||||
|   }; | ||||
|  | ||||
|   const saveRenameWorkflow = async () => { | ||||
|     if (!selectedNode) return; | ||||
|     try { | ||||
| @@ -197,7 +199,7 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|  | ||||
|   const saveRenameFolder = async () => { | ||||
|     try { | ||||
|       if (selectedNode && selectedNode.isFolder) { | ||||
|       if (folderDialogMode === "rename" && selectedNode) { | ||||
|         await fetch("/api/storage/rename_folder", { | ||||
|           method: "POST", | ||||
|           headers: { "Content-Type": "application/json" }, | ||||
| @@ -226,6 +228,29 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const confirmDelete = async () => { | ||||
|     if (!selectedNode) return; | ||||
|     try { | ||||
|       if (selectedNode.isFolder) { | ||||
|         await fetch("/api/storage/delete_folder", { | ||||
|           method: "POST", | ||||
|           headers: { "Content-Type": "application/json" }, | ||||
|           body: JSON.stringify({ path: selectedNode.path }) | ||||
|         }); | ||||
|       } else { | ||||
|         await fetch("/api/storage/delete_workflow", { | ||||
|           method: "POST", | ||||
|           headers: { "Content-Type": "application/json" }, | ||||
|           body: JSON.stringify({ path: selectedNode.path }) | ||||
|         }); | ||||
|       } | ||||
|       loadTree(); | ||||
|     } catch (err) { | ||||
|       console.error("Failed to delete:", err); | ||||
|     } | ||||
|     setDeleteOpen(false); | ||||
|   }; | ||||
|  | ||||
|   const renderItems = (nodes) => | ||||
|     nodes.map((n) => ( | ||||
|       <TreeItem | ||||
| @@ -243,6 +268,7 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|               e.preventDefault(); | ||||
|               handleDrop(n); | ||||
|             }} | ||||
|             onContextMenu={(e) => handleContextMenu(e, n)} | ||||
|           > | ||||
|             {n.isFolder ? ( | ||||
|               <FolderIcon sx={{ mr: 1, color: "#0475c2" }} /> | ||||
| @@ -250,13 +276,6 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|               <DescriptionIcon sx={{ mr: 1, color: "#0475c2" }} /> | ||||
|             )} | ||||
|             <Typography sx={{ flexGrow: 1, color: "#e6edf3" }}>{n.label}</Typography> | ||||
|             <IconButton | ||||
|               size="small" | ||||
|               onClick={(e) => openMenu(e, n)} | ||||
|               sx={{ color: "#ccc" }} | ||||
|             > | ||||
|               <MoreVertIcon fontSize="small" /> | ||||
|             </IconButton> | ||||
|           </Box> | ||||
|         } | ||||
|       > | ||||
| @@ -283,41 +302,7 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|             Manage workflow folders and files. | ||||
|           </Typography> | ||||
|         </Box> | ||||
|         <Box> | ||||
|           <Button | ||||
|             startIcon={<CreateNewFolderIcon />} | ||||
|             sx={{ | ||||
|               mr: 1, | ||||
|               color: "#0475c2", | ||||
|               borderColor: "#0475c2", | ||||
|               textTransform: "none", | ||||
|               border: "1px solid #0475c2", | ||||
|               backgroundColor: "#1e1e1e", | ||||
|               "&:hover": { backgroundColor: "#1b1b1b" } | ||||
|             }} | ||||
|             onClick={() => { | ||||
|               setSelectedNode(null); | ||||
|               setRenameValue(""); | ||||
|               setRenameFolderOpen(true); | ||||
|             }} | ||||
|           > | ||||
|             New Folder | ||||
|           </Button> | ||||
|           <Button | ||||
|             startIcon={<PlayCircleIcon />} | ||||
|             sx={{ | ||||
|               color: "#0475c2", | ||||
|               borderColor: "#0475c2", | ||||
|               textTransform: "none", | ||||
|               border: "1px solid #0475c2", | ||||
|               backgroundColor: "#1e1e1e", | ||||
|               "&:hover": { backgroundColor: "#1b1b1b" } | ||||
|             }} | ||||
|             onClick={() => onOpenWorkflow && onOpenWorkflow()} | ||||
|           > | ||||
|             New Workflow | ||||
|           </Button> | ||||
|         </Box> | ||||
|         <Box /> | ||||
|       </Box> | ||||
|       <Box | ||||
|         sx={{ p: 2 }} | ||||
| @@ -339,21 +324,30 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|         </SimpleTreeView> | ||||
|       </Box> | ||||
|       <Menu | ||||
|         anchorEl={menuAnchor} | ||||
|         open={Boolean(menuAnchor)} | ||||
|         onClose={closeMenu} | ||||
|         open={contextMenu !== null} | ||||
|         onClose={() => setContextMenu(null)} | ||||
|         anchorReference="anchorPosition" | ||||
|         anchorPosition= | ||||
|           {contextMenu ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined} | ||||
|         PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} | ||||
|       > | ||||
|         {selectedNode?.isFolder && ( | ||||
|           <MenuItem onClick={handleCreateFolder}>New Folder</MenuItem> | ||||
|         )} | ||||
|         {selectedNode && selectedNode.id !== "root" && ( | ||||
|           <MenuItem onClick={handleRename}>Rename</MenuItem> | ||||
|           <> | ||||
|             <MenuItem onClick={handleNewWorkflow}>New Workflow</MenuItem> | ||||
|             <MenuItem onClick={handleNewFolder}>New Subfolder</MenuItem> | ||||
|             {selectedNode.id !== "root" && ( | ||||
|               <MenuItem onClick={handleRename}>Rename</MenuItem> | ||||
|             )} | ||||
|             {selectedNode.id !== "root" && ( | ||||
|               <MenuItem onClick={handleDelete}>Delete</MenuItem> | ||||
|             )} | ||||
|           </> | ||||
|         )} | ||||
|         {!selectedNode?.isFolder && ( | ||||
|           <> | ||||
|             <MenuItem onClick={handleEdit}>Edit</MenuItem> | ||||
|             <MenuItem onClick={handleDeleteWorkflow}>Delete</MenuItem> | ||||
|             <MenuItem onClick={handleRename}>Rename</MenuItem> | ||||
|             <MenuItem onClick={handleDelete}>Delete</MenuItem> | ||||
|           </> | ||||
|         )} | ||||
|       </Menu> | ||||
| @@ -370,6 +364,24 @@ export default function WorkflowList({ onOpenWorkflow }) { | ||||
|         onChange={setRenameValue} | ||||
|         onCancel={() => setRenameFolderOpen(false)} | ||||
|         onSave={saveRenameFolder} | ||||
|         title={folderDialogMode === "rename" ? "Rename Folder" : "New Folder"} | ||||
|         confirmText={folderDialogMode === "rename" ? "Save" : "Create"} | ||||
|       /> | ||||
|       <NewWorkflowDialog | ||||
|         open={newWorkflowOpen} | ||||
|         value={newWorkflowName} | ||||
|         onChange={setNewWorkflowName} | ||||
|         onCancel={() => setNewWorkflowOpen(false)} | ||||
|         onCreate={() => { | ||||
|           setNewWorkflowOpen(false); | ||||
|           onOpenWorkflow && onOpenWorkflow(null, selectedNode.path, newWorkflowName); | ||||
|         }} | ||||
|       /> | ||||
|       <ConfirmDeleteDialog | ||||
|         open={deleteOpen} | ||||
|         message="If you delete this, there is no undo button, are you sure you want to proceed?" | ||||
|         onCancel={() => setDeleteOpen(false)} | ||||
|         onConfirm={confirmDelete} | ||||
|       /> | ||||
|     </Paper> | ||||
|   ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user