Modularized shareModal to be used with components and flows
This commit is contained in:
parent
422e4bd5a0
commit
85dc34d8d7
7 changed files with 234 additions and 231 deletions
|
|
@ -12,8 +12,6 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
setInvalidName,
|
||||
description,
|
||||
maxLength = 50,
|
||||
flows,
|
||||
tabId,
|
||||
setName,
|
||||
setDescription,
|
||||
}: InputProps): JSX.Element => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,8 +40,6 @@ export default function FlowSettingsModal({
|
|||
setInvalidName={setInvalidName}
|
||||
name={name}
|
||||
description={description}
|
||||
flows={flows}
|
||||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
/>
|
||||
|
|
|
|||
211
src/frontend/src/modals/shareModal/index.tsx
Normal file
211
src/frontend/src/modals/shareModal/index.tsx
Normal 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;
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue