Spaces:
Running
Running
import useSaveFlow from "@/hooks/flows/use-save-flow"; | |
import { useUtilityStore } from "@/stores/utilityStore"; | |
import { cloneDeep } from "lodash"; | |
import { ReactNode, useEffect, useMemo, useState } from "react"; | |
import IconComponent from "../../components/common/genericIconComponent"; | |
import { TagsSelector } from "../../components/common/tagsSelectorComponent"; | |
import EditFlowSettings from "../../components/core/editFlowSettingsComponent"; | |
import { Button } from "../../components/ui/button"; | |
import { Checkbox } from "../../components/ui/checkbox"; | |
import { | |
getStoreComponents, | |
saveFlowStore, | |
updateFlowStore, | |
} from "../../controllers/API"; | |
import useAlertStore from "../../stores/alertStore"; | |
import { useDarkStore } from "../../stores/darkStore"; | |
import { useStoreStore } from "../../stores/storeStore"; | |
import { FlowType } from "../../types/flow"; | |
import { | |
downloadNode, | |
removeApiKeys, | |
removeFileNameFromComponents, | |
} from "../../utils/reactflowUtils"; | |
import BaseModal from "../baseModal"; | |
import ConfirmationModal from "../confirmationModal"; | |
import ExportModal from "../exportModal"; | |
import getTagsIds from "./utils/get-tags-ids"; | |
export default function ShareModal({ | |
component, | |
is_component, | |
children, | |
open, | |
setOpen, | |
disabled, | |
}: { | |
children?: ReactNode; | |
is_component: boolean; | |
component: FlowType; | |
open?: boolean; | |
setOpen?: (open: boolean) => void; | |
disabled?: boolean; | |
}): JSX.Element { | |
const version = useDarkStore((state) => state.version); | |
const hasStore = useStoreStore((state) => state.hasStore); | |
const hasApiKey = useStoreStore((state) => state.hasApiKey); | |
const setSuccessData = useAlertStore((state) => state.setSuccessData); | |
const setErrorData = useAlertStore((state) => state.setErrorData); | |
const [internalOpen, internalSetOpen] = | |
setOpen !== undefined && open !== undefined | |
? [open, setOpen] | |
: useState(children ? false : true); | |
const [openConfirmationModal, setOpenConfirmationModal] = useState(false); | |
const nameComponent = is_component ? "component" : "workflow"; | |
const [sharePublic, setSharePublic] = useState(true); | |
const [selectedTags, setSelectedTags] = useState<string[]>([]); | |
const [unavaliableNames, setUnavaliableNames] = useState< | |
{ id: string; name: string }[] | |
>([]); | |
const saveFlow = useSaveFlow(); | |
const tags = useUtilityStore((state) => state.tags); | |
const [loadingNames, setLoadingNames] = useState(false); | |
const name = component?.name ?? ""; | |
const description = component?.description ?? ""; | |
useEffect(() => { | |
if (internalOpen) { | |
if (hasApiKey && hasStore) { | |
handleGetNames(); | |
} | |
} | |
}, [internalOpen, hasApiKey, hasStore]); | |
async function handleGetNames() { | |
setLoadingNames(true); | |
const unavaliableNames: Array<{ id: string; name: string }> = []; | |
await getStoreComponents({ | |
fields: ["name", "id", "is_component"], | |
filterByUser: true, | |
}).then((res) => { | |
res?.results?.forEach((element: any) => { | |
if ((element.is_component ?? false) === is_component) | |
unavaliableNames.push({ name: element.name, id: element.id }); | |
}); | |
setUnavaliableNames(unavaliableNames); | |
setLoadingNames(false); | |
}); | |
} | |
const handleShareComponent = async (update = false) => { | |
//remove file names from flows before sharing | |
removeFileNameFromComponents(component); | |
const flow: FlowType = removeApiKeys({ | |
id: component!.id, | |
data: component!.data, | |
description, | |
name, | |
last_tested_version: version, | |
is_component: is_component, | |
}); | |
function successShare() { | |
if (!is_component) { | |
saveFlow(flow); | |
} | |
setSuccessData({ | |
title: `${is_component ? "Component" : "Flow"} shared successfully!`, | |
}); | |
} | |
if (!update) | |
saveFlowStore( | |
flow!, | |
getTagsIds(selectedTags, cloneDeep(tags) ?? []), | |
sharePublic, | |
).then(successShare, (err) => { | |
setErrorData({ | |
title: "Error sharing " + (is_component ? "component" : "flow"), | |
list: [err["response"]["data"]["detail"]], | |
}); | |
}); | |
else | |
updateFlowStore( | |
flow!, | |
getTagsIds(selectedTags, cloneDeep(tags) ?? []), | |
sharePublic, | |
unavaliableNames.find((e) => e.name === name)!.id, | |
).then(successShare, (err) => { | |
setErrorData({ | |
title: "Error sharing " + is_component ? "component" : "flow", | |
list: [err["response"]["data"]["detail"]], | |
}); | |
}); | |
}; | |
const handleUpdateComponent = () => { | |
handleShareComponent(true); | |
internalSetOpen(false); | |
}; | |
const handleExportComponent = () => { | |
component = removeApiKeys(component); | |
downloadNode(component); | |
}; | |
let modalConfirmation = useMemo(() => { | |
return ( | |
<> | |
<ConfirmationModal | |
open={openConfirmationModal} | |
title={`Replace`} | |
cancelText="Cancel" | |
confirmationText="Replace" | |
size={"x-small"} | |
icon={"SaveAll"} | |
index={6} | |
onConfirm={() => { | |
handleUpdateComponent(); | |
setOpenConfirmationModal(false); | |
}} | |
onCancel={() => { | |
setOpenConfirmationModal(false); | |
}} | |
> | |
<ConfirmationModal.Content> | |
<span> | |
It seems {name} already exists. Do you want to replace it with the | |
current? | |
</span> | |
<br></br> | |
<span className="text-xs text-destructive"> | |
Note: This action is irreversible. | |
</span> | |
</ConfirmationModal.Content> | |
</ConfirmationModal> | |
</> | |
); | |
}, [ | |
unavaliableNames, | |
name, | |
loadingNames, | |
handleShareComponent, | |
openConfirmationModal, | |
]); | |
return ( | |
<> | |
<BaseModal | |
size="smaller-h-full" | |
open={!disabled && internalOpen} | |
setOpen={internalSetOpen} | |
onSubmit={() => { | |
const isNameAvailable = !unavaliableNames.some( | |
(element) => element.name === name, | |
); | |
if (isNameAvailable) { | |
handleShareComponent(); | |
internalSetOpen(false); | |
} else { | |
setOpenConfirmationModal(true); | |
} | |
}} | |
> | |
<BaseModal.Trigger asChild> | |
{children ? children : <></>} | |
</BaseModal.Trigger> | |
<BaseModal.Header | |
description={`Publish ${ | |
is_component ? "your component" : "workflow" | |
} to the Langflow Store.`} | |
> | |
<span className="pr-2">Share</span> | |
<IconComponent | |
name="Share3" | |
className="-m-0.5 h-6 w-6 text-foreground" | |
aria-hidden="true" | |
/> | |
</BaseModal.Header> | |
<BaseModal.Content> | |
<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 ?? []} | |
loadingTags={false} | |
disabled={false} | |
selectedTags={selectedTags} | |
setSelectedTags={setSelectedTags} | |
/> | |
</div> | |
<div className="mb-2 mt-5 flex items-center space-x-2"> | |
<Checkbox | |
id="public" | |
checked={sharePublic} | |
onCheckedChange={(event: boolean) => { | |
setSharePublic(event); | |
}} | |
data-testid="public-checkbox" | |
/> | |
<label htmlFor="public" className="export-modal-save-api text-sm"> | |
Set {nameComponent} status to public | |
</label> | |
</div> | |
<span className="text-xs text-destructive"> | |
<b>Attention:</b> API keys in specified fields are automatically | |
removed upon sharing. | |
</span> | |
</BaseModal.Content> | |
<BaseModal.Footer | |
submit={{ | |
label: `Share ${is_component ? "Component" : "Flow"}`, | |
loading: loadingNames, | |
dataTestId: "share-modal-button-flow", | |
}} | |
> | |
<> | |
{!is_component && ( | |
<ExportModal> | |
<Button | |
type="button" | |
variant="outline" | |
className="gap-2" | |
onClick={() => { | |
// (setOpen || internalSetOpen)(false); | |
}} | |
> | |
<IconComponent name="Download" className="h-4 w-4" /> | |
Export | |
</Button> | |
</ExportModal> | |
)} | |
{is_component && ( | |
<Button | |
type="button" | |
variant="outline" | |
className="gap-2" | |
onClick={() => { | |
internalSetOpen(false); | |
handleExportComponent(); | |
}} | |
> | |
<IconComponent name="Download" className="h-4 w-4" /> | |
Export | |
</Button> | |
)} | |
</> | |
</BaseModal.Footer> | |
</BaseModal> | |
<>{modalConfirmation}</> | |
</> | |
); | |
} | |