Skip to content

Commit ae337a9

Browse files
committed
implements http request settings
1 parent 466f0b4 commit ae337a9

File tree

10 files changed

+511
-77
lines changed

10 files changed

+511
-77
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@trpc/client": "^11.7.1",
5151
"@trpc/server": "^11.7.1",
5252
"@trpc/tanstack-react-query": "^11.7.1",
53-
"@xyflow/react": "^12.9.2",
53+
"@xyflow/react": "^12.9.3",
5454
"ai": "^5.0.86",
5555
"better-auth": "^1.3.34",
5656
"class-variance-authority": "^0.7.1",

src/components/react-flow/BaseNode.tsx

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
11
import { cn } from "@/lib/utils";
22
import { forwardRef, type HTMLAttributes } from "react";
3+
import type { NodeStatus } from "./NodeStatusIndicator";
4+
import { CheckCircle2Icon, Loader2Icon, XCircleIcon } from "lucide-react";
35

4-
export const BaseNode = forwardRef<
5-
HTMLDivElement,
6-
HTMLAttributes<HTMLDivElement>
7-
>(({ className, ...props }, ref) => (
8-
<div
9-
ref={ref}
10-
className={cn(
11-
"relative rounded-md border bg-card text-card-foreground",
12-
"hover:ring-1",
13-
// React Flow displays node elements inside of a `NodeWrapper` component,
14-
// which compiles down to a div with the class `react-flow__node`.
15-
// When a node is selected, the class `selected` is added to the
16-
// `react-flow__node` element. This allows us to style the node when it
17-
// is selected, using Tailwind's `&` selector.
18-
"[.react-flow\\_\\_node.selected_&]:border-muted-foreground",
19-
"[.react-flow\\_\\_node.selected_&]:shadow-lg",
20-
className,
21-
)}
22-
// biome-ignore lint/a11y/noNoninteractiveTabindex: <shadcn>
23-
tabIndex={0}
24-
{...props}
25-
/>
26-
));
6+
interface BaseNodeProps extends HTMLAttributes<HTMLDivElement> {
7+
status?: NodeStatus;
8+
}
9+
10+
export const BaseNode = forwardRef<HTMLDivElement, BaseNodeProps>(
11+
({ className, status, ...props }, ref) => (
12+
<div
13+
ref={ref}
14+
className={cn(
15+
"relative rounded-sm border border-muted-foreground bg-card text-card-foreground hover:bg-accent",
16+
"hover:ring-1",
17+
className,
18+
)}
19+
// biome-ignore lint/a11y/noNoninteractiveTabindex: <shadcn>
20+
tabIndex={0}
21+
{...props}
22+
>
23+
{props.children}
24+
{status === "error" ? (
25+
<XCircleIcon className="absolute right-0.5 bottom-0.5 size-2 text-red-700 stroke-3" />
26+
) : null}
27+
{status === "success" ? (
28+
<CheckCircle2Icon className="absolute right-0.5 bottom-0.5 size-2 text-green-700 stroke-3" />
29+
) : null}
30+
{status === "loading" ? (
31+
<Loader2Icon className="absolute -right-0.5 -bottom-0.5 size-2 text-blue-700 stroke-3 animate-spin" />
32+
) : null}
33+
</div>
34+
),
35+
);
2736
BaseNode.displayName = "BaseNode";
2837

