mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-09-11 04:58:41 -06:00
feat: enhance workflow tree view
This commit is contained in:
@@ -22,12 +22,37 @@ import {
|
|||||||
} from "@mui/x-tree-view";
|
} from "@mui/x-tree-view";
|
||||||
import { RenameWorkflowDialog, RenameFolderDialog } from "./Dialogs";
|
import { RenameWorkflowDialog, RenameFolderDialog } from "./Dialogs";
|
||||||
|
|
||||||
function buildTree(workflows) {
|
function buildTree(workflows, folders) {
|
||||||
const map = {};
|
const map = {};
|
||||||
const root = [];
|
const rootNode = {
|
||||||
|
id: "root",
|
||||||
|
label: "Workflows",
|
||||||
|
path: "",
|
||||||
|
isFolder: true,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
map[rootNode.id] = rootNode;
|
||||||
|
|
||||||
|
(folders || []).forEach((f) => {
|
||||||
|
const parts = (f || "").split("/");
|
||||||
|
let children = rootNode.children;
|
||||||
|
let parentPath = "";
|
||||||
|
parts.forEach((part) => {
|
||||||
|
const path = parentPath ? `${parentPath}/${part}` : part;
|
||||||
|
let node = children.find((n) => n.id === path);
|
||||||
|
if (!node) {
|
||||||
|
node = { id: path, label: part, path, isFolder: true, children: [] };
|
||||||
|
children.push(node);
|
||||||
|
map[path] = node;
|
||||||
|
}
|
||||||
|
children = node.children;
|
||||||
|
parentPath = path;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
(workflows || []).forEach((w) => {
|
(workflows || []).forEach((w) => {
|
||||||
const parts = (w.rel_path || "").split("/");
|
const parts = (w.rel_path || "").split("/");
|
||||||
let children = root;
|
let children = rootNode.children;
|
||||||
let parentPath = "";
|
let parentPath = "";
|
||||||
parts.forEach((part, idx) => {
|
parts.forEach((part, idx) => {
|
||||||
const path = parentPath ? `${parentPath}/${part}` : part;
|
const path = parentPath ? `${parentPath}/${part}` : part;
|
||||||
@@ -56,7 +81,8 @@ function buildTree(workflows) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return { root, map };
|
|
||||||
|
return { root: [rootNode], map };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WorkflowList({ onOpenWorkflow }) {
|
export default function WorkflowList({ onOpenWorkflow }) {
|
||||||
@@ -96,7 +122,7 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
const resp = await fetch("/api/storage/load_workflows");
|
const resp = await fetch("/api/storage/load_workflows");
|
||||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
const { root, map } = buildTree(data.workflows || []);
|
const { root, map } = buildTree(data.workflows || [], data.folders || []);
|
||||||
setTree(root);
|
setTree(root);
|
||||||
setNodeMap(map);
|
setNodeMap(map);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -126,6 +152,13 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
else setRenameOpen(true);
|
else setRenameOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
closeMenu();
|
||||||
|
if (selectedNode && !selectedNode.isFolder && onOpenWorkflow) {
|
||||||
|
onOpenWorkflow(selectedNode.workflow);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteWorkflow = async () => {
|
const handleDeleteWorkflow = async () => {
|
||||||
closeMenu();
|
closeMenu();
|
||||||
if (!selectedNode) return;
|
if (!selectedNode) return;
|
||||||
@@ -212,9 +245,9 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{n.isFolder ? (
|
{n.isFolder ? (
|
||||||
<FolderIcon sx={{ mr: 1, color: "#ffd54f" }} />
|
<FolderIcon sx={{ mr: 1, color: "#0475c2" }} />
|
||||||
) : (
|
) : (
|
||||||
<DescriptionIcon sx={{ mr: 1, color: "#90caf9" }} />
|
<DescriptionIcon sx={{ mr: 1, color: "#0475c2" }} />
|
||||||
)}
|
)}
|
||||||
<Typography sx={{ flexGrow: 1, color: "#e6edf3" }}>{n.label}</Typography>
|
<Typography sx={{ flexGrow: 1, color: "#e6edf3" }}>{n.label}</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -243,7 +276,7 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h6" sx={{ color: "#58a6ff", mb: 0 }}>
|
<Typography variant="h6" sx={{ color: "#0475c2", mb: 0 }}>
|
||||||
Workflows
|
Workflows
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ color: "#aaa" }}>
|
<Typography variant="body2" sx={{ color: "#aaa" }}>
|
||||||
@@ -255,10 +288,10 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
startIcon={<CreateNewFolderIcon />}
|
startIcon={<CreateNewFolderIcon />}
|
||||||
sx={{
|
sx={{
|
||||||
mr: 1,
|
mr: 1,
|
||||||
color: "#58a6ff",
|
color: "#0475c2",
|
||||||
borderColor: "#58a6ff",
|
borderColor: "#0475c2",
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
border: "1px solid #58a6ff",
|
border: "1px solid #0475c2",
|
||||||
backgroundColor: "#1e1e1e",
|
backgroundColor: "#1e1e1e",
|
||||||
"&:hover": { backgroundColor: "#1b1b1b" }
|
"&:hover": { backgroundColor: "#1b1b1b" }
|
||||||
}}
|
}}
|
||||||
@@ -273,10 +306,10 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
<Button
|
<Button
|
||||||
startIcon={<PlayCircleIcon />}
|
startIcon={<PlayCircleIcon />}
|
||||||
sx={{
|
sx={{
|
||||||
color: "#58a6ff",
|
color: "#0475c2",
|
||||||
borderColor: "#58a6ff",
|
borderColor: "#0475c2",
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
border: "1px solid #58a6ff",
|
border: "1px solid #0475c2",
|
||||||
backgroundColor: "#1e1e1e",
|
backgroundColor: "#1e1e1e",
|
||||||
"&:hover": { backgroundColor: "#1b1b1b" }
|
"&:hover": { backgroundColor: "#1b1b1b" }
|
||||||
}}
|
}}
|
||||||
@@ -300,6 +333,7 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
sx={{ color: "#e6edf3" }}
|
sx={{ color: "#e6edf3" }}
|
||||||
onNodeSelect={handleNodeSelect}
|
onNodeSelect={handleNodeSelect}
|
||||||
apiRef={apiRef}
|
apiRef={apiRef}
|
||||||
|
defaultExpanded={["root"]}
|
||||||
>
|
>
|
||||||
{renderItems(tree)}
|
{renderItems(tree)}
|
||||||
</SimpleTreeView>
|
</SimpleTreeView>
|
||||||
@@ -313,9 +347,14 @@ export default function WorkflowList({ onOpenWorkflow }) {
|
|||||||
{selectedNode?.isFolder && (
|
{selectedNode?.isFolder && (
|
||||||
<MenuItem onClick={handleCreateFolder}>New Folder</MenuItem>
|
<MenuItem onClick={handleCreateFolder}>New Folder</MenuItem>
|
||||||
)}
|
)}
|
||||||
<MenuItem onClick={handleRename}>Rename</MenuItem>
|
{selectedNode && selectedNode.id !== "root" && (
|
||||||
|
<MenuItem onClick={handleRename}>Rename</MenuItem>
|
||||||
|
)}
|
||||||
{!selectedNode?.isFolder && (
|
{!selectedNode?.isFolder && (
|
||||||
<MenuItem onClick={handleDeleteWorkflow}>Delete</MenuItem>
|
<>
|
||||||
|
<MenuItem onClick={handleEdit}>Edit</MenuItem>
|
||||||
|
<MenuItem onClick={handleDeleteWorkflow}>Delete</MenuItem>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
<RenameWorkflowDialog
|
<RenameWorkflowDialog
|
||||||
|
@@ -200,6 +200,7 @@ def load_workflows():
|
|||||||
os.path.join(os.path.dirname(__file__), "..", "..", "Workflows")
|
os.path.join(os.path.dirname(__file__), "..", "..", "Workflows")
|
||||||
)
|
)
|
||||||
results: List[Dict] = []
|
results: List[Dict] = []
|
||||||
|
folders: List[str] = []
|
||||||
|
|
||||||
if not os.path.isdir(workflows_root):
|
if not os.path.isdir(workflows_root):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
@@ -209,6 +210,9 @@ def load_workflows():
|
|||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
for root, dirs, files in os.walk(workflows_root):
|
for root, dirs, files in os.walk(workflows_root):
|
||||||
|
rel_root = os.path.relpath(root, workflows_root)
|
||||||
|
if rel_root != ".":
|
||||||
|
folders.append(rel_root.replace(os.sep, "/"))
|
||||||
for fname in files:
|
for fname in files:
|
||||||
if not fname.lower().endswith(".json"):
|
if not fname.lower().endswith(".json"):
|
||||||
continue
|
continue
|
||||||
@@ -246,7 +250,8 @@ def load_workflows():
|
|||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"root": workflows_root,
|
"root": workflows_root,
|
||||||
"workflows": results
|
"workflows": results,
|
||||||
|
"folders": folders
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user