Modularized shareModal to be used with components and flows

This commit is contained in:
Lucas Oliveira 2023-11-18 22:09:41 -03:00
commit 85dc34d8d7
7 changed files with 234 additions and 231 deletions

View file

@ -12,8 +12,6 @@ export const EditFlowSettings: React.FC<InputProps> = ({
setInvalidName,
description,
maxLength = 50,
flows,
tabId,
setName,
setDescription,
}: InputProps): JSX.Element => {

View file

@ -40,8 +40,6 @@ const ExportModal = forwardRef(
<EditFlowSettings
name={name}
description={description}
flows={flows}
tabId={tabId}
setName={setName}
setDescription={setDescription}
/>
@ -74,6 +72,7 @@ const ExportModal = forwardRef(
description,
name,
last_tested_version: version,
is_component: false,
},
name!,
description
@ -90,6 +89,7 @@ const ExportModal = forwardRef(
description,
name,
last_tested_version: version,
is_component: false,
}),
name!,
description

View file

@ -40,8 +40,6 @@ export default function FlowSettingsModal({
setInvalidName={setInvalidName}
name={name}
description={description}
flows={flows}
tabId={tabId}
setName={setName}
setDescription={setDescription}
/>

View file

@ -0,0 +1,211 @@
import {
ReactNode,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent";
import { TagsSelector } from "../../components/tagsSelectorComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { getStoreTags, saveFlowStore } from "../../controllers/API";
import { FlowType } from "../../types/flow";
import { removeApiKeys } from "../../utils/reactflowUtils";
import { getTagsIds } from "../../utils/storeUtils";
import BaseModal from "../baseModal";
const ShareModal = forwardRef(
(
props: {
children?: ReactNode;
is_component: boolean;
component: FlowType;
open?: boolean;
setOpen?: (open: boolean) => void;
},
ref
): JSX.Element => {
const { version, addFlow } = useContext(FlowsContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const [checked, setChecked] = useState(true);
const [name, setName] = useState(props.component?.name ?? "");
const [description, setDescription] = useState(
props.component?.description ?? ""
);
const [open, setOpen] = useState(props.children ? false : true);
const nameComponent = props.is_component ? "Component" : "Flow";
const [tags, setTags] = useState<string[]>([]);
const [sharePublic, setSharePublic] = useState(true);
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
const tagListId = useRef<{ id: string; name: string }[]>([]);
useEffect(() => {
getStoreTags().then((res) => {
tagListId.current = res;
let tags = res.map((tag) => tag.name);
setTags(tags);
});
}, []);
useEffect(() => {
setName(props.component?.name ?? "");
setDescription(props.component?.description ?? "");
}, [props.component]);
function handleTagSelection(tag: string) {
setSelectedTags((prev) => {
const newSet = new Set(prev);
if (newSet.has(tag)) {
newSet.delete(tag);
} else {
newSet.add(tag);
}
return newSet;
});
}
const handleShareComponent = () => {
const saveFlow: FlowType = checked
? {
id: props.component!.id,
data: props.component!.data,
description,
name,
last_tested_version: version,
is_component: props.is_component,
}
: removeApiKeys({
id: props.component!.id,
data: props.component!.data,
description,
name,
last_tested_version: version,
is_component: props.is_component,
});
saveFlowStore(
saveFlow,
getTagsIds(Array.from(selectedTags), tagListId),
sharePublic
).then(
() => {
if (props.is_component) {
addFlow(true, saveFlow);
}
setSuccessData({
title: `${nameComponent} shared successfully`,
});
},
(err) => {
setErrorData({
title: "Error sharing flow",
list: [err["response"]["data"]["detail"]],
});
}
);
};
return (
<BaseModal
size="smaller-h-full"
open={props.open ?? open}
setOpen={props.setOpen ?? setOpen}
>
<BaseModal.Trigger>
{props.children ? props.children : <></>}
</BaseModal.Trigger>
<BaseModal.Header
description={`Share your ${nameComponent} to the Langflow Store`}
>
<span className="pr-2">Share</span>
<IconComponent
name="Share2"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
name={name}
description={description}
setName={setName}
setDescription={setDescription}
/>
<div className="mt-3 flex items-center space-x-2">
<Checkbox
id="terms"
checked={checked}
onCheckedChange={(event: boolean) => {
setChecked(event);
}}
/>
<label htmlFor="terms" className="export-modal-save-api text-sm ">
Save with my API keys
</label>
</div>
<span className=" text-xs text-destructive ">
Caution: Uncheck this box only removes API keys from fields
specifically designated for API keys.
</span>
<div className="flex h-full w-full flex-col gap-3">
<div className="flex justify-start pt-4 align-middle">
<ToggleShadComponent
disabled={false}
size="medium"
setEnabled={setSharePublic}
enabled={sharePublic}
/>
<div
className="cursor-pointer pl-1"
onClick={() => {
setSharePublic(!sharePublic);
}}
>
{sharePublic ? (
<span>
This flow will be avaliable <b>for everyone</b>
</span>
) : (
<span>
This flow will be avaliable <b>just for you</b>
</span>
)}
</div>
</div>
<div className="w-full pt-2">
<span className="text-sm">
Add some tags to your {nameComponent}
</span>
<TagsSelector
tags={tags}
selectedTags={selectedTags}
setSelectedTags={handleTagSelection}
/>
</div>
</div>
</BaseModal.Content>
<BaseModal.Footer>
<Button
onClick={() => {
handleShareComponent();
if (props.setOpen) props.setOpen(false);
else setOpen(false);
}}
type="button"
>
Share {nameComponent}
</Button>
</BaseModal.Footer>
</BaseModal>
);
}
);
export default ShareModal;

View file

@ -1,22 +1,16 @@
import { cloneDeep } from "lodash";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { ReactFlowJsonObject } from "reactflow";
import { useContext, useEffect, useMemo, useState } from "react";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { TagsSelector } from "../../../../components/tagsSelectorComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { getStoreTags, saveFlowStore } from "../../../../controllers/API";
import ApiModal from "../../../../modals/ApiModal";
import ConfirmationModal from "../../../../modals/ConfirmationModal";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
import { APIClassType, APIObjectType } from "../../../../types/api";
import { FlowType } from "../../../../types/flow";
import { getTagsIds } from "../../../../utils/storeUtils";
import {
nodeColors,
nodeIconsLucide,
@ -35,10 +29,9 @@ export default function ExtraSidebar(): JSX.Element {
useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, version } =
useContext(FlowsContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
const [sharePublic, setSharePublic] = useState(true);
const isPending = tabsState[tabId]?.isPending;
function onDragStart(
event: React.DragEvent<any>,
@ -55,30 +48,6 @@ export default function ExtraSidebar(): JSX.Element {
event.dataTransfer.setData("nodedata", JSON.stringify(data));
}
const [tags, setTags] = useState<string[]>([]);
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
const tagListId = useRef<{ id: string; name: string }[]>([]);
useEffect(() => {
getStoreTags().then((res) => {
tagListId.current = res;
let tags = res.map((tag) => tag.name);
setTags(tags);
});
}, []);
function handleTagSelection(tag: string) {
setSelectedTags((prev) => {
const newSet = new Set(prev);
if (newSet.has(tag)) {
newSet.delete(tag);
} else {
newSet.add(tag);
}
return newSet;
});
}
// Handle showing components after use search input
function handleSearchInput(e: string) {
if (e === "") {
@ -172,39 +141,6 @@ export default function ExtraSidebar(): JSX.Element {
}
}, [getFilterEdge]);
const handleShareFlow = () => {
const reactFlow = reactFlowInstance
? reactFlowInstance.toObject()
: (flow!.data as ReactFlowJsonObject);
const saveFlow: FlowType = {
name: flow!.name,
id: flow!.id,
description: flow!.description,
data: {
...reactFlow,
},
is_component: false,
last_tested_version: version,
};
saveFlowStore(
saveFlow,
getTagsIds(Array.from(selectedTags), tagListId),
sharePublic
).then(
() => {
setSuccessData({
title: "Flow shared successfully",
});
},
(err) => {
setErrorData({
title: "Error sharing flow",
list: [err["response"]["data"]["detail"]],
});
}
);
};
useEffect(() => {
if (getFilterEdge?.length > 0) {
setFilterData((_) => {
@ -244,63 +180,15 @@ export default function ExtraSidebar(): JSX.Element {
const ModalMemo = useMemo(
() => (
<ConfirmationModal
index={0}
modalContentTitle="Are you sure you want to share this flow?"
title="Share Flow"
confirmationText="Share"
icon="Share2"
size="small-h-full"
onConfirm={() => {
handleShareFlow();
}}
titleHeader=""
cancelText="Cancel"
>
<ConfirmationModal.Content>
<div className="flex h-full w-full flex-col gap-3">
<div className="flex justify-start pt-4 align-middle">
<ToggleShadComponent
disabled={false}
size="medium"
setEnabled={setSharePublic}
enabled={sharePublic}
/>
<div
className="cursor-pointer pl-1"
onClick={() => {
setSharePublic(!sharePublic);
}}
>
{sharePublic ? (
<span>
This flow will be avaliable <b>for everyone</b>
</span>
) : (
<span>
This flow will be avaliable <b>just for you</b>
</span>
)}
</div>
</div>
<div className="w-full pt-2">
<span className="text-sm">Add some tags to your Flow</span>
<TagsSelector
tags={tags}
selectedTags={selectedTags}
setSelectedTags={handleTagSelection}
/>
</div>
</div>
</ConfirmationModal.Content>
<ConfirmationModal.Trigger tolltipContent="Share" side="top">
<ShareModal is_component={false} component={flow!}>
<ShadTooltip content="Share" side="top">
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent name="Share2" className="side-bar-button-size" />
</div>
</ConfirmationModal.Trigger>
</ConfirmationModal>
</ShadTooltip>
</ShareModal>
),
[sharePublic, tags, selectedTags]
[]
);
const ExportMemo = useMemo(

View file

@ -1,29 +1,25 @@
import { cloneDeep } from "lodash";
import { useContext, useEffect, useRef, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { useReactFlow, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { TagsSelector } from "../../../../components/tagsSelectorComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "../../../../components/ui/select-custom";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { getStoreTags, saveFlowStore } from "../../../../controllers/API";
import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal";
import { nodeToolbarPropsType } from "../../../../types/components";
import { FlowType } from "../../../../types/flow";
import {
createFlowComponent,
downloadNode,
expandGroupNode,
updateFlowPosition,
} from "../../../../utils/reactflowUtils";
import { getTagsIds } from "../../../../utils/storeUtils";
import { classNames } from "../../../../utils/utils";
export default function NodeToolbarComponent({
@ -52,7 +48,6 @@ export default function NodeToolbarComponent({
);
const updateNodeInternals = useUpdateNodeInternals();
const { getNodeId } = useContext(FlowsContext);
const { setErrorData, setSuccessData } = useContext(alertContext);
function canMinimize() {
let countHandles: number = 0;
@ -70,53 +65,13 @@ export default function NodeToolbarComponent({
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [selectedValue, setSelectedValue] = useState("");
const [sharePublic, setSharePublic] = useState(true);
const [tags, setTags] = useState<string[]>([]);
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
const tagListId = useRef<{ id: string; name: string }[]>([]);
const [flowComponent, setFlowComponent] = useState<FlowType>();
useEffect(() => {
getStoreTags().then((res) => {
tagListId.current = res;
let tags = res.map((tag) => tag.name);
setTags(tags);
});
}, []);
setFlowComponent(createFlowComponent(data, version));
}, [data]);
function handleTagSelection(tag: string) {
setSelectedTags((prev) => {
const newSet = new Set(prev);
if (newSet.has(tag)) {
newSet.delete(tag);
} else {
newSet.add(tag);
}
return newSet;
});
}
function handleShareComponent() {
const componentFlow = cloneDeep(data);
saveComponent(componentFlow).then(() => {
saveFlowStore(
createFlowComponent(componentFlow, version),
getTagsIds(Array.from(selectedTags), tagListId),
sharePublic
).then(
(_) => {
setSuccessData({
title: "Component shared successfully",
});
},
(err) => {
setErrorData({
title: "Error sharing component",
list: [err["response"]["data"]["detail"]],
});
}
);
});
}
const handleSelectChange = (event) => {
switch (event) {
case "advanced":
@ -306,57 +261,12 @@ export default function NodeToolbarComponent({
<></>
</EditNodeModal>
)}
{showconfirmShare && (
<ConfirmationModal
key={data.id}
index={0}
size="smaller"
modalContentTitle="Are you sure you want to share this component?"
title="Share Component"
confirmationText="Share"
icon="Share2"
onConfirm={() => {
handleShareComponent();
}}
titleHeader=""
cancelText="Cancel"
open={showconfirmShare}
onClose={(modal) => {
setShowconfirmShare(modal);
}}
>
<ConfirmationModal.Content>
<div className="flex h-full w-full flex-col gap-7">
<div className="flex justify-start align-middle">
<ToggleShadComponent
disabled={false}
size="medium"
setEnabled={setSharePublic}
enabled={sharePublic}
/>
<div>
{sharePublic
? "This component will be avaliable for everyone"
: "This component will be avaliable just for you"}
</div>
</div>
<div className="w-full pt-2">
<span className="text-sm">
Add some tags to your component
</span>
<TagsSelector
tags={tags}
selectedTags={selectedTags}
setSelectedTags={handleTagSelection}
/>
</div>
</div>{" "}
</ConfirmationModal.Content>
<ConfirmationModal.Trigger>
<div></div>
</ConfirmationModal.Trigger>
</ConfirmationModal>
)}
<ShareModal
open={showconfirmShare}
setOpen={setShowconfirmShare}
is_component={true}
component={flowComponent!}
/>
</span>
</div>
</>

View file

@ -223,8 +223,6 @@ export type InputProps = {
name: string | null;
description: string | null;
maxLength?: number;
flows: Array<{ id: string; name: string; description: string }>;
tabId: string;
invalidName?: boolean;
setName: (name: string) => void;
setDescription: (description: string) => void;