Skip to content

Commit 884d40a

Browse files
committed
[TOOL-3112]: Various Engine Overview page improvements, Chart style adjustments, Improved Pagination component
1 parent dbb64ea commit 884d40a

File tree

26 files changed

+784
-463
lines changed

26 files changed

+784
-463
lines changed

apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
108108
: [0, 0, 0, 0]
109109
: [4, 4, 4, 4]
110110
}
111+
strokeWidth={1}
112+
className="stroke-background"
111113
/>
112114
))}
113115
</BarChart>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { useState } from "react";
3+
import { BadgeContainer, mobileViewport } from "../../stories/utils";
4+
import { PaginationButtons } from "./pagination-buttons";
5+
6+
const meta = {
7+
title: "blocks/PaginationButtons",
8+
component: Story,
9+
parameters: {
10+
nextjs: {
11+
appDirectory: true,
12+
},
13+
},
14+
} satisfies Meta<typeof Story>;
15+
16+
export default meta;
17+
type Story = StoryObj<typeof meta>;
18+
19+
export const Desktop: Story = {
20+
args: {},
21+
};
22+
23+
export const Mobile: Story = {
24+
args: {},
25+
parameters: {
26+
viewport: mobileViewport("iphone14"),
27+
},
28+
};
29+
30+
function Story() {
31+
return (
32+
<div className="container flex max-w-[1000px] flex-col gap-8 py-10">
33+
<Variant label="10 Pages" totalPages={10} />
34+
<Variant label="100 Pages" totalPages={100} />
35+
<Variant label="3 Pages" totalPages={2} />
36+
<Variant label="6 Pages" totalPages={6} />
37+
<Variant label="1 Page - nothing rendered" totalPages={1} />
38+
</div>
39+
);
40+
}
41+
42+
function Variant(props: {
43+
label: string;
44+
totalPages: number;
45+
}) {
46+
const [activePage, setActivePage] = useState(1);
47+
return (
48+
<BadgeContainer label={props.label}>
49+
<div className="border bg-card py-5">
50+
<PaginationButtons
51+
activePage={activePage}
52+
onPageClick={setActivePage}
53+
totalPages={props.totalPages}
54+
/>
55+
</div>
56+
</BadgeContainer>
57+
);
58+
}

apps/dashboard/src/@/components/pagination-buttons.tsx

Lines changed: 114 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,59 @@ import {
99
PaginationNext,
1010
PaginationPrevious,
1111
} from "@/components/ui/pagination";
12+
import { ArrowUpRightIcon } from "lucide-react";
13+
import { useState } from "react";
14+
import { cn } from "../lib/utils";
15+
import { Button } from "./ui/button";
16+
import { Input } from "./ui/input";
1217

