Skip to content

Commit 47a674e

Browse files
committed
Insight Dashboard
1 parent a7d0ecb commit 47a674e

File tree

15 files changed

+1212
-138
lines changed

15 files changed

+1212
-138
lines changed

apps/dashboard/src/@/components/ui/DynamicHeight.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function DynamicHeight(props: {
3434
);
3535
}
3636

37-
function useHeightObserver() {
37+
export function useHeightObserver() {
3838
const elementRef = useRef<HTMLDivElement>(null);
3939
const [height, setHeight] = useState<number | undefined>();
4040

apps/dashboard/src/@/components/ui/ScrollShadow/ScrollShadow.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
position: absolute;
33
left: 0;
44
width: 100%;
5-
height: 40px;
5+
height: 32px;
66
pointer-events: none;
77
}
88

99
.scrollShadowX {
1010
position: absolute;
1111
top: 0;
12-
width: 40px;
12+
width: 32px;
1313
height: 100%;
1414
pointer-events: none;
1515
}

apps/dashboard/src/@/components/ui/code/CodeBlockContainer.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export function CodeBlockContainer(props: {
1111
children: React.ReactNode;
1212
className?: string;
1313
scrollableClassName?: string;
14+
scrollableContainerClassName?: string;
1415
copyButtonClassName?: string;
16+
shadowColor?: string;
1517
}) {
1618
const { hasCopied, onCopy } = useClipboard(props.codeToCopy);
1719

@@ -24,8 +26,11 @@ export function CodeBlockContainer(props: {
2426
>
2527
<ScrollShadow
2628
scrollableClassName={cn("p-4", props.scrollableClassName)}
27-
className="text-xs md:text-sm [&_*]:leading-relaxed"
28-
shadowColor="hsl(var(--muted))"
29+
className={cn(
30+
"text-xs md:text-sm [&_*]:leading-relaxed",
31+
props.scrollableContainerClassName,
32+
)}
33+
shadowColor={props.shadowColor || "hsl(var(--muted))"}
2934
>
3035
{props.children}
3136
</ScrollShadow>

apps/dashboard/src/@/components/ui/code/RenderCode.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ export function RenderCode(props: {
66
className?: string;
77
scrollableClassName?: string;
88
copyButtonClassName?: string;
9+
scrollableContainerClassName?: string;
10+
shadowColor?: string;
911
}) {
1012
return (
1113
<CodeBlockContainer
1214
codeToCopy={props.code}
1315
className={props.className}
1416
copyButtonClassName={props.copyButtonClassName}
1517
scrollableClassName={props.scrollableClassName}
18+
scrollableContainerClassName={props.scrollableContainerClassName}
19+
shadowColor={props.shadowColor}
1620
>
1721
<div
1822
// biome-ignore lint/security/noDangerouslySetInnerHtml: we know what we're doing here

apps/dashboard/src/@/components/ui/code/code.client.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export type CodeProps = {
1212
scrollableClassName?: string;
1313
keepPreviousDataOnCodeChange?: boolean;
1414
copyButtonClassName?: string;
15+
scrollableContainerClassName?: string;
16+
shadowColor?: string;
1517
};
1618

1719
export const CodeClient: React.FC<CodeProps> = ({
@@ -21,6 +23,8 @@ export const CodeClient: React.FC<CodeProps> = ({
2123
scrollableClassName,
2224
keepPreviousDataOnCodeChange = false,
2325
copyButtonClassName,
26+
scrollableContainerClassName,
27+
shadowColor,
2428
}) => {
2529
const codeQuery = useQuery({
2630
queryKey: ["html", code],
@@ -38,6 +42,8 @@ export const CodeClient: React.FC<CodeProps> = ({
3842
className={className}
3943
scrollableClassName={scrollableClassName}
4044
copyButtonClassName={copyButtonClassName}
45+
scrollableContainerClassName={scrollableContainerClassName}
46+
shadowColor={shadowColor}
4147
/>
4248
);
4349
}
@@ -49,6 +55,8 @@ export const CodeClient: React.FC<CodeProps> = ({
4955
className={className}
5056
scrollableClassName={scrollableClassName}
5157
copyButtonClassName={copyButtonClassName}
58+
scrollableContainerClassName={scrollableContainerClassName}
59+
shadowColor={shadowColor}
5260
/>
5361
);
5462
};

apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ function isPrettierSupportedLang(lang: BundledLanguage) {
1010
lang === "ts" ||
1111
lang === "tsx" ||
1212
lang === "javascript" ||
13-
lang === "typescript" ||
14-
lang === "css" ||
15-
lang === "json"
13+
lang === "typescript"
1614
);
1715
}
1816

apps/dashboard/src/@/components/ui/code/plaintext-code.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ export function PlainTextCodeBlock(props: {
77
className?: string;
88
scrollableClassName?: string;
99
codeClassName?: string;
10+
scrollableContainerClassName?: string;
11+
shadowColor?: string;
1012
}) {
1113
return (
1214
<CodeBlockContainer
1315
codeToCopy={props.code}
1416
className={props.className}
1517
copyButtonClassName={props.copyButtonClassName}
1618
scrollableClassName={props.scrollableClassName}
19+
scrollableContainerClassName={props.scrollableContainerClassName}
20+
shadowColor={props.shadowColor}
1721
>
1822
<code className={cn("block whitespace-pre", props.codeClassName)}>
1923
{props.code}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { useMutation } from "@tanstack/react-query";
3+
import { useState } from "react";
4+
import { mobileViewport } from "../../../../../../stories/utils";
5+
import { BluePrintPlaygroundUI } from "./BluePrintPlayground";
6+
import type { BluePrintMetadata } from "./utils";
7+
8+
const meta = {
9+
title: "Insight/BluePrintPlayground",
10+
component: Story,
11+
parameters: {
12+
nextjs: {
13+
appDirectory: true,
14+
},
15+
},
16+
} satisfies Meta<typeof Story>;
17+
18+
export default meta;
19+
type Story = StoryObj<typeof meta>;
20+
21+
export const Desktop: Story = {
22+
args: {
23+
metadata: getBlueprintMetadata().transactionsMetadata,
24+
},
25+
};
26+
27+
export const Mobile: Story = {
28+
args: {
29+
metadata: getBlueprintMetadata().transactionsMetadata,
30+
},
31+
parameters: {
32+
viewport: mobileViewport("iphone14"),
33+
},
34+
};
35+
36+
function Story() {
37+
return (
38+
<div className="flex flex-col gap-10">
39+
<Variant metadata={getBlueprintMetadata().transactionsMetadata} />
40+
<Variant metadata={getBlueprintMetadata().eventsMetadata} />
41+
<Variant metadata={getBlueprintMetadata().tokensMetadata} />
42+
<Variant metadata={getBlueprintMetadata().largeNumberOfParamsMetadata} />
43+
</div>
44+
);
45+
}
46+
47+
function Variant(props: {
48+
metadata: BluePrintMetadata;
49+
}) {
50+
const [abortController, setAbortController] =
51+
useState<AbortController | null>(null);
52+
53+
const mutation = useMutation({
54+
mutationFn: async () => {
55+
const controller = new AbortController();
56+
setAbortController(controller);
57+
const start = performance.now();
58+
const promise = new Promise((resolve) =>
59+
setTimeout(resolve, Math.random() * 4000),
60+
);
61+
await Promise.race([
62+
promise,
63+
new Promise((_, reject) =>
64+
controller.signal.addEventListener("abort", reject),
65+
),
66+
]);
67+
68+
const dummyResponse = {
69+
data: {
70+
title: "This is a dummy response",
71+
content: crypto.getRandomValues(new Uint8Array(100)),
72+
},
73+
};
74+
75+
return {
76+
data: JSON.stringify(dummyResponse, null, 2),
77+
status: 200,
78+
time: performance.now() - start,
79+
};
80+
},
81+
});
82+
return (
83+
<div className="flex min-h-[800px] flex-col">
84+
<BluePrintPlaygroundUI
85+
metadata={props.metadata}
86+
backLink="/"
87+
isPending={mutation.isPending}
88+
onRun={async () => {
89+
mutation.mutateAsync();
90+
}}
91+
response={mutation.data}
92+
clientId="68665db28327c771c9a1bd5fc4580a0a"
93+
abortRequest={() => {
94+
abortController?.abort();
95+
}}
96+
/>
97+
</div>
98+
);
99+
}
100+
101+
function getBlueprintMetadata() {
102+
const transactionsMetadata: BluePrintMetadata = {
103+
domain: "https://{chainId}.insight.thirdweb.com",
104+
path: "/v1/{clientId}/transactions",
105+
parameters: [
106+
{
107+
name: "chainId",
108+
in: "path",
109+
required: true,
110+
description: "Chain ID",
111+
type: "string",
112+
},
113+
{
114+
name: "filter",
115+
in: "query",
116+
description: "Filter parameters",
117+
type: "string",
118+
},
119+
{
120+
name: "group_by",
121+
in: "query",
122+
description: "Field to group results by",
123+
type: "string",
124+
},
125+
{
126+
name: "sort_by",
127+
in: "query",
128+
description: "Field to sort results by",
129+
type: "string",
130+
},
131+
{
132+
name: "sort_order",
133+
in: "query",
134+
description: "Sort order (asc or desc)",
135+
type: "string",
136+
},
137+
{
138+
name: "page",
139+
in: "query",
140+
description: "Page number for pagination",
141+
type: "integer",
142+
},
143+
{
144+
name: "limit",
145+
in: "query",
146+
description: "Number of items per page",
147+
type: "integer",
148+
},
149+
{
150+
name: "aggregate",
151+
in: "query",
152+
description: "List of aggregate functions to apply",
153+
type: "array",
154+
},
155+
],
156+
title: "Transactions",
157+
description: "Retrieve all transactions across all contracts",
158+
};
159+
160+
const eventsMetadata: BluePrintMetadata = {
161+
domain: "https://{chainId}.insight.thirdweb.com",
162+
path: "/v1/{clientId}/events",
163+
parameters: [
164+
{
165+
name: "chainId",
166+
in: "path",
167+
required: true,
168+
description: "Chain ID",
169+
type: "string",
170+
},
171+
{
172+
name: "filter",
173+
in: "query",
174+
description: "Filter parameters",
175+
type: "string",
176+
},
177+
{
178+
name: "group_by",
179+
in: "query",
180+
description: "Field to group results by",
181+
type: "string",
182+
},
183+
{
184+
name: "sort_by",
185+
in: "query",
186+
description: "Field to sort results by",
187+
type: "string",
188+
},
189+
{
190+
name: "sort_order",
191+
in: "query",
192+
description: "Sort order (asc or desc)",
193+
type: "string",
194+
},
195+
{
196+
name: "page",
197+
in: "query",
198+
description: "Page number for pagination",
199+
type: "integer",
200+
},
201+
{
202+
name: "limit",
203+
in: "query",
204+
description: "Number of items per page",
205+
type: "integer",
206+
},
207+
{
208+
name: "aggregate",
209+
in: "query",
210+
description: "List of aggregate functions to apply",
211+
type: "array",
212+
},
213+
],
214+
title: "Events",
215+
description: "Retrieve all logs across all contracts",
216+
};
217+
218+
const tokensMetadata: BluePrintMetadata = {
219+
domain: "https://{chainId}.insight.thirdweb.com",
220+
path: "/v1/{clientId}/tokens/erc20/:ownerAddress",
221+
parameters: [
222+
{
223+
name: "ownerAddress",
224+
in: "path",
225+
required: true,
226+
type: "string",
227+
},
228+
{
229+
name: "clientId",
230+
in: "path",
231+
required: false,
232+
type: "string",
233+
},
234+
],
235+
title: "Tokens",
236+
description: "Retrieve tokens balances for a given owner address",
237+
};
238+
239+
const largeNumberOfParamsMetadata: BluePrintMetadata = {
240+
domain: "https://{chainId}.insight.thirdweb.com",
241+
path: "/v1/{clientId}/events",
242+
parameters: new Array(20).fill(null).map((_v, i) => {
243+
return {
244+
name: `param-name-${i}`,
245+
in: Math.random() > 0.5 ? "path" : "query",
246+
type: "string",
247+
};
248+
}),
249+
title: "Large Number of Params",
250+
description: "This blueprint has a large number of parameters",
251+
};
252+
253+
return {
254+
transactionsMetadata,
255+
eventsMetadata,
256+
tokensMetadata,
257+
largeNumberOfParamsMetadata,
258+
};
259+
}

0 commit comments

Comments
 (0)