Skip to content

Commit e651d0a

Browse files
committed
Dashboard: Update Project header style (#7920)
<!-- ## 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 primarily focuses on updating UI components across various files in the application. It includes adjustments to icon sizes, component styles, and the introduction of new icons, enhancing the overall user interface and experience. ### Detailed summary - Changed `PlusIcon` size from `size-4` to `size-3.5` in multiple buttons. - Replaced `SettingsIcon` with `Settings2Icon`. - Added `DatabaseIcon`, `WebhookIcon`, `TokenIcon`, `ArrowDownToLineIcon`, and others to various headers. - Updated button styles to `bg-card` and changed variants to `outline`. - Modified header layouts to include new icons and adjusted spacing. - Removed actions from several `ProjectPage` components and replaced them with links. - Adjusted descriptions and added new links in the `ContractsPage`, `PaymentsPage`, and other components. > ✨ 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 - Header icons added across many pages; Settings now appears as a dedicated header control where provided. - Single link renders as a direct action button; multiple links grouped under a labeled "Resources" menu with chevron. - Refactor - Project header redesigned to an icon-driven layout; mobile-specific branching removed and actions reorganized. - Style - Button variants, spacing, iconography, and dropdown alignment adjusted (smaller icons, new arrow/chevron visuals, reduced item padding). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 34239ea commit e651d0a

File tree

24 files changed

+215
-217
lines changed

24 files changed

+215
-217
lines changed

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

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import { Button } from "@workspace/ui/components/button";
44
import {
55
BookTextIcon,
66
BoxIcon,
7-
EllipsisVerticalIcon,
8-
NetworkIcon,
9-
SettingsIcon,
7+
ChevronDownIcon,
8+
CodeIcon,
109
WebhookIcon,
1110
} from "lucide-react";
1211
import Link from "next/link";
@@ -17,33 +16,28 @@ import {
1716
DropdownMenuItem,
1817
DropdownMenuTrigger,
1918
} from "@/components/ui/dropdown-menu";
20-
import { ToolTipLabel } from "@/components/ui/tooltip";
21-
import { useIsMobile } from "@/hooks/use-mobile";
2219

23-
type LinkType = "api" | "docs" | "playground" | "webhooks" | "settings";
20+
type LinkType = "api" | "docs" | "playground" | "webhooks";
2421

2522
const linkTypeToLabel: Record<LinkType, string> = {
2623
api: "API Reference",
2724
docs: "Documentation",
2825
playground: "Playground",
2926
webhooks: "Webhooks",
30-
settings: "Settings",
3127
};
3228

3329
const linkTypeToOrder: Record<LinkType, number> = {
3430
docs: 0,
3531
playground: 1,
3632
api: 3,
3733
webhooks: 4,
38-
settings: 5,
3934
};
4035

4136
const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = {
42-
api: NetworkIcon,
37+
api: CodeIcon,
4338
docs: BookTextIcon,
4439
playground: BoxIcon,
4540
webhooks: WebhookIcon,
46-
settings: SettingsIcon,
4741
};
4842

4943
function orderLinks(links: ActionLink[]) {
@@ -60,52 +54,43 @@ export type ActionLink = {
6054
};
6155

6256
export function LinkGroup(props: { links: ActionLink[] }) {
63-
const isMobile = useIsMobile();
64-
const maxLinks = isMobile ? 1 : 2;
6557
const orderedLinks = useMemo(() => orderLinks(props.links), [props.links]);
6658

67-
// case where we just render directly
68-
if (props.links.length <= maxLinks) {
59+
if (orderedLinks.length === 1 && orderedLinks[0]) {
60+
const link = orderedLinks[0];
61+
const Icon = linkTypeToIcon[link.type];
6962
return (
70-
<div className="flex flex-row items-center gap-2">
71-
{orderedLinks.map((link) => {
72-
const isExternal = link.href.startsWith("http");
73-
const Icon = linkTypeToIcon[link.type];
74-
return (
75-
<ToolTipLabel key={link.type} label={linkTypeToLabel[link.type]}>
76-
<Button
77-
asChild
78-
size="icon"
79-
variant="secondary"
80-
className="rounded-full border"
81-
>
82-
<Link
83-
href={link.href}
84-
target={isExternal ? "_blank" : undefined}
85-
rel={isExternal ? "noopener noreferrer" : undefined}
86-
className="flex flex-row items-center gap-2"
87-
>
88-
<Icon className="size-4 text-foreground" />
89-
</Link>
90-
</Button>
91-
</ToolTipLabel>
92-
);
93-
})}
94-
</div>
63+
<Link
64+
href={link.href}
65+
target={link.href.startsWith("http") ? "_blank" : undefined}
66+
>
67+
<Button
68+
variant="outline"
69+
size="sm"
70+
className="rounded-full border gap-2 bg-card"
71+
>
72+
<Icon className="size-3.5 text-muted-foreground" />
73+
{linkTypeToLabel[link.type]}
74+
</Button>
75+
</Link>
9576
);
9677
}
9778

98-
// case where we render a dropdown
9979
return (
10080
<DropdownMenu>
10181
<DropdownMenuTrigger asChild>
102-
<Button size="icon" variant="secondary" className="rounded-full border">
103-
<EllipsisVerticalIcon className="size-4 text-foreground" />
82+
<Button
83+
variant="outline"
84+
size="sm"
85+
className="rounded-full border gap-2 bg-card [&[data-state=open]>svg]:rotate-180"
86+
>
87+
Resources
88+
<ChevronDownIcon className="size-4 transition-transform duration-200 text-muted-foreground" />
10489
</Button>
10590
</DropdownMenuTrigger>
10691
<DropdownMenuContent
107-
align="center"
108-
className="gap-1 flex flex-col md:w-48 rounded-lg"
92+
align="end"
93+
className="gap-1 flex flex-col w-48 rounded-xl"
10994
sideOffset={10}
11095
>
11196
{orderedLinks.map((link) => {
@@ -115,7 +100,7 @@ export function LinkGroup(props: { links: ActionLink[] }) {
115100
<DropdownMenuItem
116101
key={link.type}
117102
asChild
118-
className="flex flex-row items-center gap-2 cursor-pointer py-2"
103+
className="flex flex-row items-center gap-2 cursor-pointer py-1.5"
119104
>
120105
<Link
121106
href={link.href}

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

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { Button } from "@workspace/ui/components/button";
2-
import { ArrowUpRightIcon } from "lucide-react";
2+
import { ArrowUpRightIcon, Settings2Icon } from "lucide-react";
33
import Link from "next/link";
44
import type { ThirdwebClient } from "thirdweb";
55
import { cn } from "@/lib/utils";
6-
import { ProjectAvatar } from "../avatar/project-avatar";
76
import { type ActionLink, LinkGroup } from "./header/link-group";
87

98
type Action =
@@ -58,63 +57,99 @@ export type ProjectPageHeaderProps = {
5857
title: string;
5958
description?: React.ReactNode;
6059
imageUrl?: string | null;
60+
icon: React.FC<{ className?: string }>;
61+
isProjectIcon?: boolean;
6162
actions: {
6263
primary: Action;
6364
secondary?: Action;
6465
} | null;
6566

6667
links?: ActionLink[];
68+
settings?: {
69+
href: string;
70+
};
6771

6872
// TODO: add task card component
6973
task?: never;
7074
};
7175

7276
export function ProjectPageHeader(props: ProjectPageHeaderProps) {
7377
return (
74-
<header className="flex flex-col gap-4 container max-w-7xl py-6">
75-
{/* main row */}
76-
<div className="flex flex-row items-center justify-between">
77-
{/* left */}
78-
<div className="flex flex-col gap-4">
79-
{/* image */}
80-
{props.imageUrl !== undefined && (
81-
<ProjectAvatar
82-
className="size-12"
83-
client={props.client}
84-
src={props.imageUrl ?? undefined}
85-
/>
78+
<header className="container max-w-7xl py-6 relative">
79+
{/* top row */}
80+
<div className="flex justify-between items-start mb-4">
81+
{/* left - icon */}
82+
<div className="flex">
83+
{props.isProjectIcon ? (
84+
<props.icon />
85+
) : (
86+
<div className="border rounded-full p-2.5 bg-card">
87+
<props.icon className="size-5 text-muted-foreground" />
88+
</div>
8689
)}
87-
{/* title */}
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>
92-
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
93-
{props.description}
94-
</p>
95-
</div>
9690
</div>
9791

98-
{/* right */}
99-
{/* TODO: add "current task" card component */}
100-
</div>
92+
{/* right - buttons */}
93+
<div className="flex items-center gap-3">
94+
<div className="flex items-center gap-3">
95+
{props.links && props.links.length > 0 && (
96+
<LinkGroup links={props.links} />
97+
)}
10198

102-
{/* actions row */}
103-
{props.actions && (
104-
<div className="flex flex-row items-center justify-between">
105-
{/* left actions */}
106-
<div className="flex flex-row items-center gap-3">
107-
{props.actions.primary && <Action action={props.actions.primary} />}
108-
{props.actions.secondary && (
109-
<Action action={props.actions.secondary} variant="secondary" />
99+
{props.settings && (
100+
<Link href={props.settings.href}>
101+
<Button
102+
variant="outline"
103+
size="sm"
104+
className="rounded-full gap-2 bg-card"
105+
>
106+
<Settings2Icon className="size-4 text-muted-foreground" />
107+
Settings
108+
</Button>
109+
</Link>
110110
)}
111111
</div>
112-
{/* right actions */}
113-
{props.links && props.links.length > 0 && (
114-
<LinkGroup links={props.links} />
112+
113+
{/* hide on mobile */}
114+
{props.actions && (
115+
<div className="hidden lg:flex items-center gap-3">
116+
{props.actions.secondary && (
117+
<Action action={props.actions.secondary} variant="secondary" />
118+
)}
119+
120+
{props.actions.primary && (
121+
<Action action={props.actions.primary} />
122+
)}
123+
</div>
115124
)}
116125
</div>
117-
)}
126+
</div>
127+
128+
<div className="space-y-4">
129+
{/* mid row */}
130+
<div className="space-y-1 max-w-3xl">
131+
<h2 className="text-3xl font-semibold tracking-tight">
132+
{props.title}
133+
</h2>
134+
{/* description */}
135+
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
136+
{props.description}
137+
</p>
138+
</div>
139+
140+
{/* bottom row - hidden on desktop */}
141+
{props.actions && (
142+
<div className="flex items-center gap-3 lg:hidden">
143+
{props.actions?.primary && (
144+
<Action action={props.actions.primary} />
145+
)}
146+
147+
{props.actions?.secondary && (
148+
<Action action={props.actions.secondary} variant="secondary" />
149+
)}
150+
</div>
151+
)}
152+
</div>
118153
</header>
119154
);
120155
}

apps/dashboard/src/@/components/contracts/import-contract/modal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { zodResolver } from "@hookform/resolvers/zod";
4-
import { ExternalLinkIcon, ImportIcon } from "lucide-react";
4+
import { ArrowDownToLineIcon, ExternalLinkIcon } from "lucide-react";
55
import Link from "next/link";
66
import { useForm } from "react-hook-form";
77
import { toast } from "sonner";
@@ -234,7 +234,7 @@ function ImportForm(props: {
234234
{addContractToProject.isPending ? (
235235
<Spinner className="size-4" />
236236
) : (
237-
<ImportIcon className="size-4" />
237+
<ArrowDownToLineIcon className="size-4" />
238238
)}
239239

240240
{addContractToProject.isPending ? "Importing" : "Import"}

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

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "@/components/analytics/date-range-selector";
1212
import { ProjectPage } from "@/components/blocks/project-page/project-page";
1313
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
14+
import { SmartAccountIcon } from "@/icons/SmartAccountIcon";
1415
import { getAbsoluteUrl } from "@/utils/vercel";
1516
import { AccountAbstractionSummary } from "./AccountAbstractionAnalytics/AccountAbstractionSummary";
1617
import { SmartWalletsBillingAlert } from "./Alerts";
@@ -93,22 +94,14 @@ export default async function Page(props: {
9394
return (
9495
<ProjectPage
9596
header={{
97+
icon: SmartAccountIcon,
9698
client,
9799
title: "Account Abstraction",
98100
description:
99101
"Integrate EIP-7702 and EIP-4337 compliant smart accounts for gasless sponsorships and more.",
100-
101-
actions: {
102-
primary: {
103-
label: "Documentation",
104-
href: "https://portal.thirdweb.com/transactions/sponsor",
105-
external: true,
106-
},
107-
secondary: {
108-
label: "Playground",
109-
href: "https://playground.thirdweb.com/account-abstraction/eip-7702",
110-
external: true,
111-
},
102+
actions: null,
103+
settings: {
104+
href: `/team/${params.team_slug}/${params.project_slug}/settings/account-abstraction`,
112105
},
113106
links: [
114107
{
@@ -119,10 +112,6 @@ export default async function Page(props: {
119112
type: "playground",
120113
href: "https://playground.thirdweb.com/account-abstraction/eip-7702",
121114
},
122-
{
123-
type: "settings",
124-
href: `/team/${params.team_slug}/${params.project_slug}/settings/account-abstraction`,
125-
},
126115
],
127116
}}
128117
>

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

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { DurationId } from "@/components/analytics/date-range-selector";
66
import { ResponsiveTimeFilters } from "@/components/analytics/responsive-time-filters";
77
import { ProjectPage } from "@/components/blocks/project-page/project-page";
88
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
9+
import { NebulaIcon } from "@/icons/NebulaIcon";
910
import { getFiltersFromSearchParams } from "@/lib/time";
1011
import { loginRedirect } from "@/utils/redirects";
1112
import { AiAnalytics } from "./analytics/chart";
@@ -57,21 +58,11 @@ export default async function Page(props: {
5758
<ResponsiveSearchParamsProvider value={searchParams}>
5859
<ProjectPage
5960
header={{
61+
icon: NebulaIcon,
6062
client,
6163
title: "AI",
6264
description: "Interact with any EVM chain with natural language",
63-
actions: {
64-
primary: {
65-
label: "Documentation",
66-
href: "https://portal.thirdweb.com/ai/chat",
67-
external: true,
68-
},
69-
secondary: {
70-
label: "API Reference",
71-
href: "https://api.thirdweb.com/reference#tag/ai/post/ai/chat",
72-
external: true,
73-
},
74-
},
65+
actions: null,
7566
links: [
7667
{
7768
href: "https://portal.thirdweb.com/ai/chat",

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
HomeIcon,
88
LockIcon,
99
RssIcon,
10-
SettingsIcon,
10+
Settings2Icon,
1111
WebhookIcon,
1212
} from "lucide-react";
1313
import { FullWidthSidebarLayout } from "@/components/blocks/full-width-sidebar-layout";
@@ -138,7 +138,7 @@ export function ProjectSidebarLayout(props: {
138138
},
139139
{
140140
href: `${props.layoutPath}/settings`,
141-
icon: SettingsIcon,
141+
icon: Settings2Icon,
142142
label: "Project Settings",
143143
},
144144
{

0 commit comments

Comments
 (0)