1318
export const PaginationButtons = (props: {
1419
activePage: number;
1520
totalPages: number;
1621
onPageClick: (page: number) => void;
1722
}) => {
1823
const { activePage, totalPages, onPageClick: setPage } = props;
24+
const [inputHasError, setInputHasError] = useState(false);
25+
const [pageNumberInput, setPageNumberInput] = useState("");
26+
27+
if (totalPages === 1) {
28+
return null;
29+
}
30+
31+
function handlePageInputSubmit() {
32+
const page = Number(pageNumberInput);
33+
34+
setInputHasError(false);
35+
if (Number.isInteger(page) && page > 0 && page <= totalPages) {
36+
setPage(page);
37+
setPageNumberInput("");
38+
} else {
39+
setInputHasError(true);
40+
}
41+
}
42+
43+
// just render all the page buttons directly
44+
if (totalPages <= 6) {
45+
const pages = [...Array(totalPages)].map((_, i) => i + 1);
46+
return (
47+
<Pagination>
48+
<PaginationContent>
49+
{pages.map((page) => (
50+
<PaginationItem key={page}>
51+
<PaginationLink
52+
isActive={activePage === page}
53+
onClick={() => {
54+
setPage(page);
55+
}}
56+
>
57+
{page}
58+
</PaginationLink>
59+
</PaginationItem>
60+
))}
61+
</PaginationContent>
62+
</Pagination>
63+
);
64+
}
1965

2066
return (
2167
<Pagination>
@@ -28,24 +74,28 @@ export const PaginationButtons = (props: {
2874
}}
2975
/>
3076
</PaginationItem>
77+
78+
{/* First page + ... */}
3179
{activePage - 3 > 0 && (
32-
<PaginationItem className="max-sm:hidden">
33-
<PaginationEllipsis />
34-
</PaginationItem>
35-
)}
36-
{activePage - 2 > 0 && (
37-
<PaginationItem className="max-sm:hidden">
38-
<PaginationLink
39-
onClick={() => {
40-
setPage(activePage - 2);
41-
}}
42-
>
43-
{activePage - 2}
44-
</PaginationLink>
45-
</PaginationItem>
80+
<>
81+
<PaginationItem>
82+
<PaginationLink
83+
onClick={() => {
84+
setPage(1);
85+
}}
86+
>
87+
1
88+
</PaginationLink>
89+
</PaginationItem>
90+
91+
<PaginationItem>
92+
<PaginationEllipsis className="max-sm:w-3" />
93+
</PaginationItem>
94+
</>
4695
)}
96+
4797
{activePage - 1 > 0 && (
48-
<PaginationItem>
98+
<PaginationItem className="max-sm:hidden">
4999
<PaginationLink
50100
onClick={() => {
51101
setPage(activePage - 1);
@@ -55,11 +105,13 @@ export const PaginationButtons = (props: {
55105
</PaginationLink>
56106
</PaginationItem>
57107
)}
108+
58109
<PaginationItem>
59110
<PaginationLink isActive>{activePage}</PaginationLink>
60111
</PaginationItem>
112+
61113
{activePage + 1 <= totalPages && (
62-
<PaginationItem>
114+
<PaginationItem className="max-sm:hidden">
63115
<PaginationLink
64116
onClick={() => {
65117
setPage(activePage + 1);
@@ -69,22 +121,26 @@ export const PaginationButtons = (props: {
69121
</PaginationLink>
70122
</PaginationItem>
71123
)}
72-
{activePage + 2 <= totalPages && (
73-
<PaginationItem className="max-sm:hidden">
74-
<PaginationLink
75-
onClick={() => {
76-
setPage(activePage + 2);
77-
}}
78-
>
79-
{activePage + 2}
80-
</PaginationLink>
81-
</PaginationItem>
82-
)}
124+
125+
{/* ... + Last page */}
83126
{activePage + 3 <= totalPages && (
84-
<PaginationItem className="max-sm:hidden">
85-
<PaginationEllipsis />
86-
</PaginationItem>
127+
<>
128+
<PaginationItem>
129+
<PaginationEllipsis className="max-sm:w-3" />
130+
</PaginationItem>
131+
132+
<PaginationItem>
133+
<PaginationLink
134+
onClick={() => {
135+
setPage(totalPages);
136+
}}
137+
>
138+
{totalPages}
139+
</PaginationLink>
140+
</PaginationItem>
141+
</>
87142
)}
143+
88144
<PaginationItem>
89145
<PaginationNext
90146
disabled={activePage === totalPages}
@@ -93,6 +149,34 @@ export const PaginationButtons = (props: {
93149
}}
94150
/>
95151
</PaginationItem>
152+
153+
<div className="relative flex items-center">
154+
<Input
155+
value={pageNumberInput}
156+
onChange={(e) => {
157+
setInputHasError(false);
158+
setPageNumberInput(e.target.value);
159+
}}
160+
type="number"
161+
placeholder="Page"
162+
className={cn(
163+
"w-[60px] bg-transparent [appearance:textfield] max-sm:placeholder:text-sm lg:w-[100px] lg:pr-8 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
164+
inputHasError && "text-red-500",
165+
)}
166+
onKeyDown={(e) => {
167+
if (e.key === "Enter") {
168+
handlePageInputSubmit();
169+
}
170+
}}
171+
/>
172+
<Button
173+
variant="ghost"
174+
className="absolute right-1 h-auto w-auto p-2 max-sm:hidden"
175+
onClick={handlePageInputSubmit}
176+
>
177+
<ArrowUpRightIcon className="size-4" />
178+
</Button>
179+
</div>
96180
</PaginationContent>
97181
</Pagination>
98182
);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ const ChartTooltipContent = React.forwardRef<
186186
<div
187187
ref={ref}
188188
className={cn(
189-
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
189+
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-popover px-2.5 py-1.5 text-xs shadow-xl",
190190
className,
191191
)}
192192
>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ const PaginationPrevious = ({
6363
<PaginationLink
6464
aria-label="Go to previous page"
6565
size="default"
66-
className={cn("gap-1 pl-2.5", className)}
66+
className={cn("h-10 gap-1 pl-2.5", className)}
6767
{...props}
6868
>
6969
<ChevronLeft className="h-4 w-4" />
70-
<span>Previous</span>
70+
<span className="max-sm:hidden">Previous</span>
7171
</PaginationLink>
7272
);
7373
PaginationPrevious.displayName = "PaginationPrevious";
@@ -79,10 +79,10 @@ const PaginationNext = ({
7979
<PaginationLink
8080
aria-label="Go to next page"
8181
size="default"
82-
className={cn("gap-1 pr-2.5", className)}
82+
className={cn("h-10 gap-1 pr-2.5", className)}
8383
{...props}
8484
>
85-
<span>Next</span>
85+
<span className="max-sm:hidden">Next</span>
8686
<ChevronRight className="h-4 w-4" />
8787
</PaginationLink>
8888
);

apps/dashboard/src/@3rdweb-sdk/react/cache-keys.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export const engineKeys = {
7676
[...engineKeys.all, address, "instances"] as const,
7777
backendWallets: (instance: string) =>
7878
[...engineKeys.all, instance, "backendWallets"] as const,
79-
transactions: (instance: string) =>
80-
[...engineKeys.all, instance, "transactions"] as const,
79+
transactions: (instance: string, params: object) =>
80+
[...engineKeys.all, instance, "transactions", params] as const,
8181
permissions: (instance: string) =>
8282
[...engineKeys.all, instance, "permissions"] as const,
8383
accessTokens: (instance: string) =>

apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
"use client";
22

33
import { apiServerProxy } from "@/actions/proxies";
4-
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4+
import {
5+
keepPreviousData,
6+
useMutation,
7+
useQuery,
8+
useQueryClient,
9+
} from "@tanstack/react-query";
510
import type { ResultItem } from "app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/metrics/components/StatusCodes";
611
import type { EngineBackendWalletType } from "lib/engine";
712
import { useState } from "react";
813
import { useActiveAccount, useActiveWalletChain } from "thirdweb/react";
914
import invariant from "tiny-invariant";
15+
import type { EngineStatus } from "../../../app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table";
1016
import { engineKeys } from "../cache-keys";
1117

1218
export type EngineTier = "STARTER" | "PREMIUM" | "ENTERPRISE";
@@ -383,13 +389,29 @@ export function useEngineTransactions(params: {
383389
instanceUrl: string;
384390
autoUpdate: boolean;
385391
authToken: string;
392+
queryParams?: {
393+
limit?: number;
394+
page?: number;
395+
status?: EngineStatus;
396+
};
386397
}) {
387398
const { instanceUrl, autoUpdate, authToken } = params;
388399

389400
return useQuery({
390-
queryKey: engineKeys.transactions(instanceUrl),
401+
queryKey: engineKeys.transactions(instanceUrl, params),
391402
queryFn: async () => {
392-
const res = await fetch(`${instanceUrl}transaction/get-all`, {
403+
const url = new URL(`${instanceUrl}transaction/get-all`);
404+
if (params.queryParams) {
405+
for (const key in params.queryParams) {
406+
const value =
407+
params.queryParams[key as keyof typeof params.queryParams];
408+
if (value !== undefined) {
409+
url.searchParams.append(key, value.toString());
410+
}
411+
}
412+
}
413+
414+
const res = await fetch(url, {
393415
method: "GET",
394416
headers: getEngineRequestHeaders(authToken),
395417
});
@@ -398,8 +420,8 @@ export function useEngineTransactions(params: {
398420

399421
return (json.result as TransactionResponse) || {};
400422
},
401-
enabled: !!instanceUrl,
402423
refetchInterval: autoUpdate ? 4_000 : false,
424+
placeholderData: keepPreviousData,
403425
});
404426
}
405427

apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/analytics/components/EcosystemWalletUsersChartCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ export function EcosystemWalletUsersChartCard(props: {
228228
fill={chartConfig[authMethod]?.color}
229229
radius={4}
230230
stackId="a"
231-
strokeWidth={1.5}
232-
className="stroke-muted"
231+
strokeWidth={1}
232+
className="stroke-background"
233233
/>
234234
);
235235
})}

apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/backend-wallets-table.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export const BackendWalletsTable: React.FC<BackendWalletsTableProps> = ({
182182
isPending={isPending}
183183
isFetched={isFetched}
184184
tableScrollableClassName="max-h-[1000px]"
185+
tableContainerClassName="border-x-0 rounded-t-none border-b-0"
185186
onMenuClick={[
186187
{
187188
icon: <PencilIcon className="size-4" />,

0 commit comments

Comments
 (0)