Feat: Add option to replace saved component or create a new one using confirmation modal

This commit is contained in:
igorrCarvalho 2023-11-23 22:06:07 -03:00
commit 7e4dd16881
15 changed files with 659 additions and 556 deletions

View file

@ -22,7 +22,10 @@ import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button";
import { LANGFLOW_SUPPORTED_TYPES, TOOLTIP_EMPTY } from "../../../../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { ParameterComponentType } from "../../../../types/components";
@ -222,9 +225,7 @@ export default function ParameterComponent({
}, [tooltipTitle, flow]);
return !showNode ? (
left &&
(LANGFLOW_SUPPORTED_TYPES.has(type??"")) &&
!optionalHandle ? (
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></>
) : (
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
@ -297,9 +298,7 @@ export default function ParameterComponent({
)}
</div>
</div>
{left &&
(LANGFLOW_SUPPORTED_TYPES.has(type??"")) &&
!optionalHandle ? (
{left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></>
) : (
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">

File diff suppressed because it is too large Load diff

View file

@ -664,5 +664,14 @@ export const LAST_USED_SPAN_1 = "The last time this key was used.";
export const LAST_USED_SPAN_2 =
"Accurate to within the hour from the most recent usage.";
export const LANGFLOW_SUPPORTED_TYPES= new Set(["str","bool","float","code",
"prompt","file","int","dict","NestedDict"])
export const LANGFLOW_SUPPORTED_TYPES = new Set([
"str",
"bool",
"float",
"code",
"prompt",
"file",
"int",
"dict",
"NestedDict",
]);

View file

@ -56,7 +56,11 @@ const FlowsContextInitialValue: FlowsContextType = {
isLoading: true,
flows: [],
removeFlow: (id: string) => {},
addFlow: async (newProject: boolean, flowData?: FlowType) => "",
addFlow: async (
newProject: boolean,
flowData?: FlowType,
override?: boolean
) => "",
updateFlow: (newFlow: FlowType) => {},
incrementNodeId: () => uid(),
downloadFlow: (flow: FlowType) => {},
@ -78,7 +82,7 @@ const FlowsContextInitialValue: FlowsContextType = {
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => {},
saveComponent: async (component: NodeDataType) => "",
saveComponent: async (component: NodeDataType, override: boolean) => "",
deleteComponent: (key: string) => {},
version: "",
};
@ -496,7 +500,8 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const addFlow = async (
newProject: Boolean,
flow?: FlowType
flow?: FlowType,
override?: boolean
): Promise<String | undefined> => {
if (newProject) {
let flowData = flow
@ -505,6 +510,15 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
// Create a new flow with a default name if no flow is provided.
if (override) {
deleteComponent(flow!.name);
const newFlow = createNewFlow(flowData, flow!);
const { id } = await saveFlowToDatabase(newFlow);
newFlow.id = id;
addFlowToLocalState(newFlow);
return;
}
const newFlow = createNewFlow(flowData, flow!);
const newName = addVersionToDuplicates(newFlow, flows);
@ -642,9 +656,9 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}
}
function saveComponent(component: NodeDataType) {
function saveComponent(component: NodeDataType, override: boolean) {
component.node!.official = false;
return addFlow(true, createFlowComponent(component, version));
return addFlow(true, createFlowComponent(component, version), override);
}
function deleteComponent(key: string) {

View file

@ -13,7 +13,10 @@ import {
// import "ace-builds/webpack-resolver";
import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG, LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import {
EXPORT_CODE_DIALOG,
LANGFLOW_SUPPORTED_TYPES,
} from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { TemplateVariableType } from "../../types/api";
@ -99,7 +102,9 @@ const ApiModal = forwardRef(
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
(LANGFLOW_SUPPORTED_TYPES.has(node.data.node.template[templateField].type))
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type
)
)
.map((n, i) => {
arrNodesWithValues.push(node["id"]);

View file

@ -36,10 +36,15 @@ function ConfirmationModal({
size,
open,
onClose,
onCancel,
}: ConfirmationModalType) {
const Icon: any = nodeIconsLucide[icon];
const [modalOpen, setModalOpen] = useState(open ?? false);
useEffect(() => {
if (open) setModalOpen(open);
}, [open]);
useEffect(() => {
if (onClose) onClose!(modalOpen);
}, [modalOpen]);
@ -86,6 +91,7 @@ function ConfirmationModal({
className="mt-5"
variant="outline"
onClick={() => {
if (onCancel) onCancel();
setModalOpen(false);
}}
>

View file

@ -25,7 +25,10 @@ import {
TableHeader,
TableRow,
} from "../../components/ui/table";
import { LANGFLOW_SUPPORTED_TYPES, limitScrollFieldsModal } from "../../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
limitScrollFieldsModal,
} from "../../constants/constants";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
@ -145,7 +148,9 @@ const EditNodeModal = forwardRef(
(templateParam) =>
templateParam.charAt(0) !== "_" &&
myData.current.node?.template[templateParam].show &&
(LANGFLOW_SUPPORTED_TYPES.has(myData.current.node.template[templateParam].type))
LANGFLOW_SUPPORTED_TYPES.has(
myData.current.node.template[templateParam].type
)
)
.map((templateParam, index) => (
<TableRow key={index} className="h-10">

View file

@ -6,6 +6,7 @@ import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { StoreContext } from "../../contexts/storeContext";
import {
getStoreComponents,
getStoreTags,
@ -15,7 +16,6 @@ import { FlowType } from "../../types/flow";
import { removeApiKeys } from "../../utils/reactflowUtils";
import { getTagsIds } from "../../utils/storeUtils";
import BaseModal from "../baseModal";
import { StoreContext } from "../../contexts/storeContext";
export default function ShareModal({
component,
@ -23,7 +23,7 @@ export default function ShareModal({
children,
open,
setOpen,
disabled
disabled,
}: {
children?: ReactNode;
is_component: boolean;
@ -33,7 +33,7 @@ export default function ShareModal({
disabled?: boolean;
}): JSX.Element {
const { version, addFlow } = useContext(FlowsContext);
const {hasApiKey} = useContext(StoreContext)
const { hasApiKey } = useContext(StoreContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const [checked, setChecked] = useState(true);
const [name, setName] = useState(component?.name ?? "");
@ -50,12 +50,12 @@ export default function ShareModal({
useEffect(() => {
if (open || internalOpen) {
if(hasApiKey){
if (hasApiKey) {
handleGetTags();
handleGetNames();
}
}
}, [open, internalOpen,hasApiKey]);
}, [open, internalOpen, hasApiKey]);
function handleGetTags() {
setLoadingTags(true);

View file

@ -324,7 +324,7 @@ export default function Page({
// Calculate the position where the node should be created
const position = reactFlowInstance!.screenToFlowPosition({
x: event.clientX,
y: event.clientY
y: event.clientY,
});
// Generate a unique node ID

View file

@ -6,6 +6,7 @@ import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { StoreContext } from "../../../../contexts/storeContext";
import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal";
@ -23,14 +24,13 @@ import {
} from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent";
import SidebarDraggableComponent from "./sideBarDraggableComponent";
import { StoreContext } from "../../../../contexts/storeContext";
export default function ExtraSidebar(): JSX.Element {
const { data, templates, getFilterEdge, setFilterEdge, reactFlowInstance } =
useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, version } =
useContext(FlowsContext);
const {hasApiKey} = useContext(StoreContext)
const { hasApiKey } = useContext(StoreContext);
const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
@ -50,8 +50,6 @@ export default function ExtraSidebar(): JSX.Element {
event.dataTransfer.setData("nodedata", JSON.stringify(data));
}
// Handle showing components after use search input
function handleSearchInput(e: string) {
if (e === "") {
@ -199,7 +197,10 @@ export default function ExtraSidebar(): JSX.Element {
() => (
<ExportModal>
<ShadTooltip content="Export" side="top">
<button disabled={!hasApiKey} className={classNames("extra-side-bar-buttons")}>
<button
disabled={!hasApiKey}
className={classNames("extra-side-bar-buttons")}
>
<IconComponent name="FileDown" className="side-bar-button-size" />
</button>
</ShadTooltip>

View file

@ -93,12 +93,11 @@ export default function SidebarDraggableComponent({
>
<span className="side-bar-components-text">{display_name}</span>
<div>
<IconComponent
name="Menu"
className="side-bar-components-icon "
/>
<SelectTrigger>
</SelectTrigger>
<IconComponent
name="Menu"
className="side-bar-components-icon "
/>
<SelectTrigger></SelectTrigger>
<SelectContent>
<SelectItem value={"download"}>
<div className="flex">

View file

@ -10,6 +10,8 @@ import {
SelectTrigger,
} from "../../../../components/ui/select-custom";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { StoreContext } from "../../../../contexts/storeContext";
import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal";
import { nodeToolbarPropsType } from "../../../../types/components";
@ -21,7 +23,6 @@ import {
updateFlowPosition,
} from "../../../../utils/reactflowUtils";
import { classNames } from "../../../../utils/utils";
import { StoreContext } from "../../../../contexts/storeContext";
export default function NodeToolbarComponent({
data,
@ -51,7 +52,7 @@ export default function NodeToolbarComponent({
);
const updateNodeInternals = useUpdateNodeInternals();
const { getNodeId } = useContext(FlowsContext);
const {hasApiKey} = useContext(StoreContext)
const { hasApiKey } = useContext(StoreContext);
function canMinimize() {
let countHandles: number = 0;
@ -64,11 +65,12 @@ export default function NodeToolbarComponent({
const isMinimal = canMinimize();
const isGroup = data.node?.flow ? true : false;
const { paste, saveComponent, version } = useContext(FlowsContext);
const { paste, saveComponent, version, flows } = useContext(FlowsContext);
const reactFlowInstance = useReactFlow();
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [selectedValue, setSelectedValue] = useState("");
const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>();
@ -76,6 +78,11 @@ export default function NodeToolbarComponent({
setFlowComponent(createFlowComponent(cloneDeep(data), version));
}, [data]);
function onOverrideModalConfirmation(override: boolean): void {
if (override) saveComponent(cloneDeep(data), true);
saveComponent(cloneDeep(data), false);
}
const handleSelectChange = (event) => {
switch (event) {
case "advanced":
@ -89,10 +96,10 @@ export default function NodeToolbarComponent({
downloadNode(createFlowComponent(cloneDeep(data), version));
break;
case "Share":
if(hasApiKey) setShowconfirmShare(true);
if (hasApiKey) setShowconfirmShare(true);
break;
case "SaveAll":
saveComponent(cloneDeep(data));
saveComponent(cloneDeep(data), false);
break;
case "disabled":
break;
@ -100,9 +107,16 @@ export default function NodeToolbarComponent({
updateFlowPosition(position, data.node?.flow!);
expandGroupNode(data, reactFlowInstance, getNodeId);
break;
case "override":
setShowOverrideModal(true);
break;
}
};
const isSaved = flows.some((flow) =>
Object.values(flow).includes(data.node?.display_name!)
);
return (
<>
<div className="w-26 h-10">
@ -204,24 +218,38 @@ export default function NodeToolbarComponent({
</SelectItem>
)}
<SelectItem value={"SaveAll"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
{hasApiKey && <SelectItem value={"Share"}>
<div className="flex">
<IconComponent
name="Share2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Share{" "}
</div>{" "}
</SelectItem>}
{isSaved ? (
<SelectItem value={"override"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
) : (
<SelectItem value={"SaveAll"}>
<div className="flex">
<IconComponent
name="SaveAll"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Save{" "}
</div>{" "}
</SelectItem>
)}
{hasApiKey && (
<SelectItem value={"Share"}>
<div className="flex">
<IconComponent
name="Share2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Share{" "}
</div>{" "}
</SelectItem>
)}
<SelectItem value={"Download"}>
<div className="flex">
<IconComponent
@ -255,6 +283,32 @@ export default function NodeToolbarComponent({
)}
</SelectContent>
</Select>
<ConfirmationModal
asChild
open={showOverrideModal}
title={`Replace ${data.node?.display_name}`}
titleHeader={`Please, confirm your save actions`}
modalContentTitle="Attention!"
cancelText="New"
confirmationText="Replace"
icon={"SaveAll"}
index={6}
onConfirm={(index, user) => {
saveComponent(cloneDeep(data), true);
}}
onClose={setShowOverrideModal}
onCancel={() => saveComponent(cloneDeep(data), false)}
>
<ConfirmationModal.Content>
<span>
It seems {data.node?.display_name} already exists. Replacing it
will switch the current component. Proceed with replacement?
</span>
</ConfirmationModal.Content>
<ConfirmationModal.Trigger>
</ConfirmationModal.Trigger>
</ConfirmationModal>
<EditNodeModal
data={data}
setData={setData}

View file

@ -280,6 +280,7 @@ export type PaginatorComponentType = {
};
export type ConfirmationModalType = {
onCancel?: () => void;
title: string;
titleHeader: string;
asChild?: boolean;

View file

@ -10,7 +10,8 @@ export type FlowsContextType = {
removeFlow: (id: string) => void;
addFlow: (
newProject: boolean,
flow?: FlowType
flow?: FlowType,
override?: boolean
) => Promise<String | undefined>;
updateFlow: (newFlow: FlowType) => void;
incrementNodeId: () => string;
@ -44,7 +45,10 @@ export type FlowsContextType = {
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
setTweak: (tweak: tweakType) => tweakType | void;
getTweak: tweakType;
saveComponent: (component: NodeDataType) => Promise<String | undefined>;
saveComponent: (
component: NodeDataType,
override: boolean
) => Promise<String | undefined>;
deleteComponent: (key: string) => void;
version: string;
};

View file

@ -9,7 +9,10 @@ import {
XYPosition,
} from "reactflow";
import ShortUniqueId from "short-unique-id";
import { LANGFLOW_SUPPORTED_TYPES, specialCharsRegex } from "../constants/constants";
import {
LANGFLOW_SUPPORTED_TYPES,
specialCharsRegex,
} from "../constants/constants";
import { APITemplateType, TemplateVariableType } from "../types/api";
import {
FlowType,