Skip to content

Commit fbe1aa2

Browse files
committed
Dashboard: Various UI tweaks in project pages (#7905)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on refining the UI components and improving the layout across various pages in the dashboard application. It includes adjustments to button styles, layout properties, and content formatting for better user experience. ### Detailed summary - Updated button styles for consistency (size and variant). - Adjusted layout properties for various components (e.g., `max-w-7xl`, padding). - Enhanced descriptions with line breaks for clarity. - Modified component structures for better alignment and spacing. - Removed unnecessary links from `webhooks` and `contracts` sections. - Improved the `ProjectPageHeader` and `ProjectPageFooter` components. - Revised `LinkGroup` to utilize dynamic icon rendering. - Enhanced `EmptyStateCard` and `TotalSponsoredCard` layouts. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Headers now accept rich content (React nodes) enabling responsive line breaks. - Style - Standardized small "secondary" bordered pill buttons across many actions and dialogs. - Updated header typography, spacing, avatar sizing, and max-width constraints for a more prominent layout. - Icons adjusted for size/color; token cards now stack vertically; dialog widths constrained; footer container width tightened. - Bug Fixes / Refactor - Replaced visual separators with CSS borders and simplified DOM structure. - Removed certain header doc links from Webhooks and Payments pages. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent bdcbe0e commit fbe1aa2

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)