Skip to content

Commit 1ffe360

Browse files
committed
Dashboard: Various UI tweaks in project pages
1 parent bdcbe0e commit 1ffe360

File tree

25 files changed

+206
-151
lines changed

25 files changed

+206
-151
lines changed

apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ const linkTypeToOrder: Record<LinkType, number> = {
3838
settings: 5,
3939
};
4040

41-
const linkTypeToIcon: Record<LinkType, React.ReactNode> = {
42-
api: <NetworkIcon className="size-4" />,
43-
docs: <BookTextIcon className="size-4" />,
44-
playground: <BoxIcon className="size-4" />,
45-
webhooks: <WebhookIcon className="size-4" />,
46-
settings: <SettingsIcon className="size-4" />,
41+
const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = {
42+
api: NetworkIcon,
43+
docs: BookTextIcon,
44+
playground: BoxIcon,
45+
webhooks: WebhookIcon,
46+
settings: SettingsIcon,
4747
};
4848

4949
function orderLinks(links: ActionLink[]) {
@@ -70,21 +70,22 @@ export function LinkGroup(props: { links: ActionLink[] }) {
7070
<div className="flex flex-row items-center gap-2">
7171
{orderedLinks.map((link) => {
7272
const isExternal = link.href.startsWith("http");
73+
const Icon = linkTypeToIcon[link.type];
7374
return (
7475
<ToolTipLabel key={link.type} label={linkTypeToLabel[link.type]}>
7576
<Button
7677
asChild
7778
size="icon"
78-
variant="outline"
79-
className="rounded-full"
79+
variant="secondary"
80+
className="rounded-full border"
8081
>
8182
<Link
8283
href={link.href}
8384
target={isExternal ? "_blank" : undefined}
8485
rel={isExternal ? "noopener noreferrer" : undefined}
8586
className="flex flex-row items-center gap-2"
8687
>
87-
{linkTypeToIcon[link.type]}
88+
<Icon className="size-4 text-foreground" />
8889
</Link>
8990
</Button>
9091
</ToolTipLabel>
@@ -98,25 +99,30 @@ export function LinkGroup(props: { links: ActionLink[] }) {
9899
return (
99100
<DropdownMenu>
100101
<DropdownMenuTrigger asChild>
101-
<Button size="icon" variant="outline" className="rounded-full">
102-
<EllipsisVerticalIcon className="size-4" />
102+
<Button size="icon" variant="secondary" className="rounded-full border">
103+
<EllipsisVerticalIcon className="size-4 text-foreground" />
103104
</Button>
104105
</DropdownMenuTrigger>
105-
<DropdownMenuContent align="end" className="gap-1 flex flex-col md:w-48">
106+
<DropdownMenuContent
107+
align="center"
108+
className="gap-1 flex flex-col md:w-48 rounded-lg"
109+
sideOffset={10}
110+
>
106111
{orderedLinks.map((link) => {
107112
const isExternal = link.href.startsWith("http");
113+
const Icon = linkTypeToIcon[link.type];
108114
return (
109115
<DropdownMenuItem
110116
key={link.type}
111117
asChild
112-
className="flex flex-row items-center gap-2 cursor-pointer py-2 text-muted-foreground hover:text-foreground"
118+
className="flex flex-row items-center gap-2 cursor-pointer py-2"
113119
>
114120
<Link
115121
href={link.href}
116122
target={isExternal ? "_blank" : undefined}
117123
rel={isExternal ? "noopener noreferrer" : undefined}
118124
>
119-
{linkTypeToIcon[link.type]}
125+
<Icon className="size-4 text-muted-foreground" />
120126
{linkTypeToLabel[link.type]}
121127
</Link>
122128
</DropdownMenuItem>

apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export type ProjectPageFooterProps = FooterCardProps;
77

88
export function ProjectPageFooter(props: ProjectPageFooterProps) {
99
return (
10-
<footer className="container">
10+
<footer className="container max-w-7xl">
1111
<FooterLinksSection {...props} />
1212
</footer>
1313
);

apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Button } from "@workspace/ui/components/button";
22
import { ArrowUpRightIcon } from "lucide-react";
33
import Link from "next/link";
44
import type { ThirdwebClient } from "thirdweb";
5+
import { cn } from "@/lib/utils";
56
import { ProjectAvatar } from "../avatar/project-avatar";
67
import { type ActionLink, LinkGroup } from "./header/link-group";
78

@@ -25,15 +26,24 @@ type Action =
2526

2627
function Action(props: { action: Action; variant?: "default" | "secondary" }) {
2728
const action = props.action;
29+
2830
return "component" in action ? (
2931
action.component
3032
) : (
31-
<Button asChild className="rounded-full" variant={props.variant}>
33+
<Button
34+
asChild
35+
className={cn(
36+
"rounded-full",
37+
props.variant === "secondary" && "border border-border",
38+
)}
39+
size="sm"
40+
variant={props.variant}
41+
>
3242
<Link
3343
href={action.href}
3444
target={action.external ? "_blank" : undefined}
3545
rel={action.external ? "noopener noreferrer" : undefined}
36-
className="flex flex-row items-center gap-1.5"
46+
className="flex flex-row items-center gap-2"
3747
>
3848
{action.icon}
3949
{action.label}
@@ -46,7 +56,7 @@ function Action(props: { action: Action; variant?: "default" | "secondary" }) {
4656
export type ProjectPageHeaderProps = {
4757
client: ThirdwebClient;
4858
title: string;
49-
description?: string;
59+
description?: React.ReactNode;
5060
imageUrl?: string | null;
5161
actions: {
5262
primary: Action;
@@ -61,22 +71,24 @@ export type ProjectPageHeaderProps = {
6171

6272
export function ProjectPageHeader(props: ProjectPageHeaderProps) {
6373
return (
64-
<header className="flex flex-col gap-4 container py-5">
74+
<header className="flex flex-col gap-4 container max-w-7xl py-6">
6575
{/* main row */}
6676
<div className="flex flex-row items-center justify-between">
6777
{/* left */}
68-
<div className="flex flex-col gap-2">
78+
<div className="flex flex-col gap-4">
6979
{/* image */}
7080
{props.imageUrl !== undefined && (
7181
<ProjectAvatar
72-
className="size-14"
82+
className="size-12"
7383
client={props.client}
7484
src={props.imageUrl ?? undefined}
7585
/>
7686
)}
7787
{/* title */}
78-
<div className="flex flex-col gap-1 max-w-xl">
79-
<h2 className="text-2xl font-medium line-clamp-1">{props.title}</h2>
88+
<div className="flex flex-col gap-1 max-w-3xl">
89+
<h2 className="text-3xl font-semibold tracking-tight line-clamp-1">
90+
{props.title}
91+
</h2>
8092
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
8193
{props.description}
8294
</p>
@@ -91,7 +103,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
91103
{props.actions && (
92104
<div className="flex flex-row items-center justify-between">
93105
{/* left actions */}
94-
<div className="flex flex-row items-center gap-4">
106+
<div className="flex flex-row items-center gap-3">
95107
{props.actions.primary && <Action action={props.actions.primary} />}
96108
{props.actions.secondary && (
97109
<Action action={props.actions.secondary} variant="secondary" />
Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Separator } from "../../ui/separator";
1+
import { cn } from "@/lib/utils";
22
import { type TabPathLink, TabPathLinks } from "../../ui/tabs";
33
import {
44
ProjectPageFooter,
@@ -21,24 +21,23 @@ type ProjectPageProps = {
2121

2222
export function ProjectPage(props: React.PropsWithChildren<ProjectPageProps>) {
2323
return (
24-
<section className="flex flex-col gap-4 pb-20">
25-
<ProjectPageHeader {...props.header} />
26-
{props.tabs ? (
27-
<TabPathLinks
28-
className="-mt-4"
29-
tabContainerClassName="container"
30-
links={props.tabs}
31-
/>
32-
) : (
33-
<Separator />
34-
)}
35-
<main className="container py-6">{props.children}</main>
24+
<div className={cn("flex flex-col pb-20", props.footer && "pb-0")}>
25+
<div className={cn(!props.tabs && "border-b")}>
26+
<ProjectPageHeader {...props.header} />
27+
{props.tabs && (
28+
<TabPathLinks
29+
scrollableClassName="container max-w-7xl"
30+
links={props.tabs}
31+
/>
32+
)}
33+
</div>
34+
35+
<main className="container max-w-7xl pt-6">{props.children}</main>
3636
{props.footer && (
37-
<>
38-
<Separator />
37+
<div className="border-t mt-20">
3938
<ProjectPageFooter {...props.footer} />
40-
</>
39+
</div>
4140
)}
42-
</section>
41+
</div>
4342
);
4443
}

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import Link from "next/link";
55
import { useState } from "react";
66
import type { ThirdwebClient } from "thirdweb";
77
import { ImportModal } from "@/components/contracts/import-contract/modal";
8+
import { GridPattern } from "@/components/ui/background-patterns";
89
import { Button } from "@/components/ui/button";
10+
import { ContractIcon } from "@/icons/ContractIcon";
911

1012
export function DeployViaCLIOrImportCard(props: {
1113
teamId: string;
@@ -17,7 +19,12 @@ export function DeployViaCLIOrImportCard(props: {
1719
const [importModalOpen, setImportModalOpen] = useState(false);
1820

1921
return (
20-
<div className="rounded-lg border bg-card p-4 lg:p-6">
22+
<div className="rounded-2xl border bg-card p-4 lg:p-6 relative overflow-hidden">
23+
<div className="flex mb-3">
24+
<div className="p-2 rounded-full border bg-card">
25+
<ContractIcon className="size-5 text-muted-foreground" />
26+
</div>
27+
</div>
2128
<ImportModal
2229
client={props.client}
2330
isOpen={importModalOpen}
@@ -31,20 +38,32 @@ export function DeployViaCLIOrImportCard(props: {
3138
type="contract"
3239
/>
3340

34-
<h2 className="mb-0.5 font-semibold text-lg">
41+
<GridPattern
42+
width={30}
43+
height={30}
44+
strokeDasharray={"4 2"}
45+
className="text-border dark:text-border/70 hidden lg:block translate-x-5"
46+
style={{
47+
maskImage:
48+
"linear-gradient(to left bottom,white,transparent,transparent)",
49+
}}
50+
/>
51+
52+
<h2 className="mb-1 font-semibold tracking-tight text-lg">
3553
Already have a smart contract?
3654
</h2>
37-
<p className="max-w-2xl text-muted-foreground text-sm lg:text-base">
55+
<p className="text-muted-foreground text-sm">
3856
Import an already deployed contract or deploy a contract from source
39-
code to easily manage permissions, upload assets, and interact with
40-
contract functions
57+
code to easily manage <br className="max-sm:hidden" /> permissions,
58+
upload assets, and interact with contract functions
4159
</p>
4260

43-
<div className="mt-6 flex flex-col gap-3 lg:flex-row">
61+
<div className="mt-5 flex gap-3">
4462
<Button
4563
asChild
46-
className="gap-2 bg-background lg:px-10"
64+
className="gap-2 bg-background rounded-full"
4765
variant="outline"
66+
size="sm"
4867
>
4968
<Link
5069
href="https://portal.thirdweb.com/contracts/deploy/overview"
@@ -56,13 +75,14 @@ export function DeployViaCLIOrImportCard(props: {
5675
</Link>
5776
</Button>
5877
<Button
59-
className="gap-2 bg-background"
78+
className="gap-2 bg-background rounded-full"
6079
onClick={() => {
6180
setImportModalOpen(true);
6281
}}
6382
variant="outline"
83+
size="sm"
6484
>
65-
<DownloadIcon className="size-4" />
85+
<DownloadIcon className="size-4 text-muted-foreground" />
6686
Import Contract
6787
</Button>
6888
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ export function ProjectSidebarLayout(props: {
125125
},
126126
]}
127127
footerSidebarLinks={[
128+
{
129+
separator: true,
130+
},
128131
{
129132
href: `${props.layoutPath}/webhooks/contracts`,
130133
icon: WebhookIcon,

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ export function ImportContractButton(props: {
3131
/>
3232

3333
<Button
34-
className="gap-1.5 rounded-full"
34+
className="gap-2 rounded-full border"
35+
size="sm"
3536
onClick={() => {
3637
setImportModalOpen(true);
3738
}}
38-
variant="outline"
39+
variant="secondary"
3940
>
4041
<ImportIcon className="size-4" />
4142
Import contract

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@ export default async function Page(props: {
4242
header={{
4343
client,
4444
title: "Contracts",
45-
description:
46-
"Read, write, and deploy smart contracts on any EVM compatible blockchain. Deploy contracts from templates, or build your own from scratch.",
45+
description: (
46+
<>
47+
Read, write, and deploy smart contracts on any EVM compatible
48+
blockchain. <br className="max-sm:hidden" /> Deploy contracts from
49+
templates, or build your own from scratch
50+
</>
51+
),
4752
actions: {
4853
primary: {
4954
label: "Deploy Contract",
@@ -79,10 +84,6 @@ export default async function Page(props: {
7984
type: "webhooks",
8085
href: `/team/${params.team_slug}/${params.project_slug}/webhooks/contracts`,
8186
},
82-
{
83-
type: "settings",
84-
href: `/team/${params.team_slug}/${params.project_slug}/settings/contracts`,
85-
},
8687
],
8788
}}
8889
footer={{

0 commit comments

Comments
 (0)