2938
/**
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import type { ReactNode } from "react";
2+
import { LoaderCircle } from "lucide-react";
3+
4+
import { cn } from "@/lib/utils";
5+
6+
export type NodeStatus = "loading" | "success" | "error" | "initial";
7+
8+
export type NodeStatusVariant = "overlay" | "border";
9+
10+
export type NodeStatusIndicatorProps = {
11+
status?: NodeStatus;
12+
variant?: NodeStatusVariant;
13+
children: ReactNode;
14+
className?: string;
15+
};
16+
17+
export const SpinnerLoadingIndicator = ({
18+
children,
19+
}: {
20+
children: ReactNode;
21+
}) => {
22+
return (
23+
<div className="relative">
24+
<StatusBorder className="border-blue-700/40">{children}</StatusBorder>
25+
26+
<div className="bg-background/50 absolute inset-0 z-50 rounded-[9px] backdrop-blur-xs" />
27+
<div className="absolute inset-0 z-50">
28+
<span className="absolute top-[calc(50%-1.25rem)] left-[calc(50%-1.25rem)] inline-block h-10 w-10 animate-ping rounded-full bg-blue-700/20" />
29+
30+
<LoaderCircle className="absolute top-[calc(50%-0.75rem)] left-[calc(50%-0.75rem)] size-6 animate-spin text-blue-700" />
31+
</div>
32+
</div>
33+
);
34+
};
35+
36+
export const BorderLoadingIndicator = ({
37+
children,
38+
className,
39+
}: {
40+
className?: string;
41+
children: ReactNode;
42+
}) => {
43+
return (
44+
<>
45+
<div className="absolute -top-[2px] -left-[2px] h-[calc(100%+4px)] w-[calc(100%+4px)]">
46+
<style>
47+
{`
48+
@keyframes spin {
49+
from { transform: translate(-50%, -50%) rotate(0deg); }
50+
to { transform: translate(-50%, -50%) rotate(360deg); }
51+
}
52+
.spinner {
53+
animation: spin 2s linear infinite;
54+
position: absolute;
55+
left: 50%;
56+
top: 50%;
57+
width: 140%;
58+
aspect-ratio: 1;
59+
transform-origin: center;
60+
}
61+
`}
62+
</style>
63+
<div
64+
className={cn(
65+
"absolute inset-0 overflow-hidden rounded-sm",
66+
className,
67+
)}
68+
>
69+
<div className="spinner rounded-full bg-[conic-gradient(from_0deg_at_50%_50%,rgba(42,67,233,0.5)_0deg,rgba(42,138,246,0)_360deg)]" />
70+
</div>
71+
</div>
72+
{children}
73+
</>
74+
);
75+
};
76+
77+
const StatusBorder = ({
78+
children,
79+
className,
80+
}: {
81+
children: ReactNode;
82+
className?: string;
83+
}) => {
84+
return (
85+
<>
86+
<div
87+
className={cn(
88+
"absolute -top-[2px] -left-[2px] h-[calc(100%+4px)] w-[calc(100%+4px)] rounded-md border-3",
89+
className,
90+
)}
91+
/>
92+
{children}
93+
</>
94+
);
95+
};
96+
97+
export const NodeStatusIndicator = ({
98+
status,
99+
variant = "border",
100+
children,
101+
className,
102+
}: NodeStatusIndicatorProps) => {
103+
switch (status) {
104+
case "loading":
105+
switch (variant) {
106+
case "overlay":
107+
return <SpinnerLoadingIndicator>{children}</SpinnerLoadingIndicator>;
108+
case "border":
109+
return (
110+
<BorderLoadingIndicator className={className}>
111+
{children}
112+
</BorderLoadingIndicator>
113+
);
114+
default:
115+
return <>{children}</>;
116+
}
117+
case "success":
118+
return (
119+
<StatusBorder className={cn("border-green-700/50", className)}>
120+
{children}
121+
</StatusBorder>
122+
);
123+
case "error":
124+
return (
125+
<StatusBorder className={cn("border-red-700/50", className)}>
126+
{children}
127+
</StatusBorder>
128+
);
129+
default:
130+
return <>{children}</>;
131+
}
132+
};

src/features/executions/components/BaseExecutionNode.tsx

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import { WorkflowNode } from "@/components/react-flow/WorkflowNode";
77
import { BaseNode, BaseNodeContent } from "@/components/react-flow/BaseNode";
88
import Image from "next/image";
99
import { BaseHandle } from "@/components/react-flow/BaseHandle";
10+
import {
11+
NodeStatusIndicator,
12+
type NodeStatus,
13+
} from "@/components/react-flow/NodeStatusIndicator";
1014

1115
interface BaseExecutionNodeProps extends NodeProps {
1216
icon: LucideIcon | string;
1317
name: string;
1418
description?: string;
1519
children?: ReactNode;
16-
// status?: NodeStatus;
20+
status?: NodeStatus;
1721
showToolbar?: boolean;
1822
onSettings?: () => void;
1923
onDoubleClick?: () => void;
@@ -29,6 +33,7 @@ const BaseExecutionNode = memo(
2933
onSettings,
3034
onDoubleClick,
3135
showToolbar = false,
36+
status = "initial",
3237
}: BaseExecutionNodeProps) => {
3338
const { setNodes, setEdges } = useReactFlow();
3439

@@ -51,18 +56,28 @@ const BaseExecutionNode = memo(
5156
onDeleteAction={onDelete}
5257
showToolbar={showToolbar}
5358
>
54-
<BaseNode onDoubleClick={onDoubleClick}>
55-
<BaseNodeContent>
56-
{typeof Icon === "string" ? (
57-
<Image src={Icon} alt={name} width={16} height={16} />
58-
) : (
59-
<Icon className="size-4 text-muted-foreground" />
60-
)}
61-
{children}
62-
<BaseHandle id="target-1" type="target" position={Position.Left} />
63-
<BaseHandle id="source-1" type="source" position={Position.Right} />
64-
</BaseNodeContent>
65-
</BaseNode>
59+
<NodeStatusIndicator status={status} variant="border">
60+
<BaseNode status={status} onDoubleClick={onDoubleClick}>
61+
<BaseNodeContent>
62+
{typeof Icon === "string" ? (
63+
<Image src={Icon} alt={name} width={16} height={16} />
64+
) : (
65+
<Icon className="size-4 text-muted-foreground" />
66+
)}
67+
{children}
68+
<BaseHandle
69+
id="target-1"
70+
type="target"
71+
position={Position.Left}
72+
/>
73+
<BaseHandle
74+
id="source-1"
75+
type="source"
76+
position={Position.Right}
77+
/>
78+
</BaseNodeContent>
79+
</BaseNode>
80+
</NodeStatusIndicator>
6681
</WorkflowNode>
6782
);
6883
},

0 commit comments

Comments
 (0)