Fixed errors on code and made MarketCard default

This commit is contained in:
Lucas Oliveira 2023-11-16 18:49:08 -03:00
commit f7903043fe
10 changed files with 308 additions and 368 deletions

View file

@ -1,6 +1,17 @@
import { cardComponentPropsType } from "../../types/components";
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { StoreContext } from "../../contexts/storeContext";
import { getComponent, postLikeComponent } from "../../controllers/API";
import { storeComponent } from "../../types/store";
import cloneFLowWithParent from "../../utils/storeUtils";
import { gradients } from "../../utils/styleUtils";
import { classNames } from "../../utils/utils";
import ShadTooltip from "../ShadTooltipComponent";
import IconComponent from "../genericIconComponent";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import {
Card,
CardDescription,
@ -9,47 +20,288 @@ import {
CardTitle,
} from "../ui/card";
export const CardComponent = ({
export default function CollectionCardComponent({
data,
onDelete,
authorized = true,
disabled = false,
button,
}: cardComponentPropsType): JSX.Element => {
return (
<Card className="group">
<CardHeader>
<CardTitle className="card-component-title-display">
<span
className={
"card-component-image " +
gradients[parseInt(data.id.slice(0, 12), 16) % gradients.length]
}
></span>
<span className="card-component-title-size">{data.name}</span>
{onDelete && (
<button className="card-component-delete-button" onClick={onDelete}>
<IconComponent
name="Trash2"
className="card-component-delete-icon"
/>
</button>
)}
</CardTitle>
<CardDescription className="card-component-desc">
<div className="card-component-desc-text">
{data.description}
{/* {flow.description} */}
</div>
</CardDescription>
</CardHeader>
onDelete,
}: {
data: storeComponent;
authorized?: boolean;
disabled?: boolean;
button?: JSX.Element;
onDelete?: () => void;
}) {
const [loading, setLoading] = useState(false);
const { addFlow } = useContext(FlowsContext);
const [loadingLike, setLoadingLike] = useState(false);
const { setSuccessData, setErrorData } = useContext(alertContext);
const { setValidApiKey } = useContext(StoreContext);
const [liked_by_user, setLiked_by_user] = useState(data.liked_by_user);
const [likes_count, setLikes_count] = useState(data.liked_by_count ?? 0);
{button && (
<CardFooter>
<div className="card-component-footer-arrangement">
<div className="card-component-footer"></div>
{button}
</div>
</CardFooter>
const name = data.is_component ? "Component" : "Flow";
const navigate = useNavigate();
function handleInstall() {
setLoading(true);
getComponent(data.id).then((res) => {
const newFlow = cloneFLowWithParent(res, res.id, data.is_component);
addFlow(true, newFlow).then((id) => {
setSuccessData({ title: `${name} Installed` });
setLoading(false);
if (!data.is_component) navigate("/flow/" + id);
});
});
}
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) => prev + 1);
} else {
setLikes_count((prev) => prev - 1);
}
console.log(data.id);
postLikeComponent(data.id)
.then((response) => {
setLoadingLike(false);
setLikes_count(response.likes_count);
setLiked_by_user(response.liked_by_user);
})
.catch((error) => {
setLoadingLike(false);
setLikes_count(tempNum);
setLiked_by_user(temp);
if (error.response.status === 403 || error.response.status === 401) {
setValidApiKey(false);
} else {
console.error(error);
setErrorData({
title: `Error liking ${name}.`,
list: [error["response"]["data"]["detail"]],
});
}
});
}
}
const totalComponentsMetadata = () => {
return data?.metadata ? data.metadata["total"] : 0;
};
return (
<Card
className={classNames(
"group relative flex flex-col justify-between overflow-hidden transition-all hover:shadow-md",
disabled ? "pointer-events-none opacity-50" : ""
)}
>
<div>
<CardHeader>
<div>
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
<div
className={classNames(
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary",
gradients[
parseInt(data.id.slice(0, 12), 16) % gradients.length
]
)}
>
<div
className={classNames(
data.is_component ? "h-7 w-7 rounded-full bg-muted" : "",
"flex items-center justify-center"
)}
>
{data.is_component ? (
<svg className="h-5 w-5" viewBox="0 0 24 24">
<defs>
<linearGradient
id={data.id}
x1="0%"
y1="0%"
x2="100%"
y2="100%"
className={
gradients[
parseInt(data.id.slice(0, 12), 16) %
gradients.length
]
}
>
<stop
offset="0%"
stopColor="var(--tw-gradient-from)"
/>
<stop
offset="100%"
stopColor="var(--tw-gradient-to)"
/>
</linearGradient>
</defs>
<IconComponent
className={classNames(
"h-4 w-4",
gradients[
parseInt(data.id.slice(0, 12), 16) %
gradients.length
]
)}
stroke={`url(#${data.id})`}
name="ToyBrick"
/>
</svg>
) : (
<IconComponent
className="h-4 w-4 text-background"
name="Network"
/>
)}
</div>
</div>
<ShadTooltip content={data.name}>
<div className="w-full truncate">{data.name}</div>
</ShadTooltip>
{data?.metadata && (
<div className="flex gap-3">
{!data.is_component && (
<ShadTooltip content="Components">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="ToyBrick" className="h-4 w-4" />
{totalComponentsMetadata()}
</span>
</ShadTooltip>
)}
<ShadTooltip content="Likes">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent
name="Heart"
className={classNames("h-4 w-4 ")}
/>
{likes_count ?? 0}
</span>
</ShadTooltip>
<ShadTooltip content="Downloads">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="DownloadCloud" className="h-4 w-4" />
{data.downloads_count}
</span>
</ShadTooltip>
</div>
)}
{onDelete && (
<button onClick={onDelete}>
<IconComponent
name="Trash2"
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
/>
</button>
)}
</CardTitle>
</div>
{data.user_created && data.user_created.username && (
<span className="text-xs text-primary">
by <b>{data.user_created.username}</b>
</span>
)}
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
</CardDescription>
</CardHeader>
</div>
<CardFooter>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2">
<div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags &&
data.tags.length > 0 &&
data.tags.map((tag, index) => (
<Badge
key={index}
variant="outline"
size="xq"
className="text-muted-foreground"
>
{tag.name}
</Badge>
))}
</div>
{data.liked_by_count && (
<div className="flex gap-0.5">
<ShadTooltip
content={authorized ? "Like" : "Please review your API key."}
>
<Button
disabled={loadingLike || !authorized}
variant="ghost"
size="xs"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
onClick={() => {
handleLike();
}}
>
<IconComponent
name="Heart"
className={classNames(
"h-6 w-6 p-0.5",
liked_by_user
? "fill-destructive stroke-destructive"
: "",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</ShadTooltip>
<ShadTooltip
content={
authorized
? "Install Locally"
: "Please review your API key."
}
>
<Button
variant="ghost"
size="xs"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
onClick={() => {
if (loading || !authorized) {
return;
}
handleInstall();
}}
>
<IconComponent
name={loading ? "Loader2" : "Plus"}
className={classNames(
loading ? "h-5 w-5 animate-spin" : "h-6 w-6 p-0.5",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</ShadTooltip>
</div>
)}
{button && button}
</div>
</div>
</CardFooter>
</Card>
);
};
}

View file

@ -29,7 +29,7 @@ import {
sourceHandleType,
targetHandleType,
} from "../types/flow";
import { FlowsContextType, TabsState } from "../types/tabs";
import { FlowsContextType, FlowsState } from "../types/tabs";
import {
addVersionToDuplicates,
checkOldEdgesHandles,
@ -73,7 +73,7 @@ const FlowsContextInitialValue: FlowsContextType = {
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
tabsState: {},
setTabsState: (state: TabsState) => {},
setTabsState: (state: FlowsState) => {},
getNodeId: (nodeType: string) => "",
setTweak: (tweak: any) => {},
getTweak: [],
@ -107,7 +107,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
nodes: any;
edges: any;
} | null>(null);
const [tabsState, setTabsState] = useState<TabsState>({});
const [tabsState, setTabsState] = useState<FlowsState>({});
const [getTweak, setTweak] = useState<tweakType>([]);
useEffect(() => {
@ -698,7 +698,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
` (${increment})`;
}
}
return addFlow(true, createFlowComponent(component));
return addFlow(true, createFlowComponent(component, version));
}
function deleteComponent(id: string, key: string) {

View file

@ -1,5 +1,5 @@
import { useContext } from "react";
import { CardComponent } from "../../../../components/cardComponent";
import CollectionCardComponent from "../../../../components/cardComponent";
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
import { alertContext } from "../../../../contexts/alertContext";
import { FlowsContext } from "../../../../contexts/flowsContext";
@ -31,7 +31,7 @@ export default function ComponentsComponent() {
{flows
.filter((flow) => flow.is_component)
.map((flow, idx) => (
<CardComponent
<CollectionCardComponent
key={idx}
data={flow}
onDelete={() => {

View file

@ -1,6 +1,6 @@
import { useContext } from "react";
import { Link } from "react-router-dom";
import { CardComponent } from "../../../../components/cardComponent";
import CollectionCardComponent from "../../../../components/cardComponent";
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { Button } from "../../../../components/ui/button";
@ -34,7 +34,7 @@ export default function FlowsComponent() {
.filter((flow) => !flow.is_component)
.reverse()
.map((flow, idx) => (
<CardComponent
<CollectionCardComponent
key={idx}
data={flow}
button={

View file

@ -1,286 +0,0 @@
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import ShadTooltip from "../../../components/ShadTooltipComponent";
import IconComponent from "../../../components/genericIconComponent";
import { Badge } from "../../../components/ui/badge";
import { Button } from "../../../components/ui/button";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "../../../components/ui/card";
import { alertContext } from "../../../contexts/alertContext";
import { FlowsContext } from "../../../contexts/flowsContext";
import { StoreContext } from "../../../contexts/storeContext";
import { getComponent, postLikeComponent } from "../../../controllers/API";
import { storeComponent } from "../../../types/store";
import cloneFLowWithParent from "../../../utils/storeUtils";
import { gradients } from "../../../utils/styleUtils";
import { classNames } from "../../../utils/utils";
export const MarketCardComponent = ({
data,
authorized = true,
disabled = false,
}: {
data: storeComponent;
authorized?: boolean;
disabled?: boolean;
}) => {
const [loading, setLoading] = useState(false);
const { addFlow } = useContext(FlowsContext);
const [loadingLike, setLoadingLike] = useState(false);
const { setSuccessData, setErrorData } = useContext(alertContext);
const { setValidApiKey } = useContext(StoreContext);
const [liked_by_user, setLiked_by_user] = useState(data.liked_by_user);
const [likes_count, setLikes_count] = useState(data.liked_by_count ?? 0);
const name = data.is_component ? "Component" : "Flow";
const navigate = useNavigate();
function handleInstall() {
setLoading(true);
getComponent(data.id).then((res) => {
const newFlow = cloneFLowWithParent(res, res.id, data.is_component);
addFlow(true, newFlow).then((id) => {
setSuccessData({ title: `${name} Installed` });
setLoading(false);
if (!data.is_component) navigate("/flow/" + id);
});
});
}
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) => prev + 1);
} else {
setLikes_count((prev) => prev - 1);
}
console.log(data.id);
postLikeComponent(data.id)
.then((response) => {
setLoadingLike(false);
setLikes_count(response.likes_count);
setLiked_by_user(response.liked_by_user);
})
.catch((error) => {
setLoadingLike(false);
setLikes_count(tempNum);
setLiked_by_user(temp);
if (error.response.status === 403 || error.response.status === 401) {
setValidApiKey(false);
} else {
console.error(error);
setErrorData({
title: `Error liking ${name}.`,
list: [error["response"]["data"]["detail"]],
});
}
});
}
}
const totalComponentsMetadata = () => {
return data?.metadata ? data.metadata["total"] : 0;
};
return (
<Card
className={classNames(
"group relative flex flex-col justify-between overflow-hidden transition-all hover:shadow-md",
disabled ? "pointer-events-none opacity-50" : ""
)}
>
<div>
<CardHeader>
<div>
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
<div
className={classNames(
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-primary",
gradients[
parseInt(data.id.slice(0, 12), 16) % gradients.length
]
)}
>
<div
className={classNames(
data.is_component ? "h-7 w-7 rounded-full bg-muted" : "",
"flex items-center justify-center"
)}
>
{data.is_component ? (
<svg className="h-5 w-5" viewBox="0 0 24 24">
<defs>
<linearGradient
id={data.id}
x1="0%"
y1="0%"
x2="100%"
y2="100%"
className={
gradients[
parseInt(data.id.slice(0, 12), 16) %
gradients.length
]
}
>
<stop
offset="0%"
stopColor="var(--tw-gradient-from)"
/>
<stop
offset="100%"
stopColor="var(--tw-gradient-to)"
/>
</linearGradient>
</defs>
<IconComponent
className={classNames(
"h-4 w-4",
gradients[
parseInt(data.id.slice(0, 12), 16) %
gradients.length
]
)}
stroke={`url(#${data.id})`}
name="ToyBrick"
/>
</svg>
) : (
<IconComponent
className="h-4 w-4 text-background"
name="Network"
/>
)}
</div>
</div>
<ShadTooltip content={data.name}>
<div className="w-full truncate">{data.name}</div>
</ShadTooltip>
<div className="flex gap-3">
{!data.is_component && (
<ShadTooltip content="Components">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="ToyBrick" className="h-4 w-4" />
{totalComponentsMetadata()}
</span>
</ShadTooltip>
)}
<ShadTooltip content="Likes">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent
name="Heart"
className={classNames("h-4 w-4 ")}
/>
{likes_count ?? 0}
</span>
</ShadTooltip>
<ShadTooltip content="Downloads">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="DownloadCloud" className="h-4 w-4" />
{data.downloads_count}
</span>
</ShadTooltip>
</div>
</CardTitle>
</div>
{data.user_created.username && (
<span className="text-xs text-primary">
by <b>{data.user_created.username}</b>
</span>
)}
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
</CardDescription>
</CardHeader>
</div>
<CardFooter>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2">
<div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags.length > 0 &&
data.tags.map((tag, index) => (
<Badge
key={index}
variant="outline"
size="xq"
className="text-muted-foreground"
>
{tag.name}
</Badge>
))}
</div>
<div className="flex gap-0.5">
<ShadTooltip
content={authorized ? "Like" : "Please review your API key."}
>
<Button
disabled={loadingLike || !authorized}
variant="ghost"
size="xs"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
onClick={() => {
handleLike();
}}
>
<IconComponent
name="Heart"
className={classNames(
"h-6 w-6 p-0.5",
liked_by_user
? "fill-destructive stroke-destructive"
: "",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</ShadTooltip>
<ShadTooltip
content={
authorized ? "Install Locally" : "Please review your API key."
}
>
<Button
variant="ghost"
size="xs"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
onClick={() => {
if (loading || !authorized) {
return;
}
handleInstall();
}}
>
<IconComponent
name={loading ? "Loader2" : "Plus"}
className={classNames(
loading ? "h-5 w-5 animate-spin" : "h-6 w-6 p-0.5",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</ShadTooltip>
</div>
</div>
</div>
</CardFooter>
</Card>
);
};

View file

@ -1,6 +1,7 @@
import { useContext, useEffect, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import CollectionCardComponent from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
@ -21,7 +22,6 @@ import { getStoreComponents, getStoreTags } from "../../controllers/API";
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
import { storeComponent } from "../../types/store";
import { cn } from "../../utils/utils";
import { MarketCardComponent } from "./components/market-card";
export default function StorePage(): JSX.Element {
const { validApiKey, setValidApiKey, hasApiKey, loadingApiKey } =
useContext(StoreContext);
@ -326,7 +326,7 @@ export default function StorePage(): JSX.Element {
searchData.map((item) => {
return (
<>
<MarketCardComponent
<CollectionCardComponent
key={item.id}
data={item}
authorized={validApiKey}

View file

@ -353,34 +353,6 @@
@apply alert-font-size text-success-foreground word-break-break-word;
}
.card-component-title-display {
@apply round-button-div flex-max-width;
}
.card-component-image {
@apply flex h-7 w-7 items-center justify-center rounded-full text-2xl;
}
.card-component-title-size {
@apply w-full flex-1 truncate-doubleline word-break-break-word;
}
.card-component-delete-button {
@apply flex self-start;
}
.card-component-delete-icon {
@apply h-4 w-4 text-primary opacity-0 transition-all group-hover:opacity-100;
}
.card-component-desc {
@apply pb-2 pt-2;
}
.card-component-desc-text {
@apply truncate-doubleline;
}
.card-component-footer-arrangement {
@apply flex-max-width items-end justify-between gap-2;
}
.card-component-footer {
@apply flex flex-wrap gap-2;
}
.unused-side-bar-aside {
@apply flex flex-shrink-0 flex-col overflow-hidden border-r transition-all duration-500;
}

View file

@ -7,7 +7,7 @@ export type FlowType = {
data: ReactFlowJsonObject | null;
description: string;
style?: FlowStyleType;
is_component?: boolean;
is_component: boolean;
parent?: string;
date_created?: string;
last_tested_version?: string;

View file

@ -1,14 +1,14 @@
export type storeComponent = {
id: string;
is_component: boolean;
tags: { id: string; name: string }[];
tags?: { id: string; name: string }[];
metadata?: {};
downloads_count: number;
downloads_count?: number;
name: string;
description: string;
liked_by_count: number;
liked_by_count?: number;
liked_by_user?: boolean;
user_created: { username: string };
user_created?: { username: string };
};
export type StoreComponentResponse = {

View file

@ -8,6 +8,7 @@ import {
ReactFlowJsonObject,
XYPosition,
} from "reactflow";
import ShortUniqueId from "short-unique-id";
import { specialCharsRegex } from "../constants/constants";
import { APITemplateType, TemplateVariableType } from "../types/api";
import {
@ -519,6 +520,7 @@ export function generateFlow(
const newFlow: FlowType = {
data: newFlowData,
is_component: false,
name: name,
description: "",
//generating local id instead of using the id from the server, can change in the future