251205:0000 Just start debug backend/frontend
This commit is contained in:
115
frontend/components/workflows/dsl-editor.tsx
Normal file
115
frontend/components/workflows/dsl-editor.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { CheckCircle, AlertCircle, Play, Loader2 } from "lucide-react";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import { workflowApi } from "@/lib/api/workflows";
|
||||
import { ValidationResult } from "@/types/workflow";
|
||||
|
||||
interface DSLEditorProps {
|
||||
initialValue?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export function DSLEditor({ initialValue = "", onChange }: DSLEditorProps) {
|
||||
const [dsl, setDsl] = useState(initialValue);
|
||||
const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);
|
||||
const [isValidating, setIsValidating] = useState(false);
|
||||
|
||||
const handleEditorChange = (value: string | undefined) => {
|
||||
const newValue = value || "";
|
||||
setDsl(newValue);
|
||||
onChange?.(newValue);
|
||||
setValidationResult(null); // Clear validation on change
|
||||
};
|
||||
|
||||
const validateDSL = async () => {
|
||||
setIsValidating(true);
|
||||
try {
|
||||
const result = await workflowApi.validateDSL(dsl);
|
||||
setValidationResult(result);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setValidationResult({ valid: false, errors: ["Validation failed due to an error"] });
|
||||
} finally {
|
||||
setIsValidating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const testWorkflow = async () => {
|
||||
alert("Test workflow functionality to be implemented");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h3 className="text-lg font-semibold">Workflow DSL</h3>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={validateDSL}
|
||||
disabled={isValidating}
|
||||
>
|
||||
{isValidating ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<CheckCircle className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
Validate
|
||||
</Button>
|
||||
<Button variant="outline" onClick={testWorkflow}>
|
||||
<Play className="mr-2 h-4 w-4" />
|
||||
Test
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="overflow-hidden border rounded-md">
|
||||
<Editor
|
||||
height="500px"
|
||||
defaultLanguage="yaml"
|
||||
value={dsl}
|
||||
onChange={handleEditorChange}
|
||||
theme="vs-dark"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
lineNumbers: "on",
|
||||
rulers: [80],
|
||||
wordWrap: "on",
|
||||
scrollBeyondLastLine: false,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{validationResult && (
|
||||
<Alert variant={validationResult.valid ? "default" : "destructive"} className={validationResult.valid ? "border-green-500 text-green-700 bg-green-50" : ""}>
|
||||
{validationResult.valid ? (
|
||||
<CheckCircle className="h-4 w-4 text-green-600" />
|
||||
) : (
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
)}
|
||||
<AlertDescription>
|
||||
{validationResult.valid ? (
|
||||
"DSL is valid ✓"
|
||||
) : (
|
||||
<div>
|
||||
<p className="font-medium mb-2">Validation Errors:</p>
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
{validationResult.errors?.map((error: string, i: number) => (
|
||||
<li key={i} className="text-sm">
|
||||
{error}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
109
frontend/components/workflows/visual-builder.tsx
Normal file
109
frontend/components/workflows/visual-builder.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import ReactFlow, {
|
||||
Node,
|
||||
Edge,
|
||||
Controls,
|
||||
Background,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
addEdge,
|
||||
Connection,
|
||||
} from "reactflow";
|
||||
import "reactflow/dist/style.css";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
const nodeTypes = {
|
||||
// We can define custom node types here if needed
|
||||
};
|
||||
|
||||
// Color mapping for node types
|
||||
const nodeColors: Record<string, string> = {
|
||||
start: "#10b981", // green
|
||||
step: "#3b82f6", // blue
|
||||
condition: "#f59e0b", // amber
|
||||
end: "#ef4444", // red
|
||||
};
|
||||
|
||||
export function VisualWorkflowBuilder() {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
|
||||
const onConnect = useCallback(
|
||||
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
|
||||
[setEdges]
|
||||
);
|
||||
|
||||
const addNode = (type: string) => {
|
||||
const newNode: Node = {
|
||||
id: `${type}-${Date.now()}`,
|
||||
type: "default", // Using default node type for now
|
||||
position: { x: Math.random() * 400, y: Math.random() * 400 },
|
||||
data: { label: `${type.charAt(0).toUpperCase() + type.slice(1)} Node` },
|
||||
style: {
|
||||
background: nodeColors[type] || "#64748b",
|
||||
color: "white",
|
||||
padding: 10,
|
||||
borderRadius: 5,
|
||||
border: "1px solid #fff",
|
||||
width: 150,
|
||||
},
|
||||
};
|
||||
setNodes((nds) => [...nds, newNode]);
|
||||
};
|
||||
|
||||
const generateDSL = () => {
|
||||
// Convert visual workflow to DSL (Mock implementation)
|
||||
const dsl = {
|
||||
name: "Generated Workflow",
|
||||
steps: nodes.map((node) => ({
|
||||
step_name: node.data.label,
|
||||
step_type: "APPROVAL",
|
||||
})),
|
||||
connections: edges.map((edge) => ({
|
||||
from: edge.source,
|
||||
to: edge.target,
|
||||
})),
|
||||
};
|
||||
alert(JSON.stringify(dsl, null, 2));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button onClick={() => addNode("start")} variant="outline" size="sm" className="border-green-500 text-green-600 hover:bg-green-50">
|
||||
Add Start
|
||||
</Button>
|
||||
<Button onClick={() => addNode("step")} variant="outline" size="sm" className="border-blue-500 text-blue-600 hover:bg-blue-50">
|
||||
Add Step
|
||||
</Button>
|
||||
<Button onClick={() => addNode("condition")} variant="outline" size="sm" className="border-amber-500 text-amber-600 hover:bg-amber-50">
|
||||
Add Condition
|
||||
</Button>
|
||||
<Button onClick={() => addNode("end")} variant="outline" size="sm" className="border-red-500 text-red-600 hover:bg-red-50">
|
||||
Add End
|
||||
</Button>
|
||||
<Button onClick={generateDSL} className="ml-auto" size="sm">
|
||||
Generate DSL
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card className="h-[600px] border">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
nodeTypes={nodeTypes}
|
||||
fitView
|
||||
>
|
||||
<Controls />
|
||||
<Background color="#aaa" gap={16} />
|
||||
</ReactFlow>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user