diff --git a/src/backend/langflow/api/v1/store.py b/src/backend/langflow/api/v1/store.py index bbd05f4aa..47f377058 100644 --- a/src/backend/langflow/api/v1/store.py +++ b/src/backend/langflow/api/v1/store.py @@ -12,6 +12,7 @@ from langflow.services.store.schema import ( ListComponentResponse, StoreComponentCreate, TagResponse, + UsersLikesResponse, ) from fastapi import APIRouter, Depends, HTTPException, Query @@ -77,11 +78,14 @@ def list_components( "id", "name", "description", - "user_created.name", + "user_created.first_name", + "user_created.id", "is_component", "tags.tags_id.name", "tags.tags_id.id", "count(liked_by)", + "count(downloads)", + "metadata", ] result = store_service.query_components( store_api_Key, @@ -90,6 +94,9 @@ def list_components( fields=fields, filter_by_user=filter_by_user, ) + # tags comes as "tags" : [{"tags_id": {"name": "tag1", "id": 1}}] + # so we need to flatten it + return result except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) @@ -171,3 +178,14 @@ def get_tags( return store_service.get_tags(store_api_Key) except Exception as exc: raise HTTPException(status_code=500, detail=str(exc)) + + +@router.get("/users/likes", response_model=List[UsersLikesResponse]) +def get_list_of_components_liked_by_user( + store_service: StoreService = Depends(get_store_service), + store_api_Key: str = Depends(get_user_store_api_key), +): + try: + return store_service.get_user_likes(store_api_Key) + except Exception as exc: + raise HTTPException(status_code=500, detail=str(exc)) diff --git a/src/backend/langflow/services/store/schema.py b/src/backend/langflow/services/store/schema.py index 259d341b2..215b15b91 100644 --- a/src/backend/langflow/services/store/schema.py +++ b/src/backend/langflow/services/store/schema.py @@ -9,6 +9,11 @@ class TagResponse(BaseModel): name: Optional[str] +class UsersLikesResponse(BaseModel): + id: UUID + likes: Optional[List[UUID]] + + class ComponentResponse(BaseModel): id: UUID status: Optional[str] @@ -22,8 +27,14 @@ class ComponentResponse(BaseModel): description: Optional[str] data: Optional[dict] tags: Optional[List[int]] - liked_by_count: Optional[List[UUID]] + liked_by_count: Optional[int] + downloads_count: Optional[int] parent: Optional[UUID] + metadata: Optional[dict] + + +class TagsIdResponse(BaseModel): + tags_id: Optional[TagResponse] class ListComponentResponse(BaseModel): @@ -32,6 +43,10 @@ class ListComponentResponse(BaseModel): description: Optional[str] liked_by_count: Optional[int] is_component: Optional[bool] + metadata: Optional[dict] + user_created: Optional[dict] + tags: Optional[List[TagsIdResponse]] = None + downloads_count: Optional[int] class DownloadComponentResponse(BaseModel): @@ -40,6 +55,8 @@ class DownloadComponentResponse(BaseModel): description: Optional[str] data: Optional[dict] is_component: Optional[bool] + metadata: Optional[dict] + downloads_count: Optional[int] class StoreComponentCreate(BaseModel): @@ -49,3 +66,4 @@ class StoreComponentCreate(BaseModel): tags: Optional[List[str]] parent: Optional[UUID] is_component: Optional[bool] + metadata: Optional[dict] diff --git a/src/backend/langflow/services/store/service.py b/src/backend/langflow/services/store/service.py index 83a199445..ab7cdd86f 100644 --- a/src/backend/langflow/services/store/service.py +++ b/src/backend/langflow/services/store/service.py @@ -152,27 +152,46 @@ class StoreService(Service): ",".join(fields) if fields else ",".join( - ["id", "name", "description", "count(liked_by)", "is_component"] + [ + "id", + "name", + "description", + "user_created.first_name", + "user_created.id", + "is_component", + "tags.tags_id.name", + "tags.tags_id.id", + "count(liked_by)", + "count(downloads)", + "metadata", + ] ) ) # Only public components or the ones created by the user # check for "public" or "Public" if filter_by_user: - params["deep"] = json.dumps( - { - "components": { - "_filter": {"user_created": {"token": {"_eq": api_key}}} - } - } + user_data = self._get( + f"{self.base_url}/users/me", api_key, params={"fields": "id"} ) + params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}}) + # Get the + params.pop("page", None) + params.pop("limit", None) + + params["fields"] = ["id"] else: params["filter"] = params["filter"] = json.dumps( {"status": {"_in": ["public", "Public"]}} ) results = self._get(self.components_url, api_key, params) - return [ListComponentResponse(**component) for component in results] + results_objects = [ListComponentResponse(**component) for component in results] + # Flatten the tags + for component in results_objects: + if component.tags: + component.tags = [tags_id.tags_id for tags_id in component.tags] + return results_objects def download(self, api_key: str, component_id: str) -> DownloadComponentResponse: url = f"{self.components_url}/{component_id}" @@ -216,3 +235,11 @@ class StoreService(Service): params = {"fields": ",".join(["id", "name"])} tags = self._get(url, api_key, params) return tags + + def get_user_likes(self, api_key: str) -> List[Dict[str, Any]]: + url = f"{self.base_url}/users/me" + params = { + "fields": ",".join(["id", "likes"]), + } + likes = self._get(url, api_key, params) + return likes diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 0bf66bdca..c17d7de6c 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -3796,9 +3796,9 @@ "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" }, "node_modules/@types/react": { - "version": "18.2.32", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.32.tgz", - "integrity": "sha512-F0FVIZQ1x5Gxy/VYJb7XcWvCcHR28Sjwt1dXLspdIatfPq1MVACfnBDwKe6ANLxQ64riIJooXClpUR6oxTiepg==", + "version": "18.2.33", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", + "integrity": "sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -5237,9 +5237,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.566", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.566.tgz", - "integrity": "sha512-mv+fAy27uOmTVlUULy15U3DVJ+jg+8iyKH1bpwboCRhtDC69GKf1PPTZvEIhCyDr81RFqfxZJYrbgp933a1vtg==" + "version": "1.4.567", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz", + "integrity": "sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -13755,9 +13755,9 @@ "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" }, "@types/react": { - "version": "18.2.32", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.32.tgz", - "integrity": "sha512-F0FVIZQ1x5Gxy/VYJb7XcWvCcHR28Sjwt1dXLspdIatfPq1MVACfnBDwKe6ANLxQ64riIJooXClpUR6oxTiepg==", + "version": "18.2.33", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", + "integrity": "sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -14768,9 +14768,9 @@ } }, "electron-to-chromium": { - "version": "1.4.566", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.566.tgz", - "integrity": "sha512-mv+fAy27uOmTVlUULy15U3DVJ+jg+8iyKH1bpwboCRhtDC69GKf1PPTZvEIhCyDr81RFqfxZJYrbgp933a1vtg==" + "version": "1.4.567", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz", + "integrity": "sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w==" }, "emoji-regex": { "version": "8.0.0", diff --git a/src/frontend/src/pages/MainPage/components/saved-components/index.tsx b/src/frontend/src/pages/MainPage/components/saved-components/index.tsx index 5f1bf4882..3e629ddfa 100644 --- a/src/frontend/src/pages/MainPage/components/saved-components/index.tsx +++ b/src/frontend/src/pages/MainPage/components/saved-components/index.tsx @@ -6,7 +6,7 @@ import { alertContext } from "../../../../contexts/alertContext"; import { AuthContext } from "../../../../contexts/authContext"; import { TabsContext } from "../../../../contexts/tabsContext"; import { getStoreComponents } from "../../../../controllers/API"; -import { FlowComponent } from "../../../../types/store"; +import { storeComponent } from "../../../../types/store"; import { MarketCardComponent } from "../../../StorePage/components/market-card"; export default function SavedComponents(): JSX.Element { @@ -18,7 +18,7 @@ export default function SavedComponents(): JSX.Element { useEffect(() => { setTabId(""); }, []); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [filteredCategories, setFilteredCategories] = useState(new Set()); const { setErrorData } = useContext(alertContext); diff --git a/src/frontend/src/pages/StorePage/components/market-card.tsx b/src/frontend/src/pages/StorePage/components/market-card.tsx index 87b73a28c..8816c63df 100644 --- a/src/frontend/src/pages/StorePage/components/market-card.tsx +++ b/src/frontend/src/pages/StorePage/components/market-card.tsx @@ -1,5 +1,5 @@ -import { Link, ToyBrick } from "lucide-react"; import { useContext, useEffect, useRef, useState } from "react"; +import ShadTooltip from "../../../components/ShadTooltipComponent"; import IconComponent from "../../../components/genericIconComponent"; import { Badge } from "../../../components/ui/badge"; import { Button } from "../../../components/ui/button"; @@ -15,15 +15,15 @@ import { StoreContext } from "../../../contexts/storeContext"; import { TabsContext } from "../../../contexts/tabsContext"; import { getComponent, saveFlowStore } from "../../../controllers/API"; import { FlowType } from "../../../types/flow"; -import { FlowComponent } from "../../../types/store"; +import { storeComponent } from "../../../types/store"; import cloneFLowWithParent from "../../../utils/storeUtils"; -export const MarketCardComponent = ({ data }: { data: FlowComponent }) => { +export const MarketCardComponent = ({ data }: { data: storeComponent }) => { const { savedFlows } = useContext(StoreContext); const [added, setAdded] = useState(savedFlows.has(data.id) ? true : false); const [loading, setLoading] = useState(false); const { addFlow } = useContext(TabsContext); - const { setSuccessData } = useContext(alertContext); + const { setSuccessData, setErrorData } = useContext(alertContext); const flowData = useRef(); useEffect(() => { @@ -42,9 +42,14 @@ export const MarketCardComponent = ({ data }: { data: FlowComponent }) => { .then(() => { setAdded(true); setLoading(false); + setSuccessData({ title: "Component Added to account" }); }) .catch((error) => { console.error(error); + setErrorData({ + title: "Error on adding Component", + list: [error["response"]["data"]["detail"]], + }); }); }, (error) => { @@ -69,28 +74,6 @@ export const MarketCardComponent = ({ data }: { data: FlowComponent }) => { } } - function handleFork(flowId: string, is_component: boolean) { - getComponent(flowId).then( - (res) => { - console.log(res); - const newFLow = cloneFLowWithParent(res.data, res.id, is_component); - console.log(newFLow); - saveFlowStore(newFLow).then( - (res) => { - console.log(JSON.parse(JSON.stringify(res))); - addFlow(true, newFLow); - }, - (error) => { - console.error(error); - } - ); - }, - (error) => { - console.log(error); - } - ); - } - return (
@@ -143,12 +126,29 @@ export const MarketCardComponent = ({ data }: { data: FlowComponent }) => {
chain - + - - - 123 - + + + + 123 + + + + + + {data.liked_by_count ?? 0} + + + + + + {data.downloads_count} + +
{/* {data.isChat ? ( diff --git a/src/frontend/src/types/store/index.ts b/src/frontend/src/types/store/index.ts index 63f29b4e6..096572e9a 100644 --- a/src/frontend/src/types/store/index.ts +++ b/src/frontend/src/types/store/index.ts @@ -1,13 +1,10 @@ -export type FlowComponent = { +export type storeComponent = { id: string; - status: string; - sort: null | any; - user_created: string; - date_created: string; - user_updated: string; - date_updated: string; is_component: boolean; + tags: string[]; + metadata?: {}; + downloads_count: number; name: string; description: string; - data: Object; + liked_by_count: number; }; diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 090f6f356..a5bebeae3 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -38,6 +38,7 @@ import { GithubIcon, Group, Hammer, + Heart, HelpCircle, Home, Info, @@ -75,6 +76,7 @@ import { Store, SunIcon, TerminalSquare, + ToyBrick, Trash2, Undo, Ungroup, @@ -334,4 +336,7 @@ export const nodeIconsLucide: iconsType = { GitBranchPlus, Loader2, BookmarkPlus, + Heart, + Link, + ToyBrick, };