Skip to content

Commit 3c1cdeb

Browse files
authored
Merge branch 'main' into greg/mny-94-fix-transaction-widget-forcing-payment-even-when-balance-is
2 parents ac18def + d812cc3 commit 3c1cdeb

File tree

15 files changed

+401
-25
lines changed

15 files changed

+401
-25
lines changed

apps/dashboard/src/@/analytics/report.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,17 @@ export function reportFundWalletSuccessful() {
503503
export function reportFundWalletFailed(params: { errorMessage: string }) {
504504
posthog.capture("fund wallet failed", params);
505505
}
506+
507+
/**
508+
* ### Why do we need to report this event?
509+
* - To track the conversion rate of the users choosing to create a token from new flow instead of the old flow
510+
*
511+
* ### Who is responsible for this event?
512+
* @MananTank
513+
*/
514+
export function reportTokenUpsellClicked(params: {
515+
assetType: "nft" | "coin";
516+
pageType: "explore" | "deploy-contract";
517+
}) {
518+
posthog.capture("token upsell clicked", params);
519+
}

apps/dashboard/src/@/components/blocks/dismissible-alert.tsx

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
"use client";
22

33
import { XIcon } from "lucide-react";
4+
import { useState } from "react";
45
import { Button } from "@/components/ui/button";
56
import { useLocalStorage } from "@/hooks/useLocalStorage";
67

7-
export function DismissibleAlert(props: {
8+
export function DismissibleAlert(
9+
props: {
10+
title: React.ReactNode;
11+
header?: React.ReactNode;
12+
className?: string;
13+
description: React.ReactNode;
14+
children?: React.ReactNode;
15+
} & (
16+
| {
17+
preserveState: true;
18+
localStorageId: string;
19+
}
20+
| {
21+
preserveState: false;
22+
}
23+
),
24+
) {
25+
if (props.preserveState) {
26+
return <DismissibleAlertWithLocalStorage {...props} />;
27+
}
28+
29+
return <DismissibleAlertWithoutLocalStorage {...props} />;
30+
}
31+
32+
function DismissibleAlertWithLocalStorage(props: {
833
title: React.ReactNode;
34+
header?: React.ReactNode;
935
description: React.ReactNode;
36+
children?: React.ReactNode;
1037
localStorageId: string;
1138
}) {
1239
const [isVisible, setIsVisible] = useLocalStorage(
@@ -17,19 +44,48 @@ export function DismissibleAlert(props: {
1744

1845
if (!isVisible) return null;
1946

47+
return <AlertUI {...props} onClose={() => setIsVisible(false)} />;
48+
}
49+
50+
function DismissibleAlertWithoutLocalStorage(props: {
51+
title: React.ReactNode;
52+
description: React.ReactNode;
53+
children?: React.ReactNode;
54+
}) {
55+
const [isVisible, setIsVisible] = useState(true);
56+
57+
if (!isVisible) return null;
58+
59+
return <AlertUI {...props} onClose={() => setIsVisible(false)} />;
60+
}
61+
62+
function AlertUI(props: {
63+
title: React.ReactNode;
64+
header?: React.ReactNode;
65+
description: React.ReactNode;
66+
children?: React.ReactNode;
67+
className?: string;
68+
onClose: () => void;
69+
}) {
2070
return (
21-
<div className="relative rounded-lg border border-border bg-card p-4">
22-
<Button
23-
aria-label="Close alert"
24-
className="absolute top-4 right-4 h-auto w-auto p-1 text-muted-foreground"
25-
onClick={() => setIsVisible(false)}
26-
variant="ghost"
27-
>
28-
<XIcon className="size-5" />
29-
</Button>
30-
<div>
31-
<h2 className="mb-0.5 font-semibold">{props.title} </h2>
32-
<div className="text-muted-foreground text-sm">{props.description}</div>
71+
<div className={props.className}>
72+
<div className="relative rounded-lg border border-border bg-card p-4 lg:p-6 overflow-hidden">
73+
<Button
74+
aria-label="Close alert"
75+
className="absolute top-4 right-4 h-auto w-auto p-1 text-muted-foreground"
76+
onClick={props.onClose}
77+
variant="ghost"
78+
>
79+
<XIcon className="size-5" />
80+
</Button>
81+
<div>
82+
{props.header}
83+
<h2 className="mb-0.5 font-semibold">{props.title} </h2>
84+
<div className="text-muted-foreground text-sm">
85+
{props.description}
86+
</div>
87+
{props.children}
88+
</div>
3389
</div>
3490
</div>
3591
);

apps/dashboard/src/app/(app)/(dashboard)/explore/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ContractRow } from "./components/contract-row";
44
import { DeployUpsellCard } from "./components/upsells/deploy-your-own";
55
import { PublishUpsellCard } from "./components/upsells/publish-submit";
66
import { EXPLORE_PAGE_DATA } from "./data";
7+
import { TokensSection } from "./tokens-section";
78

89
const title = "List of smart contracts for EVM Developers";
910
const description =
@@ -22,19 +23,23 @@ export default async function ExplorePage() {
2223
return (
2324
<div className="flex flex-col">
2425
<div className="border-b py-10">
25-
<div className="container">
26-
<h1 className="mb-2 font-bold text-3xl lg:text-5xl tracking-tighter">
26+
<div className="container max-w-7xl">
27+
<h1 className="mb-2 font-bold text-4xl lg:text-5xl tracking-tight">
2728
Explore
2829
</h1>
29-
<p className="max-w-screen-sm text-base text-muted-foreground">
30+
<p className="max-w-screen-sm text-sm lg:text-base text-muted-foreground">
3031
The best place for web3 developers to explore smart contracts from
3132
world-class web3 protocols & engineers all deployable with one
3233
click.
3334
</p>
3435
</div>
3536
</div>
3637

37-
<div className="container flex flex-col gap-4 py-10">
38+
<div className="container max-w-7xl pt-8 pb-4">
39+
<TokensSection />
40+
</div>
41+
42+
<div className="container max-w-7xl flex flex-col gap-8 py-10">
3843
<div className="flex flex-col gap-14">
3944
{EXPLORE_PAGE_DATA.map((category, idx) => (
4045
<Fragment key={category.id}>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"use client";
2+
3+
import { ZapIcon } from "lucide-react";
4+
import Link from "next/link";
5+
import { reportTokenUpsellClicked } from "@/analytics/report";
6+
import { GridPattern } from "@/components/ui/background-patterns";
7+
import { Badge } from "@/components/ui/badge";
8+
import { cn } from "@/lib/utils";
9+
10+
export function TokensSection() {
11+
return (
12+
<div className="bg-card p-4 lg:py-8 lg:px-6 border rounded-xl relative w-full overflow-hidden">
13+
<GridPattern
14+
width={30}
15+
height={30}
16+
strokeDasharray={"4 2"}
17+
className="text-border dark:text-border/70 hidden lg:block translate-x-5"
18+
style={{
19+
maskImage:
20+
"linear-gradient(to bottom left,white,transparent,transparent)",
21+
}}
22+
/>
23+
24+
<Badge
25+
variant="outline"
26+
className="text-sm bg-background h-auto px-3 py-1 gap-2 mb-4"
27+
>
28+
<div className="rounded-full bg-primary size-2" />
29+
New
30+
</Badge>
31+
32+
<h2 className="font-semibold text-2xl tracking-tight mb-1">
33+
Launch Your Tokens Effortlessly
34+
</h2>
35+
<p className="text-muted-foreground mb-6 text-sm lg:text-base">
36+
Deploy contract and configure all settings you need to launch your token
37+
in one seamless flow
38+
</p>
39+
40+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 max-w-4xl">
41+
<CardLink
42+
assetType="coin"
43+
title="Launch Coin"
44+
description="Launch an ERC-20 token to create a cryptocurrency"
45+
href="/team/~/~project/tokens/create/token"
46+
bullets={[
47+
"Deploy Contract",
48+
"Configure Price and Supply",
49+
"Airdrop Tokens",
50+
]}
51+
/>
52+
53+
<CardLink
54+
assetType="nft"
55+
title="Launch NFT Collection"
56+
description="Launch an ERC-721 or ERC-1155 NFT collection"
57+
href="/team/~/~project/tokens/create/nft"
58+
bullets={[
59+
"Deploy Contract",
60+
"Upload NFTs",
61+
"Configure Price and Supply",
62+
]}
63+
/>
64+
</div>
65+
</div>
66+
);
67+
}
68+
69+
function CardLink(props: {
70+
title: string;
71+
description: string;
72+
href: string;
73+
assetType: "nft" | "coin";
74+
bullets: string[];
75+
}) {
76+
return (
77+
<div
78+
className={cn(
79+
"relative flex flex-col rounded-lg border p-4 cursor-pointer hover:border-active-border bg-background",
80+
)}
81+
>
82+
<div className="mb-3 flex">
83+
<div className="flex items-center justify-center rounded-full border p-2 bg-card">
84+
<ZapIcon className="size-4 text-muted-foreground" />
85+
</div>
86+
</div>
87+
88+
<h3 className="mb-0.5 font-semibold text-lg tracking-tight">
89+
<Link
90+
className="before:absolute before:inset-0"
91+
href={props.href}
92+
onClick={() => {
93+
reportTokenUpsellClicked({
94+
assetType: props.assetType,
95+
pageType: "explore",
96+
});
97+
}}
98+
>
99+
{props.title}
100+
</Link>
101+
</h3>
102+
<p className="text-muted-foreground text-sm">{props.description}</p>
103+
104+
<ul className="mt-4 space-y-1 text-sm list-disc list-inside text-muted-foreground">
105+
{props.bullets.map((bullet) => (
106+
<li key={bullet}>{bullet}</li>
107+
))}
108+
</ul>
109+
</div>
110+
);
111+
}

apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/publish-based-deploy.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ZERO_FEE_VERSIONS } from "@/constants/fee-config";
1010
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
1111
import { PublishedContractBreadcrumbs } from "../[publisher]/[contract_id]/components/breadcrumbs.client";
1212
import { DeployContractHeader } from "./contract-header";
13+
import { TokenBanner } from "./token-banner";
1314
import { DeployFormForUri } from "./uri-based-deploy";
1415

1516
type PublishBasedDeployProps = {
@@ -95,6 +96,10 @@ export async function DeployFormForPublishInfo(props: PublishBasedDeployProps) {
9596
),
9697
]);
9798

99+
const isTWPublisher =
100+
contractMetadata?.publisher ===
101+
"0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024";
102+
98103
return (
99104
<div>
100105
<div className="border-border border-b border-dashed">
@@ -111,6 +116,23 @@ export async function DeployFormForPublishInfo(props: PublishBasedDeployProps) {
111116
/>
112117
</div>
113118

119+
{isTWPublisher &&
120+
(contractMetadata.name === "DropERC20" ||
121+
contractMetadata.name === "TokenERC20") && (
122+
<TokenBanner type="erc20" />
123+
)}
124+
{isTWPublisher &&
125+
(contractMetadata.name === "DropERC721" ||
126+
contractMetadata.name === "TokenERC721") && (
127+
<TokenBanner type="erc721" />
128+
)}
129+
130+
{isTWPublisher &&
131+
(contractMetadata.name === "DropERC1155" ||
132+
contractMetadata.name === "TokenERC1155") && (
133+
<TokenBanner type="erc1155" />
134+
)}
135+
114136
<div className="container max-w-5xl py-8">
115137
<DeployFormForUri
116138
contractMetadata={contractMetadata}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"use client";
2+
3+
import { ArrowUpRightIcon } from "lucide-react";
4+
import Link from "next/link";
5+
import { reportTokenUpsellClicked } from "@/analytics/report";
6+
import { DismissibleAlert } from "@/components/blocks/dismissible-alert";
7+
import { Badge } from "@/components/ui/badge";
8+
import { Button } from "@/components/ui/button";
9+
10+
export function TokenBanner(props: { type: "erc20" | "erc721" | "erc1155" }) {
11+
const title =
12+
props.type === "erc20"
13+
? "Launch Your Token Effortlessly"
14+
: "Launch Your NFT Collection Effortlessly";
15+
const description =
16+
props.type === "erc20"
17+
? "Deploy contract, set price and supply, airdrop tokens all in one seamless flow"
18+
: props.type === "erc721"
19+
? "Deploy contract, upload NFTs, set price all in one seamless flow"
20+
: "Deploy contract, upload NFTs, set prices and supply all in one seamless flow";
21+
const href =
22+
props.type === "erc20"
23+
? "/team/~/~project/tokens/create/token"
24+
: "/team/~/~project/tokens/create/nft";
25+
26+
return (
27+
<DismissibleAlert
28+
header={
29+
<Badge
30+
variant="outline"
31+
className="bg-background gap-1.5 py-1 px-2.5 h-auto mb-3"
32+
>
33+
<div className="rounded-full bg-primary size-1.5" />
34+
New
35+
</Badge>
36+
}
37+
title={title}
38+
className="container max-w-5xl mt-8"
39+
preserveState={false}
40+
description={description}
41+
>
42+
<Button
43+
variant="default"
44+
size="sm"
45+
className="gap-2 rounded-full mt-4 px-6"
46+
asChild
47+
>
48+
<Link
49+
href={href}
50+
target="_blank"
51+
onClick={() => {
52+
reportTokenUpsellClicked({
53+
assetType: props.type === "erc20" ? "coin" : "nft",
54+
pageType: "deploy-contract",
55+
});
56+
}}
57+
>
58+
Try Now <ArrowUpRightIcon className="size-3.5" />
59+
</Link>
60+
</Button>
61+
</DismissibleAlert>
62+
);
63+
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export default async function Page(props: {
6767
<FreePlanUpsellBannerUI highlightPlan="growth" teamSlug={team.slug} />
6868
) : (
6969
<DismissibleAlert
70+
preserveState={true}
7071
description="Engines, contracts, project settings, and more are now managed within projects. Open or create a project to access them."
7172
localStorageId={`${team.id}-engines-alert`}
7273
title="Looking for Engines?"

apps/dashboard/src/app/(app)/team/~/[[...paths]]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AppFooter } from "@/components/footers/app-footer";
55
import { DotsBackgroundPattern } from "@/components/ui/background-patterns";
66
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
77
import { TeamHeader } from "../../components/TeamHeader/team-header";
8-
import { createTeamLink, TeamSelectorCard } from "./components/team-selector";
8+
import { createTeamLink, TeamSelectorCard } from "../_components/team-selector";
99

1010
export default async function Page(props: {
1111
params: Promise<{

0 commit comments

Comments
 (0)