diff --git a/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx index 28674f3bc..9d24f9e26 100644 --- a/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx @@ -1,9 +1,10 @@ +import { FlowType } from "@/types/flow"; import { storeComponent } from "../../../../types/store"; import { cn } from "../../../../utils/utils"; import ForwardedIconComponent from "../../../genericIconComponent"; import { Card, CardHeader, CardTitle } from "../../../ui/card"; -export default function DragCardComponent({ data }: { data: storeComponent }) { +export default function DragCardComponent({ data }: { data: FlowType }) { return ( <> { +const useDragStart = (data: FlowType) => { const getFlowById = useFlowsManagerStore((state) => state.getFlowById); const onDragStart = useCallback( diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index c36aef291..5f53a0a20 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -1,17 +1,10 @@ -import { usePostLikeComponent } from "@/controllers/API/queries/store"; import { useState } from "react"; import { Control } from "react-hook-form"; -import { getComponent } from "../../controllers/API"; import IOModal from "../../modals/IOModal"; -import DeleteConfirmationModal from "../../modals/deleteConfirmationModal"; import useAlertStore from "../../stores/alertStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; -import { useStoreStore } from "../../stores/storeStore"; import { FlowType } from "../../types/flow"; -import { storeComponent } from "../../types/store"; -import cloneFLowWithParent, { - getInputsAndOutputs, -} from "../../utils/storeUtils"; +import { getInputsAndOutputs } from "../../utils/storeUtils"; import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; import ShadTooltip from "../shadTooltipComponent"; @@ -26,57 +19,28 @@ import { import { Checkbox } from "../ui/checkbox"; import { FormControl, FormField } from "../ui/form"; import Loading from "../ui/loading"; -import useDataEffect from "./hooks/use-data-effect"; -import useInstallComponent from "./hooks/use-handle-install"; import useDragStart from "./hooks/use-on-drag-start"; import { convertTestName } from "./utils/convert-test-name"; export default function CollectionCardComponent({ data, - authorized = true, disabled = false, - button, onClick, - onDelete, - playground, control, }: { - data: storeComponent; - authorized?: boolean; + data: FlowType; disabled?: boolean; onClick?: () => void; - button?: JSX.Element; - playground?: boolean; - onDelete?: () => void; control?: Control; }) { - 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 [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 setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const getFlowById = useFlowsManagerStore((state) => state.getFlowById); const [openPlayground, setOpenPlayground] = useState(false); const [loadingPlayground, setLoadingPlayground] = useState(false); - const selectedFlowsComponentsCards = useFlowsManagerStore( (state) => state.selectedFlowsComponentsCards, ); - - const name = data.is_component ? "Component" : "Flow"; - - async function getFlowData() { - const res = await getComponent(data.id); - const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true); - return newFlow; - } - function hasPlayground(flow?: FlowType) { if (!flow) { return false; @@ -84,52 +48,7 @@ export default function CollectionCardComponent({ const { inputs, outputs } = getInputsAndOutputs(flow?.data?.nodes ?? []); return inputs.length > 0 || outputs.length > 0; } - - useDataEffect(data, setLikedByUser, setLikesCount, setDownloadsCount); - - const { handleInstall } = useInstallComponent( - data, - name, - isStore, - downloadsCount, - setDownloadsCount, - setLoading, - setSuccessData, - setErrorData, - ); - - const { mutate, isPending } = usePostLikeComponent(); - - const handleLikeWMutate = () => { - if (likedByUser !== undefined || likedByUser !== null) { - const temp = likedByUser; - const tempNum = likesCount; - setLikedByUser((prev) => !prev); - setLikesCount((prev) => (temp ? prev - 1 : prev + 1)); - mutate( - { componentId: data.id }, - { - onSuccess: (res) => { - setLikesCount(res.data.likes_count); - setLikedByUser(res.data.liked_by_user); - }, - onError: (error) => { - setLikesCount(tempNum); - setLikedByUser(temp); - if (error.response.status === 403) { - return setValidApiKey(false); - } - console.error(error); - setErrorData({ - title: `Error liking ${name}.`, - list: [error.response.data.detail], - }); - }, - }, - ); - } - }; - + const playground = !(data.is_component ?? false); const isSelectedCard = selectedFlowsComponentsCards?.includes(data?.id) ?? false; @@ -165,51 +84,9 @@ export default function CollectionCardComponent({ )} name={data.is_component ? "ToyBrick" : "Group"} /> -
{data.name}
- {data?.metadata !== undefined && ( -
- {data.private && ( - - - - - - )} - {!data.is_component && ( - - - - - {data?.metadata?.total ?? 0} - - - - )} - - - - - {likesCount ?? 0} - - - - - - - - {downloadsCount ?? 0} - - - -
- )} - {control && (
- {data.user_created && data.user_created.username && ( - - by {data.user_created.username} - {data.last_tested_version && ( - <> - {" "} - |{" "} - - {" "} - ⛓︎ v{data.last_tested_version} - - - )} - - )}
- -
- {data.description} -
+
{data.description}
-
- {playground && data?.metadata !== undefined ? ( + {playground && ( - ) : undefined} - {data.liked_by_count != undefined && ( -
- {onDelete && data?.metadata !== undefined ? ( - - - - - - ) : ( - - - - )} - - - -
- )} - {playground && data?.metadata === undefined && ( - -
-
-
-
- ); -} diff --git a/src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx b/src/frontend/src/components/storeCardComponent/hooks/use-data-effect.tsx similarity index 100% rename from src/frontend/src/components/cardComponent/hooks/use-data-effect.tsx rename to src/frontend/src/components/storeCardComponent/hooks/use-data-effect.tsx diff --git a/src/frontend/src/components/storeCardComponent/hooks/use-handle-install.tsx b/src/frontend/src/components/storeCardComponent/hooks/use-handle-install.tsx new file mode 100644 index 000000000..2b5269348 --- /dev/null +++ b/src/frontend/src/components/storeCardComponent/hooks/use-handle-install.tsx @@ -0,0 +1,53 @@ +import useAddFlow from "@/hooks/flows/use-add-flow"; +import { getComponent } from "../../../controllers/API"; +import { storeComponent } from "../../../types/store"; +import cloneFlowWithParent from "../../../utils/storeUtils"; + +const useInstallComponent = ( + data: storeComponent, + name: string, + downloadsCount: number, + setDownloadsCount: (value: any) => void, + setLoading: (value: boolean) => void, + setSuccessData: (value: { title: string }) => void, + setErrorData: (value: { title: string; list: string[] }) => void, +) => { + const addFlow = useAddFlow(); + + 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({ flow: newFlow }) + .then((id) => { + setSuccessData({ + title: `${name} Installed Successfully.`, + }); + setLoading(false); + }) + .catch((error) => { + setLoading(false); + setErrorData({ + title: `Error installing the ${name}`, + list: [error.response.data.detail], + }); + }); + }) + .catch((err) => { + setLoading(false); + setErrorData({ + title: `Error installing the ${name}`, + list: [err.response.data.detail], + }); + setDownloadsCount(temp); + }); + }; + + return { handleInstall }; +}; + +export default useInstallComponent; diff --git a/src/frontend/src/components/storeCardComponent/index.tsx b/src/frontend/src/components/storeCardComponent/index.tsx new file mode 100644 index 000000000..652c413e4 --- /dev/null +++ b/src/frontend/src/components/storeCardComponent/index.tsx @@ -0,0 +1,328 @@ +import { usePostLikeComponent } from "@/controllers/API/queries/store"; +import { useState } from "react"; +import { getComponent } from "../../controllers/API"; +import IOModal from "../../modals/IOModal"; +import useAlertStore from "../../stores/alertStore"; +import useFlowsManagerStore from "../../stores/flowsManagerStore"; +import { useStoreStore } from "../../stores/storeStore"; +import { FlowType } from "../../types/flow"; +import { storeComponent } from "../../types/store"; +import cloneFLowWithParent, { + getInputsAndOutputs, +} from "../../utils/storeUtils"; +import { cn } from "../../utils/utils"; +import IconComponent from "../genericIconComponent"; +import ShadTooltip from "../shadTooltipComponent"; +import { Button } from "../ui/button"; +import { + Card, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../ui/card"; +import Loading from "../ui/loading"; +import useDataEffect from "./hooks/use-data-effect"; +import useInstallComponent from "./hooks/use-handle-install"; +import { convertTestName } from "./utils/convert-test-name"; + +export default function StoreCardComponent({ + data, + authorized = true, + disabled = false, +}: { + data: storeComponent; + authorized?: boolean; + disabled?: boolean; +}) { + const setSuccessData = useAlertStore((state) => state.setSuccessData); + const setErrorData = useAlertStore((state) => state.setErrorData); + const setValidApiKey = useStoreStore((state) => state.updateValidApiKey); + const [loading, setLoading] = useState(false); + 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 setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); + const [openPlayground, setOpenPlayground] = useState(false); + const [loadingPlayground, setLoadingPlayground] = useState(false); + const playground = + data.last_tested_version?.includes("1.0.0") && !data.is_component; + + const name = data.is_component ? "Component" : "Flow"; + + async function getFlowData() { + const res = await getComponent(data.id); + const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true); + return newFlow; + } + + function hasPlayground(flow?: FlowType) { + if (!flow) { + return false; + } + const { inputs, outputs } = getInputsAndOutputs(flow?.data?.nodes ?? []); + return inputs.length > 0 || outputs.length > 0; + } + + useDataEffect(data, setLikedByUser, setLikesCount, setDownloadsCount); + + const { handleInstall } = useInstallComponent( + data, + name, + downloadsCount, + setDownloadsCount, + setLoading, + setSuccessData, + setErrorData, + ); + + const { mutate, isPending } = usePostLikeComponent(); + + const handleLikeWMutate = () => { + if (likedByUser !== undefined || likedByUser !== null) { + const temp = likedByUser; + const tempNum = likesCount; + setLikedByUser((prev) => !prev); + setLikesCount((prev) => (temp ? prev - 1 : prev + 1)); + mutate( + { componentId: data.id }, + { + onSuccess: (res) => { + setLikesCount(res.data.likes_count); + setLikedByUser(res.data.liked_by_user); + }, + onError: (error) => { + setLikesCount(tempNum); + setLikedByUser(temp); + if (error.response.status === 403) { + return setValidApiKey(false); + } + console.error(error); + setErrorData({ + title: `Error liking ${name}.`, + list: [error.response.data.detail], + }); + }, + }, + ); + } + }; + + return ( + <> + +
+ +
+ + + + +
{data.name}
+
+
+ {data.private && ( + + + + + + )} + {!data.is_component && ( + + + + + {data?.metadata?.total ?? 0} + + + + )} + + + + + {likesCount ?? 0} + + + + + + + + {downloadsCount ?? 0} + + + +
+
+
+
+ {data.user_created && data.user_created.username && ( + + by {data.user_created.username} + {data.last_tested_version && ( + <> + {" "} + |{" "} + + {" "} + ⛓︎ v{data.last_tested_version} + + + )} + + )} +
+
+ + +
{data.description}
+
+
+
+ + +
+
+ {playground && ( + + )} +
+ + + + + + +
+
+
+
+
+ {openPlayground && ( + + <> + + )} + + ); +} diff --git a/src/frontend/src/components/storeCardComponent/utils/convert-test-name.tsx b/src/frontend/src/components/storeCardComponent/utils/convert-test-name.tsx new file mode 100644 index 000000000..068b7b585 --- /dev/null +++ b/src/frontend/src/components/storeCardComponent/utils/convert-test-name.tsx @@ -0,0 +1,3 @@ +export function convertTestName(name: string): string { + return name.replace(/ /g, "-").toLowerCase(); +} 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 index 906e8d1c3..ed50edb1d 100644 --- a/src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx +++ b/src/frontend/src/pages/MainPage/components/componentsComponent/components/collectionCard/index.tsx @@ -17,29 +17,6 @@ const CollectionCard = ({ item, type, isLoading, control }) => { } }; - const renderButton = () => { - if (!isComponent) { - return ( - - - - ); - } - return null; - }; - return ( { }} disabled={isLoading} data-testid={editFlowButtonTestId} - button={renderButton()!} onClick={!isComponent ? handleClick : undefined} - playground={!isComponent} control={control} /> ); diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index 5342eb248..bac2e604e 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -7,6 +7,7 @@ import ShadTooltip from "../../components/shadTooltipComponent"; import { SkeletonCardComponent } from "../../components/skeletonCardComponent"; import { Button } from "../../components/ui/button"; +import StoreCardComponent from "@/components/storeCardComponent"; import { useGetTagsQuery } from "@/controllers/API/queries/store"; import { Link, useNavigate, useParams } from "react-router-dom"; import PaginatorComponent from "../../components/paginatorComponent"; @@ -326,15 +327,11 @@ export default function StorePage(): JSX.Element { searchData.map((item) => { return ( <> - );