Skip to content

Commit 1dc928b

Browse files
gregfromstlMananTank
authored andcommitted
[Dashboard] Fix: Trend markers and UB volume type dropdown (#7059)
1 parent f31116e commit 1dc928b

File tree

33 files changed

+2663
-73
lines changed

33 files changed

+2663
-73
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { cn } from "@/lib/utils";
2+
3+
export type Segment = {
4+
label: string;
5+
percent: number;
6+
color: string;
7+
};
8+
9+
type DistributionBarChartProps = {
10+
segments: Segment[];
11+
title: string;
12+
};
13+
export function DistributionBarChart(props: DistributionBarChartProps) {
14+
const totalPercentage = props.segments.reduce(
15+
(sum, segment) => sum + segment.percent,
16+
0,
17+
);
18+
19+
const invalidTotalPercentage = totalPercentage !== 100;
20+
21+
return (
22+
<div>
23+
<div className="mb-2 flex items-center justify-between">
24+
<h3 className="font-medium text-sm">{props.title}</h3>
25+
<div
26+
className={cn(
27+
"font-medium text-muted-foreground text-sm",
28+
invalidTotalPercentage && "text-red-500",
29+
)}
30+
>
31+
Total: {totalPercentage}%
32+
</div>
33+
</div>
34+
35+
{/* Bar */}
36+
<div className="flex h-3 overflow-hidden rounded-lg">
37+
{props.segments.map((segment) => {
38+
return (
39+
<div
40+
key={segment.label}
41+
className="flex h-full items-center justify-center transition-all duration-200"
42+
style={{
43+
width: `${segment.percent}%`,
44+
backgroundColor: segment.color,
45+
}}
46+
/>
47+
);
48+
})}
49+
</div>
50+
51+
{/* Legends */}
52+
<div className="mt-3 flex flex-col gap-1 lg:flex-row lg:gap-6">
53+
{props.segments.map((segment) => {
54+
return (
55+
<div key={segment.label} className="flex items-center gap-1.5">
56+
<div
57+
className="size-3 rounded-full"
58+
style={{
59+
backgroundColor: segment.color,
60+
}}
61+
/>
62+
<p className="text-sm">
63+
{segment.label}: {segment.percent}%
64+
</p>
65+
</div>
66+
);
67+
})}
68+
</div>
69+
</div>
70+
);
71+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { MultiStepStatus } from "./multi-step-status";
3+
4+
const meta = {
5+
title: "Blocks/MultiStepStatus",
6+
component: MultiStepStatus,
7+
decorators: [
8+
(Story) => (
9+
<div className="container w-full max-w-md py-10">
10+
<Story />
11+
</div>
12+
),
13+
],
14+
} satisfies Meta<typeof MultiStepStatus>;
15+
16+
export default meta;
17+
type Story = StoryObj<typeof meta>;
18+
19+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
20+
21+
export const AllStates: Story = {
22+
args: {
23+
steps: [
24+
{
25+
status: "completed",
26+
label: "Connect Wallet",
27+
retryLabel: "Failed to connect wallet",
28+
execute: async () => {
29+
await sleep(1000);
30+
},
31+
},
32+
{
33+
status: "pending",
34+
label: "Sign Message",
35+
retryLabel: "Failed to sign message",
36+
execute: async () => {
37+
await sleep(1000);
38+
},
39+
},
40+
{
41+
status: "error",
42+
label: "Approve Transaction",
43+
retryLabel: "Transaction approval failed",
44+
execute: async () => {
45+
await sleep(1000);
46+
},
47+
},
48+
{
49+
status: "idle",
50+
label: "Confirm Transaction",
51+
retryLabel: "Transaction confirmation failed",
52+
execute: async () => {
53+
await sleep(1000);
54+
},
55+
},
56+
{
57+
status: "idle",
58+
label: "Finalize",
59+
retryLabel: "Finalization failed",
60+
execute: async () => {
61+
await sleep(1000);
62+
},
63+
},
64+
],
65+
},
66+
};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
AlertCircleIcon,
6+
CircleCheckIcon,
7+
CircleIcon,
8+
RefreshCwIcon,
9+
} from "lucide-react";
10+
import { DynamicHeight } from "../../ui/DynamicHeight";
11+
import { Spinner } from "../../ui/Spinner/Spinner";
12+
13+
export type MultiStepState = {
14+
status: "idle" | "pending" | "completed" | "error";
15+
retryLabel: string;
16+
label: string;
17+
execute: () => Promise<void>;
18+
};
19+
20+
export function MultiStepStatus(props: {
21+
steps: MultiStepState[];
22+
}) {
23+
return (
24+
<DynamicHeight>
25+
<div className="space-y-4">
26+
{props.steps.map((step) => (
27+
<div key={step.label} className="flex items-start space-x-3 ">
28+
{step.status === "completed" ? (
29+
<CircleCheckIcon className="mt-0.5 size-5 flex-shrink-0 text-green-500" />
30+
) : step.status === "pending" ? (
31+
<Spinner className="mt-0.5 size-5 flex-shrink-0 text-foreground" />
32+
) : step.status === "error" ? (
33+
<AlertCircleIcon className="mt-0.5 size-5 flex-shrink-0 text-red-500" />
34+
) : (
35+
<CircleIcon className="mt-0.5 size-5 flex-shrink-0 text-muted-foreground/70" />
36+
)}
37+
<div className="flex-1">
38+
<p
39+
className={`font-medium ${
40+
step.status === "pending"
41+
? "text-foreground"
42+
: step.status === "completed"
43+
? "text-green-500"
44+
: step.status === "error"
45+
? "text-red-500"
46+
: "text-muted-foreground/70"
47+
}`}
48+
>
49+
{step.label}
50+
</p>
51+
52+
{step.status === "error" && (
53+
<div className="mt-1 space-y-2">
54+
<p className="mb-1 text-red-500 text-sm">{step.retryLabel}</p>
55+
<Button
56+
variant="destructive"
57+
size="sm"
58+
className="gap-2"
59+
onClick={() => step.execute()}
60+
>
61+
<RefreshCwIcon className="size-4" />
62+
Retry
63+
</Button>
64+
</div>
65+
)}
66+
</div>
67+
</div>
68+
))}
69+
</div>
70+
</DynamicHeight>
71+
);
72+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ const getClaimConditionTypeFromPhase = (
126126
if (!phase.snapshot) {
127127
return "public";
128128
}
129+
129130
if (phase.snapshot) {
130131
if (
132+
phase.price === "0" &&
131133
typeof phase.snapshot !== "string" &&
132134
phase.snapshot.length === 1 &&
133135
phase.snapshot.some(

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ function AddToProjectModalContent(props: {
147147
teamId: params.teamId,
148148
projectId: params.projectId,
149149
chainId: props.chainId,
150+
deploymentType: undefined,
151+
contractType: undefined,
150152
},
151153
{
152154
onSuccess: () => {

apps/dashboard/src/app/(app)/account/contracts/DeployedContractsPageHeader.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function DeployedContractsPageHeader(props: {
2626
}}
2727
teamId={props.teamId}
2828
projectId={props.projectId}
29+
type="contract"
2930
/>
3031

3132
<div className="container flex max-w-7xl flex-col gap-3 py-10 lg:flex-row lg:items-center lg:justify-between">

apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function DeployViaCLIOrImportCard(props: {
1919
return (
2020
<div className="rounded-lg border bg-card p-4 lg:p-6">
2121
<ImportModal
22+
type="contract"
2223
client={client}
2324
isOpen={importModalOpen}
2425
onClose={() => {

apps/dashboard/src/app/(app)/account/contracts/_components/DeployedContractsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ async function DeployedContractsPageAsync(props: {
4444
teamId: props.teamId,
4545
projectId: props.projectId,
4646
authToken: props.authToken,
47+
deploymentType: undefined,
4748
});
4849

4950
return (
5051
<ClientOnly ssr={<Loading />}>
5152
<ContractTable
53+
variant="contract"
5254
contracts={deployedContracts}
5355
pageSize={10}
5456
teamId={props.teamId}

apps/dashboard/src/app/(app)/account/contracts/_components/getProjectContracts.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,28 @@ export type ProjectContract = {
77
chainId: string;
88
createdAt: string;
99
updatedAt: string;
10+
deploymentType: string | null;
11+
contractType: string | null;
1012
};
1113

1214
export async function getProjectContracts(options: {
1315
teamId: string;
1416
projectId: string;
1517
authToken: string;
18+
deploymentType: string | undefined;
1619
}) {
17-
const res = await fetch(
20+
const url = new URL(
1821
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${options.teamId}/projects/${options.projectId}/contracts`,
19-
{
20-
headers: {
21-
Authorization: `Bearer ${options.authToken}`,
22-
},
23-
},
2422
);
23+
if (options.deploymentType) {
24+
url.searchParams.set("deploymentType", options.deploymentType);
25+
}
26+
27+
const res = await fetch(url, {
28+
headers: {
29+
Authorization: `Bearer ${options.authToken}`,
30+
},
31+
});
2532

2633
if (!res.ok) {
2734
const errorMessage = await res.text();

apps/dashboard/src/app/(app)/account/contracts/_components/getSortedDeployedContracts.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ export async function getSortedDeployedContracts(params: {
99
teamId: string;
1010
projectId: string;
1111
authToken: string;
12+
deploymentType: string | undefined;
1213
}) {
1314
const contracts = await getProjectContracts({
1415
teamId: params.teamId,
1516
projectId: params.projectId,
1617
authToken: params.authToken,
18+
deploymentType: params.deploymentType,
1719
});
1820

1921
const chainIds = Array.from(new Set(contracts.map((c) => c.chainId)));

0 commit comments

Comments
 (0)