merge dev on playground

This commit is contained in:
cristhianzl 2024-05-02 13:59:52 -03:00
commit 101a045cba
90 changed files with 1802 additions and 1114 deletions

View file

@ -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(() => {

View file

@ -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>

View file

@ -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

View file

@ -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;

View file

@ -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"

View file

@ -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}
/>
))

View file

@ -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} />}

View file

@ -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)
),
},

View file

@ -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];

View file

@ -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;
}