mirror of
https://github.com/Nikhil-Doye/workflow-builder.git
synced 2026-07-22 02:01:56 +02:00
Enhance WorkflowToolbar and localStorage management for workflows
- Added success feedback in WorkflowToolbar upon saving a workflow, including visual changes to the save button. - Implemented localStorage operations for saving, loading, and clearing workflows in workflowStore. - Updated workflow management functions to persist changes to localStorage, ensuring data consistency across sessions.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useWorkflowStore } from "../store/workflowStore";
|
||||
import {
|
||||
Download,
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Square,
|
||||
RotateCcw,
|
||||
Settings,
|
||||
Check,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
downloadWorkflow,
|
||||
@@ -26,10 +27,13 @@ export const WorkflowToolbar: React.FC = () => {
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [showOpenAIConfig, setShowOpenAIConfig] = useState(false);
|
||||
const [showSaveSuccess, setShowSaveSuccess] = useState(false);
|
||||
|
||||
const handleSave = () => {
|
||||
if (currentWorkflow) {
|
||||
saveWorkflow();
|
||||
setShowSaveSuccess(true);
|
||||
setTimeout(() => setShowSaveSuccess(false), 2000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,11 +90,19 @@ export const WorkflowToolbar: React.FC = () => {
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="flex items-center space-x-2 px-3 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 transition-colors"
|
||||
className={`flex items-center space-x-2 px-3 py-2 rounded-md transition-colors ${
|
||||
showSaveSuccess
|
||||
? "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
title="Save workflow"
|
||||
>
|
||||
<Save className="w-4 h-4" />
|
||||
<span>Save</span>
|
||||
{showSaveSuccess ? (
|
||||
<Check className="w-4 h-4" />
|
||||
) : (
|
||||
<Save className="w-4 h-4" />
|
||||
)}
|
||||
<span>{showSaveSuccess ? "Saved!" : "Save"}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
||||
+91
-14
@@ -9,6 +9,53 @@ import {
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { callOpenAI, OpenAIConfig } from "../services/openaiService";
|
||||
|
||||
// localStorage key for workflows
|
||||
const WORKFLOWS_STORAGE_KEY = "agent-workflow-builder-workflows";
|
||||
|
||||
// Helper functions for localStorage operations
|
||||
const saveWorkflowsToStorage = (workflows: Workflow[]): boolean => {
|
||||
try {
|
||||
localStorage.setItem(WORKFLOWS_STORAGE_KEY, JSON.stringify(workflows));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to save workflows to localStorage:", error);
|
||||
// Show user-friendly error message
|
||||
alert(
|
||||
"Failed to save workflows. Your browser's storage might be full or disabled."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadWorkflowsFromStorage = (): Workflow[] => {
|
||||
try {
|
||||
const stored = localStorage.getItem(WORKFLOWS_STORAGE_KEY);
|
||||
if (stored) {
|
||||
const workflows = JSON.parse(stored);
|
||||
// Convert date strings back to Date objects
|
||||
return workflows.map((workflow: any) => ({
|
||||
...workflow,
|
||||
createdAt: new Date(workflow.createdAt),
|
||||
updatedAt: new Date(workflow.updatedAt),
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load workflows from localStorage:", error);
|
||||
alert("Failed to load saved workflows. Some data might be corrupted.");
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const clearWorkflowsFromStorage = (): boolean => {
|
||||
try {
|
||||
localStorage.removeItem(WORKFLOWS_STORAGE_KEY);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to clear workflows from localStorage:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
interface WorkflowStore {
|
||||
workflows: Workflow[];
|
||||
currentWorkflow: Workflow | null;
|
||||
@@ -21,6 +68,7 @@ interface WorkflowStore {
|
||||
loadWorkflow: (workflowId: string) => void;
|
||||
saveWorkflow: () => void;
|
||||
deleteWorkflow: (workflowId: string) => void;
|
||||
clearAllWorkflows: () => void;
|
||||
|
||||
// Node management
|
||||
addNode: (type: string, position: { x: number; y: number }) => void;
|
||||
@@ -58,7 +106,7 @@ const createEmptyWorkflow = (name: string): Workflow => ({
|
||||
});
|
||||
|
||||
export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
|
||||
workflows: [],
|
||||
workflows: loadWorkflowsFromStorage(),
|
||||
currentWorkflow: null,
|
||||
selectedNodeId: null,
|
||||
isExecuting: false,
|
||||
@@ -66,10 +114,15 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
|
||||
|
||||
createWorkflow: (name: string) => {
|
||||
const newWorkflow = createEmptyWorkflow(name);
|
||||
set((state) => ({
|
||||
workflows: [...state.workflows, newWorkflow],
|
||||
const newWorkflows = [...get().workflows, newWorkflow];
|
||||
|
||||
set({
|
||||
workflows: newWorkflows,
|
||||
currentWorkflow: newWorkflow,
|
||||
}));
|
||||
});
|
||||
|
||||
// Persist to localStorage
|
||||
saveWorkflowsToStorage(newWorkflows);
|
||||
},
|
||||
|
||||
loadWorkflow: (workflowId: string) => {
|
||||
@@ -87,21 +140,45 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
set((state) => ({
|
||||
workflows: state.workflows.map((w) =>
|
||||
w.id === updatedWorkflow.id ? updatedWorkflow : w
|
||||
),
|
||||
const newWorkflows = get().workflows.map((w) =>
|
||||
w.id === updatedWorkflow.id ? updatedWorkflow : w
|
||||
);
|
||||
|
||||
set({
|
||||
workflows: newWorkflows,
|
||||
currentWorkflow: updatedWorkflow,
|
||||
}));
|
||||
});
|
||||
|
||||
// Persist to localStorage
|
||||
const success = saveWorkflowsToStorage(newWorkflows);
|
||||
if (success) {
|
||||
console.log(`Workflow "${currentWorkflow.name}" saved successfully`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
deleteWorkflow: (workflowId: string) => {
|
||||
set((state) => ({
|
||||
workflows: state.workflows.filter((w) => w.id !== workflowId),
|
||||
currentWorkflow:
|
||||
state.currentWorkflow?.id === workflowId ? null : state.currentWorkflow,
|
||||
}));
|
||||
const newWorkflows = get().workflows.filter((w) => w.id !== workflowId);
|
||||
const currentWorkflow =
|
||||
get().currentWorkflow?.id === workflowId ? null : get().currentWorkflow;
|
||||
|
||||
set({
|
||||
workflows: newWorkflows,
|
||||
currentWorkflow,
|
||||
});
|
||||
|
||||
// Persist to localStorage
|
||||
saveWorkflowsToStorage(newWorkflows);
|
||||
},
|
||||
|
||||
clearAllWorkflows: () => {
|
||||
set({
|
||||
workflows: [],
|
||||
currentWorkflow: null,
|
||||
});
|
||||
|
||||
// Clear from localStorage
|
||||
clearWorkflowsFromStorage();
|
||||
},
|
||||
|
||||
addNode: (type: string, position: { x: number; y: number }) => {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test localStorage Persistence</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test localStorage Persistence</h1>
|
||||
<div id="status"></div>
|
||||
<button onclick="testSave()">Test Save</button>
|
||||
<button onclick="testLoad()">Test Load</button>
|
||||
<button onclick="testClear()">Test Clear</button>
|
||||
<pre id="output"></pre>
|
||||
|
||||
<script>
|
||||
const WORKFLOWS_STORAGE_KEY = "agent-workflow-builder-workflows";
|
||||
|
||||
function testSave() {
|
||||
const testWorkflow = {
|
||||
id: "test-123",
|
||||
name: "Test Workflow",
|
||||
nodes: [],
|
||||
edges: [],
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
try {
|
||||
localStorage.setItem(
|
||||
WORKFLOWS_STORAGE_KEY,
|
||||
JSON.stringify([testWorkflow])
|
||||
);
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: green;">✓ Saved successfully</span>';
|
||||
} catch (error) {
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: red;">✗ Save failed: ' +
|
||||
error.message +
|
||||
"</span>";
|
||||
}
|
||||
}
|
||||
|
||||
function testLoad() {
|
||||
try {
|
||||
const stored = localStorage.getItem(WORKFLOWS_STORAGE_KEY);
|
||||
if (stored) {
|
||||
const workflows = JSON.parse(stored);
|
||||
document.getElementById("output").textContent = JSON.stringify(
|
||||
workflows,
|
||||
null,
|
||||
2
|
||||
);
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: green;">✓ Loaded successfully</span>';
|
||||
} else {
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: orange;">⚠ No data found</span>';
|
||||
}
|
||||
} catch (error) {
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: red;">✗ Load failed: ' +
|
||||
error.message +
|
||||
"</span>";
|
||||
}
|
||||
}
|
||||
|
||||
function testClear() {
|
||||
try {
|
||||
localStorage.removeItem(WORKFLOWS_STORAGE_KEY);
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: green;">✓ Cleared successfully</span>';
|
||||
document.getElementById("output").textContent = "";
|
||||
} catch (error) {
|
||||
document.getElementById("status").innerHTML =
|
||||
'<span style="color: red;">✗ Clear failed: ' +
|
||||
error.message +
|
||||
"</span>";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user