From 1d7dd4550e6fb8e9c3ec104367cecc6ed597d4c0 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Fri, 21 Jun 2024 17:25:39 -0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(hooks):=20add=20custom=20hooks=20f?= =?UTF-8?q?or=20card=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add `useDataEffect` to handle data-related side effects - Add `useInstallComponent` to manage component installation logic - Add `useLikeComponent` to handle like functionality - Add `useDragStart` to manage drag start events - Add `usePlaygroundEffect` to handle playground-related side effects ✨ (cardComponent): add custom hooks for data, install, like, drag start, and playground effects ♻️ (cardComponent): rename state variables for consistency and readability ✨ (MainPage): add CollectionCard component to handle card rendering and interactions ✨ (index.tsx): add CollectionCard component to ComponentsComponent ♻️ (index.tsx): refactor to use CollectionCard instead of inline code --- .../cardComponent/hooks/use-data-effect.tsx | 18 ++ .../hooks/use-handle-install.tsx | 54 ++++++ .../cardComponent/hooks/use-handle-like.tsx | 50 ++++++ .../cardComponent/hooks/use-on-drag-start.tsx | 33 ++++ .../hooks/use-playground-effect.tsx | 26 +++ .../src/components/cardComponent/index.tsx | 168 +++++------------- .../components/collectionCard/index.tsx | 58 ++++++ .../components/componentsComponent/index.tsx | 43 +---- 8 files changed, 288 insertions(+), 162 deletions(-) create mode 100644 src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx create mode 100644 src/frontend/src/components/cardComponent/hooks/use-handle-install.tsx create mode 100644 src/frontend/src/components/cardComponent/hooks/use-handle-like.tsx create mode 100644 src/frontend/src/components/cardComponent/hooks/use-on-drag-start.tsx create mode 100644 src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx create mode 100644 src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx diff --git a/src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx b/src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx new file mode 100644 index 000000000..fd03753b2 --- /dev/null +++ b/src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx @@ -0,0 +1,18 @@ +import { useEffect } from "react"; + +const useDataEffect = ( + data, + setLikedByUser, + setLikesCount, + setDownloadsCount, +) => { + useEffect(() => { + if (data) { + setLikedByUser(data?.liked_by_user ?? false); + setLikesCount(data?.liked_by_count ?? 0); + setDownloadsCount(data?.downloads_count ?? 0); + } + }, [data, data?.liked_by_count, data?.liked_by_user, data?.downloads_count]); +}; + +export default useDataEffect; diff --git a/src/frontend/src/components/cardComponent/hooks/use-handle-install.tsx b/src/frontend/src/components/cardComponent/hooks/use-handle-install.tsx new file mode 100644 index 000000000..062f51ebd --- /dev/null +++ b/src/frontend/src/components/cardComponent/hooks/use-handle-install.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; +import { getComponent } from "../../../controllers/API"; +import useFlowsManagerStore from "../../../stores/flowsManagerStore"; +import cloneFlowWithParent from "../../../utils/storeUtils"; + +const useInstallComponent = ( + data, + name, + isStore, + downloadsCount, + setDownloadsCount, + setLoading, + setSuccessData, + setErrorData, +) => { + const addFlow = useFlowsManagerStore((state) => state.addFlow); + + const handleInstall = () => { + const temp = downloadsCount; + setDownloadsCount((old) => Number(old) + 1); + setLoading(true); + + getComponent(data.id) + .then((res) => { + const newFlow = cloneFlowWithParent(res, res.id, data.is_component); + addFlow(true, newFlow) + .then((id) => { + setSuccessData({ + title: `${name} ${isStore ? "Downloaded" : "Installed"} Successfully.`, + }); + setLoading(false); + }) + .catch((error) => { + setLoading(false); + setErrorData({ + title: `Error ${isStore ? "downloading" : "installing"} the ${name}`, + list: [error.response.data.detail], + }); + }); + }) + .catch((err) => { + setLoading(false); + setErrorData({ + title: `Error ${isStore ? "downloading" : "installing"} the ${name}`, + list: [err.response.data.detail], + }); + setDownloadsCount(temp); + }); + }; + + return { handleInstall }; +}; + +export default useInstallComponent; diff --git a/src/frontend/src/components/cardComponent/hooks/use-handle-like.tsx b/src/frontend/src/components/cardComponent/hooks/use-handle-like.tsx new file mode 100644 index 000000000..61e049e4d --- /dev/null +++ b/src/frontend/src/components/cardComponent/hooks/use-handle-like.tsx @@ -0,0 +1,50 @@ +import { postLikeComponent } from "../../../controllers/API"; + +const useLikeComponent = ( + data, + name, + setLoadingLike, + likedByUser, + likesCount, + setLikedByUser, + setLikesCount, + setValidApiKey, + setErrorData, +) => { + const handleLike = () => { + setLoadingLike(true); + if (likedByUser !== undefined || likedByUser !== null) { + const temp = likedByUser; + const tempNum = likesCount; + setLikedByUser((prev) => !prev); + setLikesCount((prev) => (temp ? prev - 1 : prev + 1)); + + postLikeComponent(data.id) + .then((response) => { + setLoadingLike(false); + setLikesCount(response.data.likes_count); + setLikedByUser(response.data.liked_by_user); + }) + .catch((error) => { + setLoadingLike(false); + setLikesCount(tempNum); + setLikedByUser(temp); + if (error.response.status === 403) { + setValidApiKey(false); + } else { + console.error(error); + setErrorData({ + title: `Error liking ${name}.`, + list: [error.response.data.detail], + }); + } + }); + } + }; + + return { + handleLike, + }; +}; + +export default useLikeComponent; diff --git a/src/frontend/src/components/cardComponent/hooks/use-on-drag-start.tsx b/src/frontend/src/components/cardComponent/hooks/use-on-drag-start.tsx new file mode 100644 index 000000000..a73a2cf3b --- /dev/null +++ b/src/frontend/src/components/cardComponent/hooks/use-on-drag-start.tsx @@ -0,0 +1,33 @@ +import { useCallback } from "react"; +import { createRoot } from "react-dom/client"; +import useFlowsManagerStore from "../../../stores/flowsManagerStore"; +import DragCardComponent from "../components/dragCardComponent"; + +const useDragStart = (data) => { + const getFlowById = useFlowsManagerStore((state) => state.getFlowById); + + const onDragStart = useCallback( + (event) => { + let image = ; // Replace with whatever you want here + + const ghost = document.createElement("div"); + ghost.style.transform = "translate(-10000px, -10000px)"; + ghost.style.position = "absolute"; + document.body.appendChild(ghost); + event.dataTransfer.setDragImage(ghost, 0, 0); + + const root = createRoot(ghost); + root.render(image); + + const flow = getFlowById(data.id); + if (flow) { + event.dataTransfer.setData("flow", JSON.stringify(data)); + } + }, + [data], + ); + + return { onDragStart }; +}; + +export default useDragStart; diff --git a/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx b/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx new file mode 100644 index 000000000..ff8e3a96f --- /dev/null +++ b/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx @@ -0,0 +1,26 @@ +import { useEffect } from "react"; + +const usePlaygroundEffect = ( + currentFlowId, + playground, + openPlayground, + currentFlow, + setNodes, + setEdges, + cleanFlowPool, +) => { + useEffect(() => { + if (currentFlowId && playground) { + if (openPlayground) { + setNodes(currentFlow?.data?.nodes ?? [], true); + setEdges(currentFlow?.data?.edges ?? [], true); + } else { + setNodes([], true); + setEdges([], true); + } + cleanFlowPool(); + } + }, [openPlayground]); +}; + +export default usePlaygroundEffect; diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index ba00e6958..68ba1e231 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -28,6 +28,11 @@ import { Checkbox } from "../ui/checkbox"; import { FormControl, FormField } from "../ui/form"; import Loading from "../ui/loading"; import DragCardComponent from "./components/dragCardComponent"; +import useDataEffect from "./hooks/use-data-effect"; +import useInstallComponent from "./hooks/use-handle-install"; +import useLikeComponent from "./hooks/use-handle-like"; +import useDragStart from "./hooks/use-on-drag-start"; +import usePlaygroundEffect from "./hooks/use-playground-effect"; import { convertTestName } from "./utils/convert-test-name"; export default function CollectionCardComponent({ @@ -59,11 +64,9 @@ export default function CollectionCardComponent({ const isStore = false; const [loading, setLoading] = useState(false); const [loadingLike, setLoadingLike] = useState(false); - const [liked_by_user, setLiked_by_user] = useState( - data?.liked_by_user ?? false, - ); - const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0); - const [downloads_count, setDownloads_count] = useState( + const [likedByUser, setLikedByUser] = useState(data?.liked_by_user ?? false); + const [likesCount, setLikesCount] = useState(data?.liked_by_count ?? 0); + const [downloadsCount, setDownloadsCount] = useState( data?.downloads_count ?? 0, ); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); @@ -99,115 +102,45 @@ export default function CollectionCardComponent({ return inputs.length > 0 || outputs.length > 0; } - useEffect(() => { - if (currentFlowId && playground) { - if (openPlayground) { - setNodes(currentFlow?.data?.nodes ?? [], true); - setEdges(currentFlow?.data?.edges ?? [], true); - } else { - setNodes([], true); - setEdges([], true); - } - cleanFlowPool(); - } - }, [openPlayground]); + usePlaygroundEffect( + currentFlowId, + playground, + openPlayground, + currentFlow, + setNodes, + setEdges, + cleanFlowPool, + ); - useEffect(() => { - if (data) { - setLiked_by_user(data?.liked_by_user ?? false); - setLikes_count(data?.liked_by_count ?? 0); - setDownloads_count(data?.downloads_count ?? 0); - } - }, [data, data.liked_by_count, data.liked_by_user, data.downloads_count]); + useDataEffect(data, setLikedByUser, setLikesCount, setDownloadsCount); - function handleInstall() { - const temp = downloads_count; - setDownloads_count((old) => Number(old) + 1); - setLoading(true); - getComponent(data.id) - .then((res) => { - const newFlow = cloneFLowWithParent(res, res.id, data.is_component); - addFlow(true, newFlow) - .then((id) => { - setSuccessData({ - title: `${name} ${ - isStore ? "Downloaded" : "Installed" - } Successfully.`, - }); - setLoading(false); - }) - .catch((error) => { - setLoading(false); - setErrorData({ - title: `Error ${ - isStore ? "downloading" : "installing" - } the ${name}`, - list: [error["response"]["data"]["detail"]], - }); - }); - }) - .catch((err) => { - setLoading(false); - setErrorData({ - title: `Error ${isStore ? "downloading" : "installing"} the ${name}`, - list: [err["response"]["data"]["detail"]], - }); - setDownloads_count(temp); - }); - } + const { handleInstall } = useInstallComponent( + data, + name, + isStore, + downloadsCount, + setDownloadsCount, + setLoading, + setSuccessData, + setErrorData, + ); - function handleLike() { - setLoadingLike(true); - if (liked_by_user !== undefined || liked_by_user !== null) { - const temp = liked_by_user; - const tempNum = likes_count; - setLiked_by_user((prev) => !prev); - if (!temp) { - setLikes_count((prev) => Number(prev) + 1); - } else { - setLikes_count((prev) => Number(prev) - 1); - } - postLikeComponent(data.id) - .then((response) => { - setLoadingLike(false); - setLikes_count(response.data.likes_count); - setLiked_by_user(response.data.liked_by_user); - }) - .catch((error) => { - setLoadingLike(false); - setLikes_count(tempNum); - setLiked_by_user(temp); - if (error.response.status === 403) { - setValidApiKey(false); - } else { - console.error(error); - setErrorData({ - title: `Error liking ${name}.`, - list: [error["response"]["data"]["detail"]], - }); - } - }); - } - } + const { handleLike } = useLikeComponent( + data, + name, + setLoadingLike, + likedByUser, + likesCount, + setLikedByUser, + setLikesCount, + setValidApiKey, + setErrorData, + ); const isSelectedCard = selectedFlowsComponentsCards?.includes(data?.id) ?? false; - function onDragStart(event: React.DragEvent) { - let image: JSX.Element = ; // <== whatever you want here - - var ghost = document.createElement("div"); - ghost.style.transform = "translate(-10000px, -10000px)"; - ghost.style.position = "absolute"; - document.body.appendChild(ghost); - event.dataTransfer.setDragImage(ghost, 0, 0); - const root = createRoot(ghost); - root.render(image); - const flow = getFlowById(data.id); - if (flow) { - event.dataTransfer.setData("flow", JSON.stringify(data)); - } - } + const { onDragStart } = useDragStart(data); return ( <> @@ -264,7 +197,7 @@ export default function CollectionCardComponent({ - {likes_count ?? 0} + {likesCount ?? 0} @@ -275,7 +208,7 @@ export default function CollectionCardComponent({ className="h-4 w-4" /> - {downloads_count ?? 0} + {downloadsCount ?? 0} @@ -324,20 +257,7 @@ export default function CollectionCardComponent({ )} )} -
- {/* {data.tags && - data.tags.length > 0 && - data.tags.map((tag, index) => ( - - {tag.name} - - ))} */} -
+
@@ -457,7 +377,7 @@ export default function CollectionCardComponent({ name="Heart" className={cn( "h-5 w-5", - liked_by_user + likedByUser ? "fill-destructive stroke-destructive" : "", !authorized ? "text-ring" : "", diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx new file mode 100644 index 000000000..1c9c0799e --- /dev/null +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { Link, useNavigate } from "react-router-dom"; +import CollectionCardComponent from "../../../../../../components/cardComponent"; +import IconComponent from "../../../../../../components/genericIconComponent"; +import { Button } from "../../../../../../components/ui/button"; +const CollectionCard = ({ item, type, isLoading, control }) => { + const navigate = useNavigate(); + const isComponent = item.is_component ?? false; + const editFlowLink = `/flow/${item.id}`; + const editFlowButtonTestId = `edit-flow-button-${item.id}`; + + const handleClick = () => { + if (!isComponent) { + navigate(editFlowLink); + } + }; + + const renderButton = () => { + if (!isComponent) { + return ( + + + + ); + } + return null; + }; + + return ( + + ); +}; + +export default CollectionCard; diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx index af13e2bb4..d60bf3a05 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx @@ -19,6 +19,7 @@ import { getNameByType } from "../../utils/get-name-by-type"; import { sortFlows } from "../../utils/sort-flows"; import EmptyComponent from "../emptyComponent"; import HeaderComponent from "../headerComponent"; +import CollectionCard from "./components/collectionCard"; import useDeleteMultipleFlows from "./hooks/use-delete-multiple"; import useDescriptionModal from "./hooks/use-description-modal"; import useFilteredFlows from "./hooks/use-filtered-flows"; @@ -61,7 +62,6 @@ export default function ComponentsComponent({ const [handleFileDrop] = useFileDrop(uploadFlow, type)!; const [pageSize, setPageSize] = useState(20); const [pageIndex, setPageIndex] = useState(1); - const navigate = useNavigate(); const location = useLocation(); const all: FlowType[] = sortFlows(allFlows, type); const start = (pageIndex - 1) * pageSize; @@ -205,43 +205,10 @@ export default function ComponentsComponent({ {data?.map((item) => (
- - - - ) : ( - <> - ) - } - onClick={ - !item.is_component - ? () => { - navigate("/flow/" + item.id); - } - : undefined - } - playground={!item.is_component} +