merge dev on playground
This commit is contained in:
commit
101a045cba
90 changed files with 1802 additions and 1114 deletions
|
|
@ -63,9 +63,10 @@ export default function App() {
|
|||
}, [dark]);
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
const isLoginPage = location.pathname.includes("login");
|
||||
|
||||
autoLogin()
|
||||
autoLogin(abortController.signal)
|
||||
.then(async (user) => {
|
||||
if (user && user["access_token"]) {
|
||||
user["refresh_token"] = "auto";
|
||||
|
|
@ -76,31 +77,44 @@ export default function App() {
|
|||
await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
|
||||
}
|
||||
})
|
||||
.catch(async () => {
|
||||
setAutoLogin(false);
|
||||
if (isAuthenticated && !isLoginPage) {
|
||||
getUser();
|
||||
await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
|
||||
} else {
|
||||
setLoading(false);
|
||||
useFlowsManagerStore.setState({ isLoading: false });
|
||||
.catch(async (error) => {
|
||||
if (error.name !== "CanceledError") {
|
||||
setAutoLogin(false);
|
||||
if (isAuthenticated && !isLoginPage) {
|
||||
getUser();
|
||||
await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
|
||||
} else {
|
||||
setLoading(false);
|
||||
useFlowsManagerStore.setState({ isLoading: false });
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [isAuthenticated]);
|
||||
|
||||
/*
|
||||
Abort the request as it isn't needed anymore, the component being
|
||||
unmounted. It helps avoid, among other things, the well-known "can't
|
||||
perform a React state update on an unmounted component" warning.
|
||||
*/
|
||||
return () => abortController.abort();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
if (isAuthenticated) {
|
||||
try {
|
||||
await getTypes();
|
||||
refreshFlows();
|
||||
const res = await getGlobalVariables();
|
||||
setGlobalVariables(res);
|
||||
checkHasStore();
|
||||
fetchApiData();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch data:", error);
|
||||
return new Promise<void>(async (resolve, reject) => {
|
||||
if (isAuthenticated) {
|
||||
try {
|
||||
await getTypes();
|
||||
await refreshFlows();
|
||||
const res = await getGlobalVariables();
|
||||
setGlobalVariables(res);
|
||||
checkHasStore();
|
||||
fetchApiData();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch data:", error);
|
||||
reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ import { classNames } from "../../utils/utils";
|
|||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import DictComponent from "../dictComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import InputGlobalComponent from "../inputGlobalComponent";
|
||||
import KeypairListComponent from "../keypairListComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
|
||||
export default function CodeTabsComponent({
|
||||
flow,
|
||||
|
|
@ -351,31 +351,38 @@ export default function CodeTabsComponent({
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<InputGlobalComponent
|
||||
<InputComponent
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
password={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].password ?? false
|
||||
}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
if (node.data) {
|
||||
setNode(
|
||||
node.data.id,
|
||||
(oldNode) => {
|
||||
let newNode =
|
||||
cloneDeep(
|
||||
oldNode
|
||||
);
|
||||
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
|
||||
newNode.data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
);
|
||||
}
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
|
|
@ -384,25 +391,6 @@ export default function CodeTabsComponent({
|
|||
]
|
||||
);
|
||||
}}
|
||||
setDb={(value) => {
|
||||
setNode(
|
||||
node.data.id,
|
||||
(oldNode) => {
|
||||
let newNode =
|
||||
cloneDeep(oldNode);
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
newNode.data.node.template[
|
||||
templateField
|
||||
].load_from_db =
|
||||
value;
|
||||
return newNode;
|
||||
}
|
||||
);
|
||||
}}
|
||||
name={templateField}
|
||||
data={node.data}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { nodeIconsLucide } from "../../utils/styleUtils";
|
|||
import { cn } from "../../utils/utils";
|
||||
import Loading from "../ui/loading";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const ForwardedIconComponent = memo(
|
||||
forwardRef(
|
||||
(
|
||||
|
|
@ -18,9 +20,18 @@ const ForwardedIconComponent = memo(
|
|||
}: IconComponentProps,
|
||||
ref
|
||||
) => {
|
||||
const [showFallback, setShowFallback] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setShowFallback(true);
|
||||
}, 30);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
let TargetIcon = nodeIconsLucide[name];
|
||||
if (!TargetIcon) {
|
||||
// check if name exists in dynamicIconImports
|
||||
if (!dynamicIconImports[name]) {
|
||||
TargetIcon = nodeIconsLucide["unknown"];
|
||||
} else TargetIcon = lazy(dynamicIconImports[name]);
|
||||
|
|
@ -35,11 +46,15 @@ const ForwardedIconComponent = memo(
|
|||
if (!TargetIcon) {
|
||||
return null; // Render nothing until the icon is loaded
|
||||
}
|
||||
const fallback = (
|
||||
|
||||
const fallback = showFallback ? (
|
||||
<div className={cn(className, "flex items-center justify-center")}>
|
||||
<Loading />
|
||||
</div>
|
||||
) : (
|
||||
<div className={className}></div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Suspense fallback={fallback}>
|
||||
<TargetIcon
|
||||
|
|
|
|||
|
|
@ -406,9 +406,11 @@ export async function onLogin(user: LoginType) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function autoLogin() {
|
||||
export async function autoLogin(abortSignal) {
|
||||
try {
|
||||
const response = await api.get(`${BASE_URL_API}auto_login`);
|
||||
const response = await api.get(`${BASE_URL_API}auto_login`, {
|
||||
signal: abortSignal,
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = response.data;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
import { getTagsIds } from "../../utils/storeUtils";
|
||||
import ConfirmationModal from "../ConfirmationModal";
|
||||
import BaseModal from "../baseModal";
|
||||
import ExportModal from "../exportModal";
|
||||
|
||||
export default function ShareModal({
|
||||
component,
|
||||
|
|
@ -206,9 +207,8 @@ export default function ShareModal({
|
|||
{children ? children : <></>}
|
||||
</BaseModal.Trigger>
|
||||
<BaseModal.Header
|
||||
description={`Publish ${
|
||||
is_component ? "your component" : "workflow"
|
||||
} to the Langflow Store.`}
|
||||
description={`Publish ${is_component ? "your component" : "workflow"
|
||||
} to the Langflow Store.`}
|
||||
>
|
||||
<span className="pr-2">Share</span>
|
||||
<IconComponent
|
||||
|
|
@ -251,18 +251,34 @@ export default function ShareModal({
|
|||
|
||||
<BaseModal.Footer>
|
||||
<div className="flex w-full justify-between gap-2">
|
||||
<Button
|
||||
{!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={() => {
|
||||
handleExportComponent();
|
||||
(setOpen || internalSetOpen)(false);
|
||||
handleExportComponent();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Download" className="h-4 w-4" />
|
||||
Export
|
||||
</Button>
|
||||
|
||||
}
|
||||
<Button
|
||||
disabled={loadingNames}
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import PaginatorComponent from "../../../../components/PaginatorComponent";
|
||||
import CollectionCardComponent from "../../../../components/cardComponent";
|
||||
|
|
@ -23,42 +23,36 @@ export default function ComponentsComponent({
|
|||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||
const removeFlow = useFlowsManagerStore((state) => state.removeFlow);
|
||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||
const setExamples = useFlowsManagerStore((state) => state.setExamples);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [pageIndex, setPageIndex] = useState(1);
|
||||
const [loadingScreen, setLoadingScreen] = useState(true);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) return;
|
||||
let all = flows
|
||||
.filter((f) => (f.is_component ?? false) === is_component)
|
||||
.sort((a, b) => {
|
||||
if (a?.updated_at && b?.updated_at) {
|
||||
return (
|
||||
new Date(b?.updated_at!).getTime() -
|
||||
new Date(a?.updated_at!).getTime()
|
||||
);
|
||||
} else if (a?.updated_at && !b?.updated_at) {
|
||||
return 1;
|
||||
} else if (!a?.updated_at && b?.updated_at) {
|
||||
return -1;
|
||||
} else {
|
||||
return (
|
||||
new Date(b?.date_created!).getTime() -
|
||||
new Date(a?.date_created!).getTime()
|
||||
);
|
||||
}
|
||||
});
|
||||
const start = (pageIndex - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
setData(all.slice(start, end));
|
||||
}, [flows,isLoading, pageIndex, pageSize]);
|
||||
|
||||
const [data, setData] = useState<FlowType[]>([]);
|
||||
const all: FlowType[] = flows
|
||||
.filter((f) => (f.is_component ?? false) === is_component)
|
||||
.sort((a, b) => {
|
||||
if (a?.updated_at && b?.updated_at) {
|
||||
return (
|
||||
new Date(b?.updated_at!).getTime() -
|
||||
new Date(a?.updated_at!).getTime()
|
||||
);
|
||||
} else if (a?.updated_at && !b?.updated_at) {
|
||||
return 1;
|
||||
} else if (!a?.updated_at && b?.updated_at) {
|
||||
return -1;
|
||||
} else {
|
||||
return (
|
||||
new Date(b?.date_created!).getTime() -
|
||||
new Date(a?.date_created!).getTime()
|
||||
);
|
||||
}
|
||||
});
|
||||
const start = (pageIndex - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const data: FlowType[] = all.slice(start, end);
|
||||
|
||||
const name = is_component ? "Component" : "Flow";
|
||||
|
||||
|
|
@ -73,8 +67,9 @@ export default function ComponentsComponent({
|
|||
})
|
||||
.then(() => {
|
||||
setSuccessData({
|
||||
title: `${is_component ? "Component" : "Flow"
|
||||
} uploaded successfully`,
|
||||
title: `${
|
||||
is_component ? "Component" : "Flow"
|
||||
} uploaded successfully`,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -139,18 +134,46 @@ export default function ComponentsComponent({
|
|||
onDelete={() => {
|
||||
removeFlow(item.id);
|
||||
setSuccessData({
|
||||
title: `${item.is_component ? "Component" : "Flow"
|
||||
} deleted successfully!`,
|
||||
title: `${
|
||||
item.is_component ? "Component" : "Flow"
|
||||
} deleted successfully!`,
|
||||
});
|
||||
resetFilter();
|
||||
}}
|
||||
key={idx}
|
||||
data={item}
|
||||
data-testid={
|
||||
"edit-flow-button-" + item.id + "-" + idx
|
||||
}
|
||||
onClick={!is_component ? () => {navigate("/flow/" + item.id);} : undefined}
|
||||
data={{ is_component: item.is_component ?? false, ...item }}
|
||||
disabled={isLoading}
|
||||
data-testid={"edit-flow-button-" + item.id + "-" + idx}
|
||||
button={
|
||||
!is_component ? (
|
||||
<Link to={"/flow/" + item.id}>
|
||||
<Button
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
data-testid={
|
||||
"edit-flow-button-" + item.id + "-" + idx
|
||||
}
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button select-none"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
onClick={
|
||||
!is_component
|
||||
? () => {
|
||||
navigate("/flow/" + item.id);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
playground={!is_component}
|
||||
/>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useEffect } from "react";
|
||||
import { Route, Routes, useNavigate } from "react-router-dom";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { ProtectedAdminRoute } from "./components/authAdminGuard";
|
||||
import { ProtectedRoute } from "./components/authGuard";
|
||||
import { ProtectedLoginRoute } from "./components/authLoginGuard";
|
||||
|
|
@ -20,13 +19,6 @@ import SignUp from "./pages/signUpPage";
|
|||
import PlaygroundPage from "./pages/Playground";
|
||||
|
||||
const Router = () => {
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
// Redirect from root to /flows
|
||||
if (window.location.pathname === "/") {
|
||||
navigate("/flows");
|
||||
}
|
||||
}, [navigate]);
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
|
|
@ -37,6 +29,7 @@ const Router = () => {
|
|||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<Navigate replace to={"flows"} />} />
|
||||
<Route
|
||||
path="flows"
|
||||
element={<ComponentsComponent key="flows" is_component={false} />}
|
||||
|
|
|
|||
|
|
@ -481,8 +481,13 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
// const nextVertices will be the zip of vertexBuildData.next_vertices_ids and
|
||||
// vertexBuildData.top_level_vertices
|
||||
// the VertexLayerElementType as {id: next_vertices_id, layer: top_level_vertex}
|
||||
|
||||
// next_vertices_ids should be next_vertices_ids without the inactivated vertices
|
||||
const next_vertices_ids = vertexBuildData.next_vertices_ids.filter(
|
||||
(id) => !vertexBuildData.inactivated_vertices?.includes(id)
|
||||
);
|
||||
const nextVertices: VertexLayerElementType[] = zip(
|
||||
vertexBuildData.next_vertices_ids,
|
||||
next_vertices_ids,
|
||||
vertexBuildData.top_level_vertices
|
||||
).map(([id, reference]) => ({ id: id!, reference }));
|
||||
|
||||
|
|
@ -492,7 +497,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
];
|
||||
const newIds = [
|
||||
...get().verticesBuild!.verticesIds,
|
||||
...vertexBuildData.next_vertices_ids,
|
||||
...next_vertices_ids,
|
||||
];
|
||||
get().updateVerticesBuild({
|
||||
verticesIds: newIds,
|
||||
|
|
@ -603,7 +608,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
set({
|
||||
verticesBuild: {
|
||||
...verticesBuild,
|
||||
// remove the vertices from the list of vertices ids
|
||||
// that are going to be built
|
||||
verticesIds: get().verticesBuild!.verticesIds.filter(
|
||||
// keep the vertices that are not in the list of vertices to remove
|
||||
(vertex) => !vertices.includes(vertex)
|
||||
),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@
|
|||
muted-foreground is too strong, maybe use a lighter shade of it?
|
||||
|
||||
*/
|
||||
@apply border-none ring ring-muted-foreground;
|
||||
@apply border-none ring grayscale;
|
||||
}
|
||||
.built-invalid-status {
|
||||
@apply border-none ring ring-[#FF9090];
|
||||
|
|
|
|||
|
|
@ -175,14 +175,26 @@ export async function buildVertices({
|
|||
!useFlowStore
|
||||
.getState()
|
||||
.verticesBuild?.verticesIds.includes(element.id) &&
|
||||
!useFlowStore
|
||||
.getState()
|
||||
.verticesBuild?.verticesIds.includes(element.reference ?? "") &&
|
||||
onBuildUpdate
|
||||
) {
|
||||
// If it is, skip building and set the state to inactive
|
||||
onBuildUpdate(
|
||||
getInactiveVertexData(element.id),
|
||||
BuildStatus.INACTIVE,
|
||||
runId
|
||||
);
|
||||
if (element.id) {
|
||||
onBuildUpdate(
|
||||
getInactiveVertexData(element.id),
|
||||
BuildStatus.INACTIVE,
|
||||
runId
|
||||
);
|
||||
}
|
||||
if (element.reference) {
|
||||
onBuildUpdate(
|
||||
getInactiveVertexData(element.reference),
|
||||
BuildStatus.INACTIVE,
|
||||
runId
|
||||
);
|
||||
}
|
||||
buildResults.push(false);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue