Merge branch 'bug/component_share' into dev
This commit is contained in:
commit
54064737e8
16 changed files with 216 additions and 170 deletions
|
|
@ -156,11 +156,16 @@ export default function ParameterComponent({
|
|||
};
|
||||
|
||||
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
|
||||
if (!data.node) return;
|
||||
if (data.node!.template[name].value !== newNodeClass.template[name].value) {
|
||||
takeSnapshot();
|
||||
}
|
||||
data.node = newNodeClass;
|
||||
data.node.template[name].value = code;
|
||||
data.node! = {
|
||||
...newNodeClass,
|
||||
description: newNodeClass.description ?? data.node!.description,
|
||||
display_name: newNodeClass.display_name ?? data.node!.display_name,
|
||||
};
|
||||
data.node!.template[name].value = code;
|
||||
updateNodeInternals(data.id);
|
||||
// Set state to pending
|
||||
//@ts-ignore
|
||||
|
|
@ -174,19 +179,22 @@ export default function ParameterComponent({
|
|||
}
|
||||
renderTooltips();
|
||||
let flow = flows.find((flow) => flow.id === tabId);
|
||||
if (reactFlowInstance && flow && flow.data) {
|
||||
cleanEdges({
|
||||
flow: {
|
||||
edges: flow.data!.edges,
|
||||
nodes: flow.data!.nodes,
|
||||
},
|
||||
updateEdge: (edge) => {
|
||||
reactFlowInstance.setEdges(edge);
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
});
|
||||
updateFlow(flow);
|
||||
}
|
||||
setTimeout(() => {
|
||||
//timeout necessary because ReactFlow updates are not async
|
||||
if (reactFlowInstance && flow && flow.data) {
|
||||
cleanEdges({
|
||||
flow: {
|
||||
edges: flow.data!.edges,
|
||||
nodes: flow.data!.nodes,
|
||||
},
|
||||
updateEdge: (edge) => {
|
||||
reactFlowInstance.setEdges(edge);
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
});
|
||||
updateFlow(flow);
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { validationStatusType } from "../../types/components";
|
|||
import { NodeDataType } from "../../types/flow";
|
||||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { classNames, getFieldTitle } from "../../utils/utils";
|
||||
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
|
||||
export default function GenericNode({
|
||||
|
|
@ -107,6 +107,8 @@ export default function GenericNode({
|
|||
|
||||
const showNode = data.showNode ?? true;
|
||||
|
||||
const nameEditable = data.node?.flow || data.type === "CustomComponent";
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeToolbar>
|
||||
|
|
@ -164,7 +166,7 @@ export default function GenericNode({
|
|||
/>
|
||||
{showNode && (
|
||||
<div className="generic-node-tooltip-div">
|
||||
{data.node?.flow && inputName ? (
|
||||
{nameEditable && inputName ? (
|
||||
<div>
|
||||
<InputComponent
|
||||
onBlur={() => {
|
||||
|
|
@ -172,6 +174,7 @@ export default function GenericNode({
|
|||
if (nodeName.trim() !== "") {
|
||||
setNodeName(nodeName);
|
||||
data.node!.display_name = nodeName;
|
||||
updateNodeInternals(data.id);
|
||||
} else {
|
||||
setNodeName(data.node!.display_name);
|
||||
}
|
||||
|
|
@ -194,7 +197,7 @@ export default function GenericNode({
|
|||
<div className="generic-node-tooltip-div pr-2 text-primary">
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
{data.node?.flow && (
|
||||
{nameEditable && (
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 text-ring"
|
||||
|
|
@ -361,57 +364,62 @@ export default function GenericNode({
|
|||
<div
|
||||
className={
|
||||
showNode
|
||||
? "generic-node-desc overflow-hidden " +
|
||||
(data.node?.description !== "" ? "py-5" : "pb-5")
|
||||
? "overflow-hidden " +
|
||||
(data.node?.description === "" && !nameEditable
|
||||
? "pb-5"
|
||||
: "py-5")
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" &&
|
||||
showNode &&
|
||||
data.node?.flow &&
|
||||
inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
<div className="generic-node-desc">
|
||||
{showNode && nameEditable && inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
}
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
updateNodeInternals(data.id);
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
setInputDescription(false);
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
updateNodeInternals(data.id);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="generic-node-desc-text truncate-multiline word-break-break-word"
|
||||
onDoubleClick={() => {
|
||||
setInputDescription(true);
|
||||
takeSnapshot();
|
||||
}}
|
||||
>
|
||||
{data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"generic-node-desc-text truncate-multiline word-break-break-word",
|
||||
(data.node?.description === "" ||
|
||||
!data.node?.description) &&
|
||||
nameEditable
|
||||
? "font-light italic"
|
||||
: ""
|
||||
)}
|
||||
onDoubleClick={() => {
|
||||
setInputDescription(true);
|
||||
takeSnapshot();
|
||||
}}
|
||||
>
|
||||
{(data.node?.description === "" || !data.node?.description) &&
|
||||
nameEditable
|
||||
? "Double Click to Edit Description"
|
||||
: data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Input } from "../../components/ui/input";
|
|||
import { Label } from "../../components/ui/label";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { InputProps } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
export const EditFlowSettings: React.FC<InputProps> = ({
|
||||
name,
|
||||
|
|
@ -21,47 +22,65 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
} else {
|
||||
setIsMaxLength(false);
|
||||
}
|
||||
setName(value);
|
||||
setName!(value);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setDescription(event.target.value);
|
||||
setDescription!(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement">
|
||||
<span className="font-medium">Name</span>{" "}
|
||||
<span className="font-medium">Name{setName ? "" : ":"}</span>{" "}
|
||||
{isMaxLength && (
|
||||
<span className="edit-flow-span">Character limit reached</span>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
className="nopan nodelete nodrag noundo nocopy mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? ""}
|
||||
placeholder="Flow name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
/>
|
||||
{setName ? (
|
||||
<Input
|
||||
className="nopan nodelete nodrag noundo nocopy mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? ""}
|
||||
placeholder="Flow name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
/>
|
||||
) : (
|
||||
<span className="font-normal text-muted-foreground word-break-break-word">
|
||||
{name}
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement mt-3">
|
||||
<span className="font-medium ">Description (optional)</span>
|
||||
<span className="font-medium ">
|
||||
Description{setDescription ? " (optional)" : ":"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
/>
|
||||
{setDescription ? (
|
||||
<Textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className={cn(
|
||||
"font-normal text-muted-foreground word-break-break-word",
|
||||
description === "" ? "font-light italic" : ""
|
||||
)}
|
||||
>
|
||||
{description === "" ? "No description" : description}
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -357,10 +357,10 @@ export async function postCustomComponent(
|
|||
code: string,
|
||||
apiClass: APIClassType
|
||||
): Promise<AxiosResponse<APIClassType>> {
|
||||
let template = apiClass.template;
|
||||
// let template = apiClass.template;
|
||||
return await api.post(`${BASE_URL_API}custom_component`, {
|
||||
code,
|
||||
template,
|
||||
frontend_node: apiClass,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,11 +44,9 @@ export default function ShareModal({
|
|||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [name, setName] = useState(component?.name ?? "");
|
||||
const [description, setDescription] = useState(component?.description ?? "");
|
||||
const [internalOpen, internalSetOpen] = useState(children ? false : true);
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState(false);
|
||||
const nameComponent = is_component ? "Component" : "Flow";
|
||||
const nameComponent = is_component ? "component" : "flow";
|
||||
|
||||
const [tags, setTags] = useState<{ id: string; name: string }[]>([]);
|
||||
const [loadingTags, setLoadingTags] = useState<boolean>(false);
|
||||
|
|
@ -61,6 +59,9 @@ export default function ShareModal({
|
|||
|
||||
const [loadingNames, setLoadingNames] = useState(false);
|
||||
|
||||
const name = component?.name ?? "";
|
||||
const description = component?.description ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
if (open || internalOpen) {
|
||||
if (hasApiKey && hasStore) {
|
||||
|
|
@ -94,11 +95,6 @@ export default function ShareModal({
|
|||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setName(component?.name ?? "");
|
||||
setDescription(component?.description ?? "");
|
||||
}, [component, open, internalOpen]);
|
||||
|
||||
const handleShareComponent = async (update = false) => {
|
||||
//remove file names from flows before sharing
|
||||
removeFileNameFromComponents(component);
|
||||
|
|
@ -120,11 +116,9 @@ export default function ShareModal({
|
|||
is_component: is_component,
|
||||
});
|
||||
|
||||
await saveFlow(flow!, true);
|
||||
|
||||
function successShare() {
|
||||
if (is_component) {
|
||||
addFlow(true, flow);
|
||||
if (!is_component) {
|
||||
saveFlow(flow!, true);
|
||||
}
|
||||
setSuccessData({
|
||||
title: `${nameComponent} shared successfully`,
|
||||
|
|
@ -223,13 +217,9 @@ export default function ShareModal({
|
|||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<EditFlowSettings
|
||||
name={name}
|
||||
invalidNameList={unavaliableNames.map((element) => element.name)}
|
||||
description={description}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
/>
|
||||
<div className="w-full rounded-lg border border-border p-4">
|
||||
<EditFlowSettings name={name} description={description} />
|
||||
</div>
|
||||
<div className="mt-3 flex h-8 w-full">
|
||||
<TagsSelector
|
||||
tags={tags}
|
||||
|
|
@ -297,8 +287,8 @@ export default function ShareModal({
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
{is_component && !loadingNames ? "Save and " : ""}Share{" "}
|
||||
{!is_component && !loadingNames ? "Flow" : ""}
|
||||
Share{" "}
|
||||
{!loadingNames && (!is_component ? "Flow" : "Component")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -282,6 +282,17 @@ export default function Page({
|
|||
let newX = _.cloneDeep(node);
|
||||
return newX;
|
||||
});
|
||||
//@ts-ignore
|
||||
setTabsState((prev: FlowsState) => {
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
...prev[tabId],
|
||||
isPending: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
saveCurrentFlowTimeout();
|
||||
},
|
||||
[setEdges, setNodes, takeSnapshot, addEdge]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,15 @@ export default function NodeToolbarComponent({
|
|||
|
||||
useEffect(() => {
|
||||
setFlowComponent(createFlowComponent(cloneDeep(data), version));
|
||||
}, [data, data.node, showModalAdvanced]);
|
||||
}, [
|
||||
data,
|
||||
data.node,
|
||||
data.node?.display_name,
|
||||
data.node?.description,
|
||||
data.node?.template,
|
||||
showModalAdvanced,
|
||||
showconfirmShare,
|
||||
]);
|
||||
|
||||
const handleSelectChange = (event) => {
|
||||
switch (event) {
|
||||
|
|
|
|||
|
|
@ -311,10 +311,10 @@
|
|||
@apply hover:text-accent-foreground hover:transition-all;
|
||||
}
|
||||
.generic-node-desc {
|
||||
@apply h-full w-full text-foreground;
|
||||
@apply h-full px-5 mb-4 w-full text-foreground;
|
||||
}
|
||||
.generic-node-desc-text {
|
||||
@apply w-full px-5 mb-4 text-sm text-muted-foreground;
|
||||
@apply w-full text-sm text-muted-foreground;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
|
|
|
|||
|
|
@ -240,8 +240,8 @@ export type InputProps = {
|
|||
name: string | null;
|
||||
description: string | null;
|
||||
maxLength?: number;
|
||||
setName: (name: string) => void;
|
||||
setDescription: (description: string) => void;
|
||||
setName?: (name: string) => void;
|
||||
setDescription?: (description: string) => void;
|
||||
invalidNameList?: string[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,37 +41,35 @@ export function cleanEdges({
|
|||
const targetNode = nodes.find((node) => node.id === edge.target);
|
||||
if (!sourceNode || !targetNode) {
|
||||
newEdges = newEdges.filter((edg) => edg.id !== edge.id);
|
||||
return;
|
||||
}
|
||||
// check if the source and target handle still exists
|
||||
if (sourceNode && targetNode) {
|
||||
const sourceHandle = edge.sourceHandle; //right
|
||||
const targetHandle = edge.targetHandle; //left
|
||||
if (targetHandle) {
|
||||
const targetHandleObject: targetHandleType =
|
||||
scapeJSONParse(targetHandle);
|
||||
const field = targetHandleObject.fieldName;
|
||||
const id: targetHandleType = {
|
||||
type: targetNode.data.node!.template[field]?.type,
|
||||
fieldName: field,
|
||||
id: targetNode.data.id,
|
||||
inputTypes: targetNode.data.node!.template[field]?.input_types,
|
||||
};
|
||||
if (targetNode.data.node!.template[field]?.proxy) {
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy;
|
||||
}
|
||||
if (scapedJSONStringfy(id) !== targetHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
const sourceHandle = edge.sourceHandle; //right
|
||||
const targetHandle = edge.targetHandle; //left
|
||||
if (targetHandle) {
|
||||
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle);
|
||||
const field = targetHandleObject.fieldName;
|
||||
const id: targetHandleType = {
|
||||
type: targetNode.data.node!.template[field]?.type,
|
||||
fieldName: field,
|
||||
id: targetNode.data.id,
|
||||
inputTypes: targetNode.data.node!.template[field]?.input_types,
|
||||
};
|
||||
if (targetNode.data.node!.template[field]?.proxy) {
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy;
|
||||
}
|
||||
if (sourceHandle) {
|
||||
const id: sourceHandleType = {
|
||||
id: sourceNode.data.id,
|
||||
baseClasses: sourceNode.data.node!.base_classes,
|
||||
dataType: sourceNode.data.type,
|
||||
};
|
||||
if (scapedJSONStringfy(id) !== sourceHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
if (scapedJSONStringfy(id) !== targetHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
}
|
||||
if (sourceHandle) {
|
||||
const id: sourceHandleType = {
|
||||
id: sourceNode.data.id,
|
||||
baseClasses: sourceNode.data.node!.base_classes,
|
||||
dataType: sourceNode.data.type,
|
||||
};
|
||||
if (scapedJSONStringfy(id) !== sourceHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -761,7 +759,7 @@ export function generateNodeFromFlow(
|
|||
display_name: "Group",
|
||||
documentation: "",
|
||||
base_classes: outputNode!.data.node!.base_classes,
|
||||
description: "double click to edit description",
|
||||
description: "",
|
||||
template: generateNodeTemplate(data),
|
||||
flow: data,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue