2024-11-18 12:16:11 -05:00
|
|
|
import React, { useState, useEffect } from "react";
|
2024-11-15 06:00:35 -05:00
|
|
|
import { X, Plus, Trash } from "lucide-react";
|
|
|
|
|
import { Node } from "reactflow";
|
|
|
|
|
import { useFlowStore } from "../store/flowStore";
|
2024-11-15 04:44:20 -05:00
|
|
|
|
|
|
|
|
interface ConfigPanelProps {
|
|
|
|
|
node: Node | null;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onUpdateNode: (id: string, data: any) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Field {
|
|
|
|
|
name: string;
|
|
|
|
|
type: string;
|
|
|
|
|
validation?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-18 13:30:35 -05:00
|
|
|
const getDefaultDataForType = (type: string) => {
|
|
|
|
|
const baseData = {
|
|
|
|
|
label: type.charAt(0).toUpperCase() + type.slice(1).replace("-", " ")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case "variable":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
name: "",
|
|
|
|
|
type: "string",
|
|
|
|
|
defaultValue: ""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "url":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
method: "GET",
|
|
|
|
|
path: "",
|
|
|
|
|
fields: [],
|
|
|
|
|
queryFields: []
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "auth":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
authType: "bearer",
|
|
|
|
|
tokenVar: ""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "output":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
outputType: "definition",
|
|
|
|
|
statusCode: 200,
|
|
|
|
|
fields: [],
|
|
|
|
|
responseRaw: ""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "logic":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
code: ""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "db-find":
|
|
|
|
|
case "db-query":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
model: "",
|
|
|
|
|
operation: "findMany",
|
|
|
|
|
query: "",
|
|
|
|
|
resultVar: "result"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "db-insert":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
model: "",
|
|
|
|
|
variables: "",
|
|
|
|
|
resultVar: "result"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
case "db-update":
|
|
|
|
|
case "db-delete":
|
|
|
|
|
return {
|
|
|
|
|
...baseData,
|
|
|
|
|
model: "",
|
|
|
|
|
idField: "id",
|
|
|
|
|
variables: "",
|
|
|
|
|
resultVar: "result"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return baseData;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-15 04:44:20 -05:00
|
|
|
export function ConfigPanel({ node, onClose, onUpdateNode }: ConfigPanelProps) {
|
2024-11-18 12:16:11 -05:00
|
|
|
const { models, updateNode } = useFlowStore();
|
2024-11-15 06:00:35 -05:00
|
|
|
const [newField, setNewField] = useState<Field>({ name: "", type: "string" });
|
2024-11-15 06:12:48 -05:00
|
|
|
const [newQueryField, setNewQueryField] = useState<Field>({
|
|
|
|
|
name: "",
|
|
|
|
|
type: "string",
|
|
|
|
|
});
|
2024-11-15 04:44:20 -05:00
|
|
|
|
2024-11-18 13:30:35 -05:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (node) {
|
|
|
|
|
// Initialize node data with defaults if not already set
|
|
|
|
|
const defaultData = getDefaultDataForType(node.type);
|
|
|
|
|
const newData = {
|
|
|
|
|
...defaultData,
|
|
|
|
|
...node.data // This will override defaults with any existing data
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Only update if the data is different
|
|
|
|
|
if (JSON.stringify(newData) !== JSON.stringify(node.data)) {
|
|
|
|
|
onUpdateNode(node.id, newData);
|
|
|
|
|
updateNode(node.id, newData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [node?.id, node?.type]);
|
|
|
|
|
|
2024-11-18 12:16:11 -05:00
|
|
|
useEffect(() => {
|
|
|
|
|
console.log("ConfigPanel re-rendered with node:", node);
|
|
|
|
|
}, [node]);
|
2024-11-15 04:44:20 -05:00
|
|
|
|
2024-11-18 12:16:11 -05:00
|
|
|
if (!node) return null;
|
|
|
|
|
console.log("what up");
|
2024-11-15 06:00:35 -05:00
|
|
|
const handleChange = (
|
2024-11-18 12:16:11 -05:00
|
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
2024-11-15 06:00:35 -05:00
|
|
|
) => {
|
2024-11-18 12:16:11 -05:00
|
|
|
if (!node) return;
|
|
|
|
|
|
|
|
|
|
console.log("Handling change for:", e.target.name, "with value:", e.target.value);
|
|
|
|
|
|
|
|
|
|
const newData = {
|
|
|
|
|
...node.data,
|
|
|
|
|
[e.target.name]: e.target.value,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log("New data to update:", newData);
|
|
|
|
|
|
|
|
|
|
onUpdateNode(node.id, newData);
|
|
|
|
|
updateNode(node.id, newData);
|
2024-11-15 04:44:20 -05:00
|
|
|
};
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
const handleArrayChange = (
|
|
|
|
|
index: number,
|
|
|
|
|
field: string,
|
|
|
|
|
value: string,
|
|
|
|
|
arrayName: string
|
|
|
|
|
) => {
|
2024-11-18 13:32:19 -05:00
|
|
|
const array = [...node.data[arrayName]];
|
2024-11-15 04:44:20 -05:00
|
|
|
array[index] = { ...array[index], [field]: value };
|
2024-11-18 12:16:11 -05:00
|
|
|
const newData = {
|
2024-11-15 04:44:20 -05:00
|
|
|
...node.data,
|
|
|
|
|
[arrayName]: array,
|
2024-11-18 12:16:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onUpdateNode(node.id, newData);
|
|
|
|
|
updateNode(node.id, newData);
|
2024-11-15 04:44:20 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const addField = (arrayName: string) => {
|
2024-11-15 06:22:30 -05:00
|
|
|
const fieldToAdd = arrayName === "queryFields" ? newQueryField : newField;
|
|
|
|
|
if (!fieldToAdd.name.trim()) return;
|
2024-11-15 06:12:48 -05:00
|
|
|
|
2024-11-18 13:32:19 -05:00
|
|
|
const array = [...node.data[arrayName], { ...fieldToAdd }];
|
2024-11-18 12:16:11 -05:00
|
|
|
const newData = {
|
2024-11-15 04:44:20 -05:00
|
|
|
...node.data,
|
|
|
|
|
[arrayName]: array,
|
2024-11-18 12:16:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onUpdateNode(node.id, newData);
|
|
|
|
|
updateNode(node.id, newData);
|
2024-11-15 06:22:30 -05:00
|
|
|
|
|
|
|
|
// Reset the appropriate state
|
|
|
|
|
if (arrayName === "queryFields") {
|
|
|
|
|
setNewQueryField({ name: "", type: "string" });
|
|
|
|
|
} else {
|
|
|
|
|
setNewField({ name: "", type: "string" });
|
|
|
|
|
}
|
2024-11-15 04:44:20 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const removeField = (index: number, arrayName: string) => {
|
2024-11-18 13:32:19 -05:00
|
|
|
const array = [...node.data[arrayName]];
|
2024-11-15 04:44:20 -05:00
|
|
|
array.splice(index, 1);
|
2024-11-18 12:16:11 -05:00
|
|
|
const newData = {
|
2024-11-15 04:44:20 -05:00
|
|
|
...node.data,
|
|
|
|
|
[arrayName]: array,
|
2024-11-18 12:16:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onUpdateNode(node.id, newData);
|
|
|
|
|
updateNode(node.id, newData);
|
2024-11-15 04:44:20 -05:00
|
|
|
};
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
const copyQueryFields = () => {
|
|
|
|
|
const currentFields = node.data.fields || [];
|
|
|
|
|
navigator.clipboard.writeText(JSON.stringify(currentFields, null, 2));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const extractQueryParams = (path: string) => {
|
|
|
|
|
const params = path.match(/:[a-zA-Z]+/g) || [];
|
|
|
|
|
return params.map((param) => ({
|
|
|
|
|
name: param.substring(1),
|
|
|
|
|
type: "string",
|
|
|
|
|
validation: "",
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-15 04:44:20 -05:00
|
|
|
const renderDatabaseFields = () => (
|
|
|
|
|
<>
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Model</label>
|
|
|
|
|
<select
|
|
|
|
|
name="model"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.model}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Model</option>
|
|
|
|
|
{models.map((model) => (
|
2024-11-16 08:52:13 -05:00
|
|
|
<option key={model.id} value={model.name}>
|
2024-11-15 06:00:35 -05:00
|
|
|
{model.name}
|
|
|
|
|
</option>
|
2024-11-15 04:44:20 -05:00
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2024-11-16 08:52:13 -05:00
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Operation</label>
|
|
|
|
|
<select
|
|
|
|
|
name="operation"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.operation}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
|
|
|
|
<option value="findMany">Find Many</option>
|
|
|
|
|
<option value="findOne">Find One</option>
|
|
|
|
|
<option value="findFirst">Find First</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2024-11-15 04:44:20 -05:00
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">SQL Query</label>
|
|
|
|
|
<textarea
|
|
|
|
|
name="query"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.query}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded h-32 font-mono text-sm"
|
|
|
|
|
placeholder="SELECT * FROM table WHERE id = :id"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Save Result In</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="resultVar"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.resultVar}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="result"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const renderFields = () => {
|
|
|
|
|
switch (node.type) {
|
2024-11-15 06:00:35 -05:00
|
|
|
case "auth":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Auth Type
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<select
|
|
|
|
|
name="authType"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.authType}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
|
|
|
|
<option value="bearer">Bearer Token</option>
|
|
|
|
|
<option value="basic">Basic Auth</option>
|
|
|
|
|
<option value="jwt">JWT</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Token Variable
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="tokenVar"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.tokenVar}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="token"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "url":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Method</label>
|
|
|
|
|
<select
|
|
|
|
|
name="method"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.method}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
2024-11-15 06:00:35 -05:00
|
|
|
{["GET", "POST", "PUT", "DELETE", "PATCH"].map((method) => (
|
|
|
|
|
<option key={method} value={method}>
|
|
|
|
|
{method}
|
|
|
|
|
</option>
|
2024-11-15 04:44:20 -05:00
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Route Path
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="path"
|
2024-11-18 13:30:35 -05:00
|
|
|
value={node.data.path}
|
2024-11-18 14:11:51 -05:00
|
|
|
onChange={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="/api/users/:id"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Body Fields
|
|
|
|
|
</label>
|
2024-11-15 06:12:48 -05:00
|
|
|
{node.data.fields?.length > 0 && (
|
|
|
|
|
<div className="mb-2 p-2 bg-gray-50 rounded text-sm">
|
|
|
|
|
{node.data.fields.map((field: Field) => (
|
2024-11-15 06:00:35 -05:00
|
|
|
<div key={field.name} className="text-gray-600">
|
|
|
|
|
{field.name}: {field.type}
|
|
|
|
|
{field.validation ? ` (${field.validation})` : ""}
|
|
|
|
|
</div>
|
2024-11-15 06:12:48 -05:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2024-11-15 04:44:20 -05:00
|
|
|
{(node.data.fields || []).map((field: Field, index: number) => (
|
2024-11-15 06:00:35 -05:00
|
|
|
<div key={index} className="flex gap-1 mb-2">
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={field.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
handleArrayChange(index, "name", e.target.value, "fields")
|
|
|
|
|
}
|
|
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
2024-11-15 04:44:20 -05:00
|
|
|
placeholder="Field name"
|
|
|
|
|
/>
|
|
|
|
|
<select
|
|
|
|
|
value={field.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
handleArrayChange(index, "type", e.target.value, "fields")
|
|
|
|
|
}
|
|
|
|
|
className="w-20 p-2 border rounded text-sm"
|
2024-11-15 04:44:20 -05:00
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
2024-11-15 06:00:35 -05:00
|
|
|
<option value="boolean">Bool</option>
|
2024-11-15 04:44:20 -05:00
|
|
|
<option value="date">Date</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
2024-11-15 06:00:35 -05:00
|
|
|
onClick={() => removeField(index, "fields")}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="p-2 text-red-500 hover:bg-red-50 rounded"
|
|
|
|
|
>
|
|
|
|
|
<Trash className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
2024-11-15 06:00:35 -05:00
|
|
|
<div className="flex gap-1 mt-2">
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={newField.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
setNewField({ ...newField, name: e.target.value })
|
|
|
|
|
}
|
|
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
2024-11-15 04:44:20 -05:00
|
|
|
placeholder="New field name"
|
|
|
|
|
/>
|
|
|
|
|
<select
|
|
|
|
|
value={newField.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
setNewField({ ...newField, type: e.target.value })
|
|
|
|
|
}
|
|
|
|
|
className="w-20 p-2 border rounded text-sm"
|
2024-11-15 04:44:20 -05:00
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
2024-11-15 06:00:35 -05:00
|
|
|
<option value="boolean">Bool</option>
|
2024-11-15 04:44:20 -05:00
|
|
|
<option value="date">Date</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
2024-11-15 06:12:48 -05:00
|
|
|
onClick={() => {
|
|
|
|
|
if (newField.name.trim()) {
|
|
|
|
|
addField("fields");
|
|
|
|
|
}
|
|
|
|
|
}}
|
2024-11-15 06:00:35 -05:00
|
|
|
className="p-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
|
|
|
>
|
|
|
|
|
<Plus className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">
|
2024-11-15 06:12:48 -05:00
|
|
|
Query Parameters
|
2024-11-15 06:00:35 -05:00
|
|
|
</label>
|
2024-11-15 06:12:48 -05:00
|
|
|
{node.data.queryFields?.length > 0 && (
|
|
|
|
|
<div className="mb-2 p-2 bg-gray-50 rounded text-sm">
|
|
|
|
|
{node.data.queryFields.map((field: Field) => (
|
2024-11-15 06:00:35 -05:00
|
|
|
<div key={field.name} className="text-gray-600">
|
|
|
|
|
{field.name}: {field.type}
|
|
|
|
|
{field.validation ? ` (${field.validation})` : ""}
|
|
|
|
|
</div>
|
2024-11-15 06:12:48 -05:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2024-11-15 06:00:35 -05:00
|
|
|
{(node.data.queryFields || []).map(
|
|
|
|
|
(field: Field, index: number) => (
|
|
|
|
|
<div key={index} className="flex gap-1 mb-2">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={field.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
handleArrayChange(
|
|
|
|
|
index,
|
|
|
|
|
"name",
|
|
|
|
|
e.target.value,
|
|
|
|
|
"queryFields"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
|
|
|
|
placeholder="Field name"
|
|
|
|
|
/>
|
|
|
|
|
<select
|
|
|
|
|
value={field.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:00:35 -05:00
|
|
|
handleArrayChange(
|
|
|
|
|
index,
|
|
|
|
|
"type",
|
|
|
|
|
e.target.value,
|
|
|
|
|
"queryFields"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
className="w-20 p-2 border rounded text-sm"
|
|
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
|
|
|
|
<option value="boolean">Bool</option>
|
|
|
|
|
<option value="date">Date</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => removeField(index, "queryFields")}
|
|
|
|
|
className="p-2 text-red-500 hover:bg-red-50 rounded"
|
|
|
|
|
>
|
|
|
|
|
<Trash className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
)}
|
|
|
|
|
<div className="flex gap-1 mt-2">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
2024-11-15 06:12:48 -05:00
|
|
|
value={newQueryField.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:12:48 -05:00
|
|
|
setNewQueryField({ ...newQueryField, name: e.target.value })
|
2024-11-15 06:00:35 -05:00
|
|
|
}
|
|
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
|
|
|
|
placeholder="New query param"
|
|
|
|
|
/>
|
|
|
|
|
<select
|
2024-11-15 06:12:48 -05:00
|
|
|
value={newQueryField.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-15 06:12:48 -05:00
|
|
|
setNewQueryField({ ...newQueryField, type: e.target.value })
|
2024-11-15 06:00:35 -05:00
|
|
|
}
|
|
|
|
|
className="w-20 p-2 border rounded text-sm"
|
|
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
|
|
|
|
<option value="boolean">Bool</option>
|
|
|
|
|
<option value="date">Date</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
2024-11-15 06:12:48 -05:00
|
|
|
onClick={() => {
|
|
|
|
|
if (newQueryField.name.trim()) {
|
2024-11-15 06:22:30 -05:00
|
|
|
const updatedQueryFields = [
|
2024-11-15 06:12:48 -05:00
|
|
|
...(node.data.queryFields || []),
|
|
|
|
|
{ ...newQueryField },
|
|
|
|
|
];
|
|
|
|
|
onUpdateNode(node.id, {
|
|
|
|
|
...node.data,
|
2024-11-15 06:22:30 -05:00
|
|
|
queryFields: updatedQueryFields,
|
2024-11-15 06:12:48 -05:00
|
|
|
});
|
|
|
|
|
setNewQueryField({ name: "", type: "string" });
|
|
|
|
|
}
|
|
|
|
|
}}
|
2024-11-15 06:00:35 -05:00
|
|
|
className="p-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
2024-11-15 04:44:20 -05:00
|
|
|
>
|
|
|
|
|
<Plus className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "output":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="mb-4">
|
2024-11-16 08:52:13 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">Output Type</label>
|
|
|
|
|
<select
|
|
|
|
|
name="outputType"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.outputType}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
|
|
|
|
<option value="definition">Definition</option>
|
|
|
|
|
<option value="mockup">Mockup</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{node.data.outputType === "mockup" ? (
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Response Raw</label>
|
|
|
|
|
<textarea
|
|
|
|
|
name="responseRaw"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.responseRaw}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="w-full p-2 border rounded h-32"
|
|
|
|
|
placeholder="Enter raw response here..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Fields</label>
|
|
|
|
|
<div className="flex flex-wrap gap-2 mb-2">
|
|
|
|
|
{(node.data.fields || []).map((field: Field, index: number) => (
|
|
|
|
|
<div key={index} className="flex items-center">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={field.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-16 08:52:13 -05:00
|
|
|
handleArrayChange(index, "name", e.target.value, "fields")
|
|
|
|
|
}
|
|
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
|
|
|
|
placeholder="Field name"
|
|
|
|
|
/>
|
|
|
|
|
<select
|
|
|
|
|
value={field.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-16 08:52:13 -05:00
|
|
|
handleArrayChange(index, "type", e.target.value, "fields")
|
|
|
|
|
}
|
|
|
|
|
className="w-24 p-2 border rounded text-sm"
|
|
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
|
|
|
|
<option value="boolean">Boolean</option>
|
|
|
|
|
<option value="date">Date</option>
|
|
|
|
|
<option value="object">Object</option>
|
|
|
|
|
<option value="array">Array</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => removeField(index, "fields")}
|
|
|
|
|
className="p-2 text-red-500 hover:bg-red-50 rounded"
|
|
|
|
|
>
|
|
|
|
|
<Trash className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2 mt-2">
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2024-11-16 08:52:13 -05:00
|
|
|
value={newField.name}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-16 08:52:13 -05:00
|
|
|
setNewField({ ...newField, name: e.target.value })
|
2024-11-15 06:00:35 -05:00
|
|
|
}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="flex-1 p-2 border rounded text-sm"
|
|
|
|
|
placeholder="New field name"
|
2024-11-15 04:44:20 -05:00
|
|
|
/>
|
|
|
|
|
<select
|
2024-11-16 08:52:13 -05:00
|
|
|
value={newField.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={(e) =>
|
2024-11-16 08:52:13 -05:00
|
|
|
setNewField({ ...newField, type: e.target.value })
|
2024-11-15 06:00:35 -05:00
|
|
|
}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="w-24 p-2 border rounded text-sm"
|
2024-11-15 04:44:20 -05:00
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
|
|
|
|
<option value="boolean">Boolean</option>
|
|
|
|
|
<option value="date">Date</option>
|
2024-11-16 08:52:13 -05:00
|
|
|
<option value="object">Object</option>
|
|
|
|
|
<option value="array">Array</option>
|
2024-11-15 04:44:20 -05:00
|
|
|
</select>
|
|
|
|
|
<button
|
2024-11-16 08:52:13 -05:00
|
|
|
onClick={() => {
|
|
|
|
|
if (newField.name.trim()) {
|
|
|
|
|
const updatedFields = [
|
|
|
|
|
...(node.data.fields || []),
|
|
|
|
|
{ ...newField },
|
|
|
|
|
];
|
|
|
|
|
onUpdateNode(node.id, {
|
|
|
|
|
...node.data,
|
|
|
|
|
fields: updatedFields,
|
|
|
|
|
});
|
|
|
|
|
setNewField({ name: "", type: newField.type }); // Use the selected type here
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600"
|
2024-11-15 04:44:20 -05:00
|
|
|
>
|
2024-11-16 08:52:13 -05:00
|
|
|
<Plus className="w-4 h-4" />
|
2024-11-15 04:44:20 -05:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-11-16 08:52:13 -05:00
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Status Code</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
name="statusCode"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.statusCode}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-16 08:52:13 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="200"
|
|
|
|
|
/>
|
2024-11-15 04:44:20 -05:00
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "variable":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Variable Name
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="name"
|
2024-11-18 13:30:35 -05:00
|
|
|
value={node.data.name}
|
|
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="myVariable"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">Type</label>
|
|
|
|
|
<select
|
|
|
|
|
name="type"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.type}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
>
|
|
|
|
|
<option value="string">String</option>
|
|
|
|
|
<option value="number">Number</option>
|
|
|
|
|
<option value="boolean">Boolean</option>
|
|
|
|
|
<option value="object">Object</option>
|
|
|
|
|
<option value="array">Array</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Default Value
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="defaultValue"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.defaultValue}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="Default value"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "logic":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
JavaScript Code
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<textarea
|
|
|
|
|
name="code"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.code}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded h-40 font-mono text-sm"
|
|
|
|
|
placeholder="// Write your JavaScript code here"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "db-find":
|
2024-11-16 08:52:13 -05:00
|
|
|
return renderDatabaseFields();
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "db-query":
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{renderDatabaseFields()}
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<button
|
|
|
|
|
onClick={copyQueryFields}
|
|
|
|
|
className="px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 rounded"
|
|
|
|
|
>
|
|
|
|
|
Copy Fields
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2024-11-15 04:44:20 -05:00
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "db-insert":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{renderDatabaseFields()}
|
|
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Variables
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<textarea
|
|
|
|
|
name="variables"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.variables}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded h-20 font-mono text-sm"
|
|
|
|
|
placeholder="name: string age: number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-15 06:00:35 -05:00
|
|
|
case "db-update":
|
|
|
|
|
case "db-delete":
|
2024-11-15 04:44:20 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{renderDatabaseFields()}
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<label className="block text-sm font-medium mb-1">ID Field</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="idField"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.idField}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded"
|
|
|
|
|
placeholder="id"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2024-11-15 06:00:35 -05:00
|
|
|
{node.type === "db-update" && (
|
2024-11-15 04:44:20 -05:00
|
|
|
<div className="mb-4">
|
2024-11-15 06:00:35 -05:00
|
|
|
<label className="block text-sm font-medium mb-1">
|
|
|
|
|
Variables
|
|
|
|
|
</label>
|
2024-11-15 04:44:20 -05:00
|
|
|
<textarea
|
|
|
|
|
name="variables"
|
2024-11-18 13:32:19 -05:00
|
|
|
value={node.data.variables}
|
2024-11-18 13:30:35 -05:00
|
|
|
onBlur={handleChange}
|
2024-11-15 04:44:20 -05:00
|
|
|
className="w-full p-2 border rounded h-20 font-mono text-sm"
|
|
|
|
|
placeholder="name: string age: number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="fixed right-0 top-0 h-full w-80 bg-white border-l border-gray-200 p-4 shadow-lg transform transition-transform overflow-y-auto">
|
|
|
|
|
<div className="flex justify-between items-center mb-4">
|
|
|
|
|
<h3 className="text-lg font-semibold">Configure Node</h3>
|
|
|
|
|
<button onClick={onClose} className="p-1 hover:bg-gray-100 rounded">
|
|
|
|
|
<X className="w-5 h-5" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{renderFields()}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2024-11-15 06:00:35 -05:00
|
|
|
}
|