diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index 9021b40b6..b8fa48577 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import Request, Response, APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from sqlmodel import Session @@ -16,6 +16,7 @@ router = APIRouter(tags=["Login"]) @router.post("/login", response_model=Token) async def login_to_get_access_token( + response: Response, form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_session), # _: Session = Depends(get_current_active_user) @@ -31,7 +32,10 @@ async def login_to_get_access_token( ) from exc if user: - return create_user_tokens(user_id=user.id, db=db, update_last_login=True) + tokens = create_user_tokens(user_id=user.id, db=db, update_last_login=True) + response.set_cookie("refresh_token_lf", tokens["refresh_token"], httponly=True, secure=True, samesite="strict") + response.set_cookie("access_token_lf", tokens["access_token"], httponly=False, secure=True, samesite="strict") + return tokens else: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -41,9 +45,13 @@ async def login_to_get_access_token( @router.get("/auto_login") -async def auto_login(db: Session = Depends(get_session), settings_service=Depends(get_settings_service)): +async def auto_login( + response: Response, db: Session = Depends(get_session), settings_service=Depends(get_settings_service) +): if settings_service.auth_settings.AUTO_LOGIN: - return create_user_longterm_token(db) + tokens = create_user_longterm_token(db) + response.set_cookie("access_token_lf", tokens["access_token"], httponly=False, secure=True, samesite="strict") + return tokens raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, @@ -55,12 +63,23 @@ async def auto_login(db: Session = Depends(get_session), settings_service=Depend @router.post("/refresh") -async def refresh_token(token: str): +async def refresh_token(request: Request, response: Response): + token = request.cookies.get("refresh_token_lf") if token: - return create_refresh_token(token) + tokens = create_refresh_token(token) + response.set_cookie("refresh_token_lf", tokens["refresh_token"], httponly=True, secure=True, samesite="strict") + response.set_cookie("access_token_lf", tokens["access_token"], httponly=False, secure=True, samesite="strict") + return tokens else: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token", headers={"WWW-Authenticate": "Bearer"}, ) + + +@router.post("/logout") +async def logout(response: Response): + response.delete_cookie("refresh_token_lf") + response.delete_cookie("access_token_lf") + return {"message": "Logout successful"} diff --git a/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py b/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py index bc8d78909..26afd765c 100644 --- a/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py +++ b/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py @@ -15,29 +15,21 @@ class VectaraSelfQueryRetriverComponent(CustomComponent): display_name: str = "Vectara Self Query Retriever for Vectara Vector Store" description: str = "Implementation of Vectara Self Query Retriever" - documentation = ( - "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query" - ) + documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query" beta = True field_config = { "code": {"show": True}, - "vectorstore": { - "display_name": "Vector Store", - "info": "Input Vectara Vectore Store" - }, - "llm": { - "display_name": "LLM", - "info": "For self query retriever" - }, - "document_content_description":{ - "display_name": "Document Content Description", + "vectorstore": {"display_name": "Vector Store", "info": "Input Vectara Vectore Store"}, + "llm": {"display_name": "LLM", "info": "For self query retriever"}, + "document_content_description": { + "display_name": "Document Content Description", "info": "For self query retriever", - }, + }, "metadata_field_info": { - "display_name": "Metadata Field Info", - "info": "Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {\"name\":\"speech\",\"description\":\"what name of the speech\",\"type\":\"string or list[string]\"}.\nThe keys should remain constant(name, description, type)", - }, + "display_name": "Metadata Field Info", + "info": 'Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {"name":"speech","description":"what name of the speech","type":"string or list[string]"}.\nThe keys should remain constant(name, description, type)', + }, } def build( @@ -47,24 +39,19 @@ class VectaraSelfQueryRetriverComponent(CustomComponent): llm: BaseLanguageModel, metadata_field_info: List[str], ) -> BaseRetriever: - metadata_field_obj = [] for meta in metadata_field_info: meta_obj = json.loads(meta) - if 'name' not in meta_obj or 'description' not in meta_obj or 'type' not in meta_obj : - raise Exception('Incorrect metadata field info format.') + if "name" not in meta_obj or "description" not in meta_obj or "type" not in meta_obj: + raise Exception("Incorrect metadata field info format.") attribute_info = AttributeInfo( - name = meta_obj['name'], - description = meta_obj['description'], - type = meta_obj['type'], + name=meta_obj["name"], + description=meta_obj["description"], + type=meta_obj["type"], ) metadata_field_obj.append(attribute_info) return SelfQueryRetriever.from_llm( - llm, - vectorstore, - document_content_description, - metadata_field_obj, - verbose=True - ) \ No newline at end of file + llm, vectorstore, document_content_description, metadata_field_obj, verbose=True + ) diff --git a/src/frontend/harFiles/backend_12112023.har b/src/frontend/harFiles/backend_12112023.har index 5021c7da5..63dbde94a 100644 --- a/src/frontend/harFiles/backend_12112023.har +++ b/src/frontend/harFiles/backend_12112023.har @@ -284,7 +284,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flows" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, @@ -338,7 +338,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flows" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, @@ -392,7 +392,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flows" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, @@ -446,7 +446,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, @@ -500,7 +500,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, @@ -554,7 +554,7 @@ { "name": "Accept-Language", "value": "en-US,en;q=0.9" }, { "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" }, { "name": "Connection", "value": "keep-alive" }, - { "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, + { "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" }, { "name": "Host", "value": "localhost:3000" }, { "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" }, { "name": "Sec-Fetch-Dest", "value": "empty" }, diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 94319da10..8ef4b20ce 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -13,6 +13,7 @@ "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "@mui/material": "^5.14.7", + "@preact/signals-react": "^2.0.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", @@ -71,7 +72,8 @@ "tailwindcss-animate": "^1.0.7", "uuid": "^9.0.0", "vite-plugin-svgr": "^3.2.0", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.4.7" }, "devDependencies": { "@playwright/test": "^1.38.0", @@ -1465,6 +1467,31 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@preact/signals-core": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.5.1.tgz", + "integrity": "sha512-dE6f+WCX5ZUDwXzUIWNMhhglmuLpqJhuy3X3xHrhZYI0Hm2LyQwOu0l9mdPiWrVNsE+Q7txOnJPgtIqHCYoBVA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@preact/signals-react": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@preact/signals-react/-/signals-react-2.0.0.tgz", + "integrity": "sha512-tMVi2SXFXlojaiPNWa8dlYaidR/XvEgMSp+iymKJgMssBM/QVtUQrodKZek1BJju+dkVHiyeuQHmkuLOI9oyNw==", + "dependencies": { + "@preact/signals-core": "^1.5.1", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "react": "^16.14.0 || 17.x || 18.x" + } + }, "node_modules/@radix-ui/number": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", @@ -11719,9 +11746,9 @@ } }, "node_modules/zustand": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.6.tgz", - "integrity": "sha512-Rb16eW55gqL4W2XZpJh0fnrATxYEG3Apl2gfHTyDSE965x/zxslTikpNch0JgNjJA9zK6gEFW8Fl6d1rTZaqgg==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.7.tgz", + "integrity": "sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==", "dependencies": { "use-sync-external-store": "1.2.0" }, @@ -12606,6 +12633,20 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, + "@preact/signals-core": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.5.1.tgz", + "integrity": "sha512-dE6f+WCX5ZUDwXzUIWNMhhglmuLpqJhuy3X3xHrhZYI0Hm2LyQwOu0l9mdPiWrVNsE+Q7txOnJPgtIqHCYoBVA==" + }, + "@preact/signals-react": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@preact/signals-react/-/signals-react-2.0.0.tgz", + "integrity": "sha512-tMVi2SXFXlojaiPNWa8dlYaidR/XvEgMSp+iymKJgMssBM/QVtUQrodKZek1BJju+dkVHiyeuQHmkuLOI9oyNw==", + "requires": { + "@preact/signals-core": "^1.5.1", + "use-sync-external-store": "^1.2.0" + } + }, "@radix-ui/number": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", @@ -19291,9 +19332,9 @@ "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==" }, "zustand": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.6.tgz", - "integrity": "sha512-Rb16eW55gqL4W2XZpJh0fnrATxYEG3Apl2gfHTyDSE965x/zxslTikpNch0JgNjJA9zK6gEFW8Fl6d1rTZaqgg==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.7.tgz", + "integrity": "sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==", "requires": { "use-sync-external-store": "1.2.0" } diff --git a/src/frontend/package.json b/src/frontend/package.json index d411b70eb..015b6d9a2 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -8,6 +8,7 @@ "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "@mui/material": "^5.14.7", + "@preact/signals-react": "^2.0.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", @@ -66,7 +67,8 @@ "tailwindcss-animate": "^1.0.7", "uuid": "^9.0.0", "vite-plugin-svgr": "^3.2.0", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.4.7" }, "scripts": { "dev:docker": "vite --host 0.0.0.0", diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 0898ad58d..3b88ee14c 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -1,6 +1,5 @@ import _ from "lodash"; import { useContext, useEffect, useState } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; import "reactflow/dist/style.css"; import "./App.css"; @@ -15,37 +14,27 @@ import { FETCH_ERROR_DESCRIPION, FETCH_ERROR_MESSAGE, } from "./constants/constants"; -import { alertContext } from "./contexts/alertContext"; -import { FlowsContext } from "./contexts/flowsContext"; -import { locationContext } from "./contexts/locationContext"; -import { typesContext } from "./contexts/typesContext"; +import { AuthContext } from "./contexts/authContext"; +import { getHealth } from "./controllers/API"; import Router from "./routes"; +import useAlertStore from "./stores/alertStore"; +import { useDarkStore } from "./stores/darkStore"; +import useFlowsManagerStore from "./stores/flowsManagerStore"; +import { useTypesStore } from "./stores/typesStore"; export default function App() { - let { setCurrent, setShowSideBar, setIsStackedOpen } = - useContext(locationContext); - let location = useLocation(); - useEffect(() => { - setCurrent(location.pathname.replace(/\/$/g, "").split("/")); - setShowSideBar(true); - setIsStackedOpen(true); - }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); - const { hardReset } = useContext(FlowsContext); - const { - errorData, - errorOpen, - setErrorOpen, - noticeData, - noticeOpen, - setNoticeOpen, - successData, - successOpen, - setSuccessOpen, - loading, - } = useContext(alertContext); - const navigate = useNavigate(); - const { fetchError } = useContext(typesContext); + const errorData = useAlertStore((state) => state.errorData); + const errorOpen = useAlertStore((state) => state.errorOpen); + const setErrorOpen = useAlertStore((state) => state.setErrorOpen); + const noticeData = useAlertStore((state) => state.noticeData); + const noticeOpen = useAlertStore((state) => state.noticeOpen); + const setNoticeOpen = useAlertStore((state) => state.setNoticeOpen); + const successData = useAlertStore((state) => state.successData); + const successOpen = useAlertStore((state) => state.successOpen); + const setSuccessOpen = useAlertStore((state) => state.setSuccessOpen); + const loading = useAlertStore((state) => state.loading); + const [fetchError, setFetchError] = useState(false); // Initialize state variable for the list of alerts const [alertsList, setAlertsList] = useState< @@ -131,28 +120,60 @@ export default function App() { ); }; + const { isAuthenticated } = useContext(AuthContext); + const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows); + const getTypes = useTypesStore((state) => state.getTypes); + const refreshVersion = useDarkStore((state) => state.refreshVersion); + const refreshStars = useDarkStore((state) => state.refreshStars); + + useEffect(() => { + refreshStars(); + refreshVersion(); + + // If the user is authenticated, fetch the types. This code is important to check if the user is auth because of the execution order of the useEffect hooks. + if (isAuthenticated === true) { + // get data from db + getTypes().then(() => { + refreshFlows(); + }); + } + }, [isAuthenticated]); + + useEffect(() => { + // Timer to call getHealth every 5 seconds + const timer = setInterval(() => { + getHealth() + .then(() => { + if (fetchError) setFetchError(false); + }) + .catch(() => { + setFetchError(true); + }); + }, 5000); + + // Clean up the timer on component unmount + return () => { + clearInterval(timer); + }; + }, []); + return ( //need parent component with width and height
{ - window.localStorage.removeItem("tabsData"); - window.localStorage.clear(); - hardReset(); - window.location.href = window.location.href; + // any reset function }} FallbackComponent={CrashErrorComponent} > - {loading ? ( + {fetchError ? ( + + ) : loading ? (
- {fetchError ? ( - - ) : ( - - )} +
) : ( <> diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 4c81e29a5..a76a15d17 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,12 +1,6 @@ import { cloneDeep } from "lodash"; -import React, { - ReactNode, - useContext, - useEffect, - useRef, - useState, -} from "react"; -import { Handle, Position, useUpdateNodeInternals } from "reactflow"; +import React, { ReactNode, useEffect, useRef, useState } from "react"; +import { Handle, Position } from "reactflow"; import ShadTooltip from "../../../../components/ShadTooltipComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent"; import DictComponent from "../../../../components/dictComponent"; @@ -26,16 +20,15 @@ import { LANGFLOW_SUPPORTED_TYPES, TOOLTIP_EMPTY, } from "../../../../constants/constants"; -import { alertContext } from "../../../../contexts/alertContext"; -import { FlowsContext } from "../../../../contexts/flowsContext"; -import { typesContext } from "../../../../contexts/typesContext"; -import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { postCustomComponentUpdate } from "../../../../controllers/API"; +import useAlertStore from "../../../../stores/alertStore"; +import useFlowStore from "../../../../stores/flowStore"; +import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; +import { useTypesStore } from "../../../../stores/typesStore"; import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; import { NodeDataType } from "../../../../types/flow"; import { - cleanEdges, convertObjToArray, convertValuesToNumbers, hasDuplicateKeys, @@ -68,47 +61,27 @@ export default function ParameterComponent({ const ref = useRef(null); const refHtml = useRef(null); const infoHtml = useRef(null); - const { setErrorData, modalContextOpen } = useContext(alertContext); - const updateNodeInternals = useUpdateNodeInternals(); - const [position, setPosition] = useState(0); - const { setTabsState, tabId, flows, tabsState, updateFlow } = - useContext(FlowsContext); + const setErrorData = useAlertStore((state) => state.setErrorData); + const currentFlow = useFlowsManagerStore((state) => state.currentFlow); + const nodes = useFlowStore((state) => state.nodes); + const edges = useFlowStore((state) => state.edges); + const setNode = useFlowStore((state) => state.setNode); - const [dataRef, setDataRef] = useState(data); - - const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null; - - // Update component position - useEffect(() => { - if (ref.current && ref.current.offsetTop && ref.current.clientHeight) { - setPosition(ref.current.offsetTop + ref.current.clientHeight / 2); - updateNodeInternals(data.id); - } - }, [data.id, ref, ref.current, ref.current?.offsetTop, updateNodeInternals]); - - useEffect(() => { - updateNodeInternals(data.id); - }, [data.id, position, updateNodeInternals]); + const flow = currentFlow?.data?.nodes ?? null; const groupedEdge = useRef(null); - useEffect(() => { - setDataRef(data); - }, [modalContextOpen]); + const setFilterEdge = useTypesStore((state) => state.setFilterEdge); - const { reactFlowInstance, setFilterEdge } = useContext(typesContext); let disabled = - reactFlowInstance - ?.getEdges() - .some( - (edge) => - edge.targetHandle === - scapedJSONStringfy(proxy ? { ...id, proxy } : id) - ) ?? false; + edges.some( + (edge) => + edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id) + ) ?? false; - const { data: myData } = useContext(typesContext); + const myData = useTypesStore((state) => state.data); - const { takeSnapshot } = useContext(undoRedoContext); + const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const handleUpdateValues = async (name: string, data: NodeDataType) => { const code = data.node?.template["code"]?.value; @@ -133,68 +106,46 @@ export default function ParameterComponent({ if (data.node!.template[name].value !== newValue) { takeSnapshot(); } - data.node!.template[name].value = newValue; - updateNodeInternals(data.id); - setDataRef((old) => { - let newData = cloneDeep(old); - newData.node!.template[name].value = newValue; - return newData; + data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input + + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + + newNode.data = { + ...newNode.data, + }; + + newNode.data.node.template[name].value = newValue; + + return newNode; }); - // Set state to pending - //@ts-ignore - if (data.node!.template[name].value !== newValue) { - const tabs = cloneDeep(tabsState); - tabs[tabId].isPending = false; - tabs[tabId].formKeysData = tabsState[tabId].formKeysData; - setTabsState({ - ...tabs, - }); - } renderTooltips(); }; const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => { if (!data.node) return; - if (data.node!.template[name].value !== newNodeClass.template[name].value) { + if (data.node!.template[name].value !== code) { takeSnapshot(); } - data.node! = { - ...newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, - }; - data.node!.template[name].value = code; - updateNodeInternals(data.id); - // Set state to pending - //@ts-ignore - if (data.node!.template[name].value !== code) { - const tabs = cloneDeep(tabsState); - tabs[tabId].isPending = false; - tabs[tabId].formKeysData = tabsState[tabId].formKeysData; - setTabsState({ - ...tabs, - }); - } + + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + + newNode.data = { + ...newNode.data, + node: newNodeClass, + description: newNodeClass.description ?? data.node!.description, + display_name: newNodeClass.display_name ?? data.node!.display_name, + }; + + newNode.data.node.template[name].value = code; + + return newNode; + }); + renderTooltips(); - let flow = flows.find((flow) => flow.id === tabId); - setTimeout(() => { - //timeout necessary because ReactFlow updates are not async - if (reactFlowInstance && flow && flow.data) { - cleanEdges({ - flow: { - edges: flow.data!.edges, - nodes: flow.data!.nodes, - }, - updateEdge: (edge) => { - reactFlowInstance.setEdges(edge); - updateNodeInternals(data.id); - }, - }); - updateFlow(flow); - } - }, 50); }; const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); @@ -323,7 +274,7 @@ export default function ParameterComponent({ : scapedJSONStringfy(id) } isValidConnection={(connection) => - isValidConnection(connection, reactFlowInstance!) + isValidConnection(connection, nodes, edges) } className={classNames( left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ", @@ -331,7 +282,6 @@ export default function ParameterComponent({ )} style={{ borderColor: color, - top: position, }} onClick={() => { setFilterEdge(groupedEdge.current); @@ -344,7 +294,7 @@ export default function ParameterComponent({ ) : (
<>
- isValidConnection(connection, reactFlowInstance!) + isValidConnection(connection, nodes, edges) } className={classNames( left ? "-ml-0.5 " : "-mr-0.5 ", @@ -404,7 +354,6 @@ export default function ParameterComponent({ )} style={{ borderColor: color, - top: position, }} onClick={() => { setFilterEdge(groupedEdge.current); @@ -454,9 +403,7 @@ export default function ParameterComponent({ id={"toggle-" + index} disabled={disabled} enabled={data.node?.template[name].value ?? false} - setEnabled={(isEnabled) => { - handleOnNewValue(isEnabled); - }} + setEnabled={handleOnNewValue} size="large" />
@@ -558,10 +505,7 @@ export default function ParameterComponent({ } : data.node!.template[name].value } - onChange={(newValue) => { - data.node!.template[name].value = newValue; - handleOnNewValue(newValue); - }} + onChange={handleOnNewValue} id="div-dict-input" />
@@ -571,15 +515,14 @@ export default function ParameterComponent({ disabled={disabled} editNode={false} value={ - dataRef.node!.template[name].value?.length === 0 || - !dataRef.node!.template[name].value + data.node!.template[name].value?.length === 0 || + !data.node!.template[name].value ? [{ "": "" }] - : convertObjToArray(dataRef.node!.template[name].value) + : convertObjToArray(data.node!.template[name].value) } duplicateKey={errorDuplicateKey} onChange={(newValue) => { const valueToNumbers = convertValuesToNumbers(newValue); - data.node!.template[name].value = valueToNumbers; setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers)); handleOnNewValue(valueToNumbers); }} diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index ca92a74cf..30110fdfe 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -1,17 +1,15 @@ -import { useContext, useEffect, useState } from "react"; -import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; +import { useEffect, useState } from "react"; +import { NodeToolbar } from "reactflow"; import ShadTooltip from "../../components/ShadTooltipComponent"; import Tooltip from "../../components/TooltipComponent"; import IconComponent from "../../components/genericIconComponent"; import InputComponent from "../../components/inputComponent"; import { Textarea } from "../../components/ui/textarea"; import { priorityFields } from "../../constants/constants"; -import { useSSE } from "../../contexts/SSEContext"; -import { alertContext } from "../../contexts/alertContext"; -import { FlowsContext } from "../../contexts/flowsContext"; -import { typesContext } from "../../contexts/typesContext"; -import { undoRedoContext } from "../../contexts/undoRedoContext"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; +import useFlowStore from "../../stores/flowStore"; +import useFlowsManagerStore from "../../stores/flowsManagerStore"; +import { useTypesStore } from "../../stores/typesStore"; import { validationStatusType } from "../../types/components"; import { NodeDataType } from "../../types/flow"; import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils"; @@ -30,11 +28,9 @@ export default function GenericNode({ xPos: number; yPos: number; }): JSX.Element { - const { updateFlow, flows, tabId, saveCurrentFlow } = - useContext(FlowsContext); - const updateNodeInternals = useUpdateNodeInternals(); - const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } = - useContext(typesContext); + const types = useTypesStore((state) => state.types); + const deleteNode = useFlowStore((state) => state.deleteNode); + const setNode = useFlowStore((state) => state.setNode); const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; const [inputName, setInputName] = useState(false); const [nodeName, setNodeName] = useState(data.node!.display_name); @@ -46,9 +42,8 @@ export default function GenericNode({ useState(null); const [handles, setHandles] = useState([]); let numberOfInputs: boolean[] = []; - const { modalContextOpen } = useContext(alertContext); - const { takeSnapshot } = useContext(undoRedoContext); + const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); function countHandles(): void { numberOfInputs = Object.keys(data.node!.template) @@ -84,7 +79,8 @@ export default function GenericNode({ }, [data, data.node]); // State for outline color - const { sseData, isBuilding } = useSSE(); + const sseData = useFlowStore((state) => state.sseData); + const isBuilding = useFlowStore((state) => state.isBuilding); useEffect(() => { setNodeDescription(data.node!.description); @@ -118,10 +114,12 @@ export default function GenericNode({ deleteNode={(id) => { takeSnapshot(); deleteNode(id); - saveCurrentFlow(); }} setShowNode={(show: boolean) => { - data.showNode = show; + setNode(data.id, (old) => ({ + ...old, + data: { ...old.data, showNode: show }, + })); }} numberOfHandles={handles} showNode={showNode} @@ -173,8 +171,16 @@ export default function GenericNode({ setInputName(false); if (nodeName.trim() !== "") { setNodeName(nodeName); - data.node!.display_name = nodeName; - updateNodeInternals(data.id); + setNode(data.id, (old) => ({ + ...old, + data: { + ...old.data, + node: { + ...old.data.node, + display_name: nodeName, + }, + }, + })); } else { setNodeName(data.node!.display_name); } @@ -380,8 +386,16 @@ export default function GenericNode({ onBlur={() => { setInputDescription(false); setNodeDescription(nodeDescription); - data.node!.description = nodeDescription; - updateNodeInternals(data.id); + setNode(data.id, (old) => ({ + ...old, + data: { + ...old.data, + node: { + ...old.data.node, + description: nodeDescription, + }, + }, + })); }} value={nodeDescription} onChange={(e) => setNodeDescription(e.target.value)} @@ -395,8 +409,16 @@ export default function GenericNode({ ) { setInputDescription(false); setNodeDescription(nodeDescription); - data.node!.description = nodeDescription; - updateNodeInternals(data.id); + setNode(data.id, (old) => ({ + ...old, + data: { + ...old.data, + node: { + ...old.data.node, + description: nodeDescription, + }, + }, + })); } }} /> diff --git a/src/frontend/src/alerts/alertDropDown/index.tsx b/src/frontend/src/alerts/alertDropDown/index.tsx index 92d9df46f..967d82174 100644 --- a/src/frontend/src/alerts/alertDropDown/index.tsx +++ b/src/frontend/src/alerts/alertDropDown/index.tsx @@ -1,23 +1,27 @@ -import { useContext, useState } from "react"; +import { useState } from "react"; import IconComponent from "../../components/genericIconComponent"; import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover"; -import { alertContext } from "../../contexts/alertContext"; +import useAlertStore from "../../stores/alertStore"; import { AlertDropdownType } from "../../types/alerts"; import SingleAlert from "./components/singleAlertComponent"; export default function AlertDropdown({ children, }: AlertDropdownType): JSX.Element { - const { - notificationList, - clearNotificationList, - removeFromNotificationList, - setNotificationCenter, - } = useContext(alertContext); + const notificationList = useAlertStore((state) => state.notificationList); + const clearNotificationList = useAlertStore( + (state) => state.clearNotificationList + ); + const removeFromNotificationList = useAlertStore( + (state) => state.removeFromNotificationList + ); + const setNotificationCenter = useAlertStore( + (state) => state.setNotificationCenter + ); const [open, setOpen] = useState(false); diff --git a/src/frontend/src/components/authAdminGuard/index.tsx b/src/frontend/src/components/authAdminGuard/index.tsx index 724d39f5a..96c404af3 100644 --- a/src/frontend/src/components/authAdminGuard/index.tsx +++ b/src/frontend/src/components/authAdminGuard/index.tsx @@ -1,30 +1,16 @@ -import { useContext, useEffect } from "react"; +import { useContext } from "react"; import { Navigate } from "react-router-dom"; import { AuthContext } from "../../contexts/authContext"; export const ProtectedAdminRoute = ({ children }) => { - const { - isAdmin, - isAuthenticated, - logout, - getAuthentication, - userData, - autoLogin, - } = useContext(AuthContext); - useEffect(() => { - if (!isAuthenticated && !getAuthentication()) { - window.location.replace("/login"); - logout(); - } - }, [isAuthenticated, getAuthentication, logout, userData]); + const { isAdmin, isAuthenticated, logout, userData, autoLogin } = + useContext(AuthContext); - if (!isAuthenticated && !getAuthentication()) { - return ; - } - - if ((userData && !isAdmin) || autoLogin) { + if (!isAuthenticated) { + logout(); + } else if ((userData && !isAdmin) || autoLogin) { return ; + } else { + return children; } - - return children; }; diff --git a/src/frontend/src/components/authGuard/index.tsx b/src/frontend/src/components/authGuard/index.tsx index 9b21a1c9f..8450248a9 100644 --- a/src/frontend/src/components/authGuard/index.tsx +++ b/src/frontend/src/components/authGuard/index.tsx @@ -1,14 +1,11 @@ import { useContext } from "react"; -import { Navigate } from "react-router-dom"; import { AuthContext } from "../../contexts/authContext"; export const ProtectedRoute = ({ children }) => { - const { isAuthenticated, logout, getAuthentication } = - useContext(AuthContext); - if (!isAuthenticated && !getAuthentication()) { + const { isAuthenticated, logout } = useContext(AuthContext); + if (!isAuthenticated) { logout(); - return ; + } else { + return children; } - - return children; }; diff --git a/src/frontend/src/components/authLoginGuard/index.tsx b/src/frontend/src/components/authLoginGuard/index.tsx index 980528023..ed12d0f54 100644 --- a/src/frontend/src/components/authLoginGuard/index.tsx +++ b/src/frontend/src/components/authLoginGuard/index.tsx @@ -3,14 +3,14 @@ import { Navigate } from "react-router-dom"; import { AuthContext } from "../../contexts/authContext"; export const ProtectedLoginRoute = ({ children }) => { - const { getAuthentication, autoLogin } = useContext(AuthContext); + const { isAuthenticated, autoLogin } = useContext(AuthContext); if (autoLogin === true) { window.location.replace("/"); return ; } - if (getAuthentication()) { + if (isAuthenticated) { window.location.replace("/"); return ; } diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 5417f58ce..a253f1f5e 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -1,9 +1,9 @@ -import { useContext, useEffect, useState } from "react"; -import { alertContext } from "../../contexts/alertContext"; -import { FlowsContext } from "../../contexts/flowsContext"; -import { StoreContext } from "../../contexts/storeContext"; +import { useEffect, useState } from "react"; import { getComponent, postLikeComponent } from "../../controllers/API"; import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal"; +import useAlertStore from "../../stores/alertStore"; +import useFlowsManagerStore from "../../stores/flowsManagerStore"; +import { useStoreStore } from "../../stores/storeStore"; import { storeComponent } from "../../types/store"; import cloneFLowWithParent from "../../utils/storeUtils"; import { cn } from "../../utils/utils"; @@ -32,9 +32,10 @@ export default function CollectionCardComponent({ button?: JSX.Element; onDelete?: () => void; }) { - const { addFlow } = useContext(FlowsContext); - const { setSuccessData, setErrorData } = useContext(alertContext); - const { setValidApiKey } = useContext(StoreContext); + const addFlow = useFlowsManagerStore((state) => state.addFlow); + const setSuccessData = useAlertStore((state) => state.setSuccessData); + const setErrorData = useAlertStore((state) => state.setErrorData); + const setValidApiKey = useStoreStore((state) => state.updateValidApiKey); const isStore = false; const [loading, setLoading] = useState(false); const [loadingLike, setLoadingLike] = useState(false); diff --git a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx index f1e374a03..546f8a529 100644 --- a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx +++ b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx @@ -1,15 +1,12 @@ import { Transition } from "@headlessui/react"; -import { useContext, useState } from "react"; +import { useState } from "react"; import Loading from "../../../components/ui/loading"; -import { useSSE } from "../../../contexts/SSEContext"; -import { alertContext } from "../../../contexts/alertContext"; -import { typesContext } from "../../../contexts/typesContext"; import { postBuildInit } from "../../../controllers/API"; import { FlowType } from "../../../types/flow"; -import { FlowsContext } from "../../../contexts/flowsContext"; +import useAlertStore from "../../../stores/alertStore"; +import useFlowStore from "../../../stores/flowStore"; import { parsedDataType } from "../../../types/components"; -import { FlowsState } from "../../../types/tabs"; import { validateNodes } from "../../../utils/reactflowUtils"; import RadialProgressComponent from "../../RadialProgress"; import IconComponent from "../../genericIconComponent"; @@ -24,11 +21,15 @@ export default function BuildTrigger({ setIsBuilt: any; isBuilt: boolean; }): JSX.Element { - const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE(); - const { reactFlowInstance } = useContext(typesContext); - const { setTabsState, saveFlow } = useContext(FlowsContext); - const { setErrorData, setSuccessData } = useContext(alertContext); - const [isIconTouched, setIsIconTouched] = useState(false); + const updateSSEData = useFlowStore((state) => state.updateSSEData); + const isBuilding = useFlowStore((state) => state.isBuilding); + const setIsBuilding = useFlowStore((state) => state.setIsBuilding); + const nodes = useFlowStore((state) => state.nodes); + const edges = useFlowStore((state) => state.edges); + const setErrorData = useAlertStore((state) => state.setErrorData); + const setSuccessData = useAlertStore((state) => state.setSuccessData); + const setFlowState = useFlowStore((state) => state.setFlowState); + const eventClick = isBuilding ? "pointer-events-none" : ""; const [progress, setProgress] = useState(0); @@ -37,10 +38,7 @@ export default function BuildTrigger({ if (isBuilding) { return; } - const errors = validateNodes( - reactFlowInstance!.getNodes(), - reactFlowInstance!.getEdges() - ); + const errors = validateNodes(nodes, edges); if (errors.length > 0) { setErrorData({ title: "Oops! Looks like you missed something", @@ -76,7 +74,6 @@ export default function BuildTrigger({ } async function streamNodeData(flow: FlowType) { // Step 1: Make a POST request to send the flow data and receive a unique session ID - const id = saveFlow(flow, true); const response = await postBuildInit(flow); const { flowId } = response.data; // Step 2: Use the session ID to establish an SSE connection using EventSource @@ -99,16 +96,7 @@ export default function BuildTrigger({ // If the event is a log, log it setSuccessData({ title: parsedData.log }); } else if (parsedData.input_keys !== undefined) { - //@ts-ignore - setTabsState((old: FlowsState) => { - return { - ...old, - [flowId]: { - ...old[flowId], - formKeysData: parsedData, - }, - }; - }); + setFlowState(parsedData); } else { // Otherwise, process the data const isValid = processStreamResult(parsedData); @@ -154,14 +142,6 @@ export default function BuildTrigger({ } } - const handleMouseEnter = () => { - setIsIconTouched(true); - }; - - const handleMouseLeave = () => { - setIsIconTouched(false); - }; - return ( { handleBuild(flow); }} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} > @@ -108,6 +105,8 @@ export const MenuBar = ({ flows, tabId }: menuBarPropsType): JSX.Element => { >
+ ) : ( + <> ); }; diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 77cd0ded8..2b934942a 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -1,14 +1,13 @@ -import { useContext } from "react"; +import { useContext, useEffect } from "react"; import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa"; import { Link, useLocation, useNavigate } from "react-router-dom"; import AlertDropdown from "../../alerts/alertDropDown"; import { USER_PROJECTS_HEADER } from "../../constants/constants"; -import { alertContext } from "../../contexts/alertContext"; import { AuthContext } from "../../contexts/authContext"; -import { darkContext } from "../../contexts/darkContext"; -import { StoreContext } from "../../contexts/storeContext"; -import { FlowsContext } from "../../contexts/flowsContext"; +import useAlertStore from "../../stores/alertStore"; +import { useDarkStore } from "../../stores/darkStore"; +import { useStoreStore } from "../../stores/storeStore"; import { gradients } from "../../utils/styleUtils"; import IconComponent from "../genericIconComponent"; import { Button } from "../ui/button"; @@ -24,25 +23,33 @@ import { Separator } from "../ui/separator"; import MenuBar from "./components/menuBar"; export default function Header(): JSX.Element { - const { flows, tabId } = useContext(FlowsContext); - const { dark, setDark } = useContext(darkContext); - const { notificationCenter } = useContext(alertContext); + const notificationCenter = useAlertStore((state) => state.notificationCenter); const location = useLocation(); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext); - const { hasStore } = useContext(StoreContext); - const { stars, gradientIndex } = useContext(darkContext); const navigate = useNavigate(); + const hasStore = useStoreStore((state) => state.hasStore); + + const dark = useDarkStore((state) => state.dark); + const setDark = useDarkStore((state) => state.setDark); + const stars = useDarkStore((state) => state.stars); + + useEffect(() => { + if (dark) { + document.getElementById("body")!.classList.add("dark"); + } else { + document.getElementById("body")!.classList.remove("dark"); + } + window.localStorage.setItem("isDark", dark.toString()); + }, [dark]); + return (
⛓️ - - {flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && ( - - )} +
@@ -159,7 +166,10 @@ export default function Header(): JSX.Element {
- {tabsState[id.current]?.formKeysData?.input_keys - ? Object.keys( - tabsState[id.current].formKeysData.input_keys! - ).map((key, index) => ( + {flowState?.input_keys + ? Object.keys(flowState?.input_keys!).map((key, index) => (
t === key )} /> @@ -508,30 +495,23 @@ export default function FormModal({ keyValue={key} >
- {tabsState[id.current].formKeysData.handle_keys!.some( - (t) => t === key - ) && ( + {flowState?.handle_keys!.some((t) => t === key) && (
Source: Component
)}