Skip to content

Commit 157f4b9

Browse files
committed
Dashboard: Chain Infra pages UI improvements (#7906)
<!-- ## 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 improving the layout and user experience in the `Deploy Infrastructure` section of the application. It refines component styles, enhances breadcrumb navigation, and updates form elements for better accessibility and consistency. ### Detailed summary - Adjusted spacing in the `InfraServiceCard` component. - Enhanced typography and layout in various sections. - Added breadcrumb navigation for better user context. - Updated the `DeployInfrastructureForm` styles for consistency. - Improved chain selection interface with better guidance. - Refined the subscription summary layout. - Enhanced accessibility features across components. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent fbe1aa2 commit 157f4b9

File tree

6 files changed

+305
-289
lines changed

6 files changed

+305
-289
lines changed

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/infrastructure/[chain_id]/_components/service-card.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ type InfraServiceCardProps = {
1414

1515
export function InfraServiceCard({ title, status }: InfraServiceCardProps) {
1616
return (
17-
<section className="flex flex-col gap-4">
17+
<section className="flex flex-col gap-3">
1818
{/* Header row with status and optional action */}
1919
<div className="flex items-center justify-between gap-4">
20-
<div className="flex items-center gap-2">
21-
<h3 className="text-lg font-semibold">{title}</h3>
20+
<div className="flex items-center gap-2.5">
21+
<h3 className="text-2xl font-semibold tracking-tight">{title}</h3>
2222
<Badge
2323
className="gap-2"
2424
variant={

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

Lines changed: 92 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -66,109 +66,104 @@ export default async function DeployInfrastructureOnChainPage(props: {
6666
: "N/A";
6767

6868
return (
69-
<div className="flex flex-col gap-8">
70-
{/* Chain header */}
71-
<div className="flex flex-col items-center gap-4 md:flex-row">
72-
<h2 className="text-2xl font-bold flex items-center gap-2">
73-
Infrastructure for
74-
</h2>
75-
<Card>
76-
<CardContent className="flex gap-4 items-center p-4">
77-
<span className="flex gap-2 truncate text-left items-center">
78-
{chain.icon && (
79-
<ChainIconClient
80-
className="size-6"
81-
client={client}
82-
loading="lazy"
83-
src={chain.icon?.url}
84-
/>
85-
)}
86-
{cleanChainName(chain.name)}
87-
</span>
88-
89-
<Badge className="gap-2" variant="outline">
90-
<span className="text-muted-foreground">Chain ID</span>
91-
{chain.chainId}
92-
</Badge>
93-
</CardContent>
94-
</Card>
95-
</div>
96-
97-
{PRODUCTS.map((product) => {
98-
const hasSku = chainSubscription.skus.includes(product.sku);
99-
100-
// Map sku to chain service key
101-
const skuToServiceKey: Record<string, string> = {
102-
"chain:infra:account_abstraction": "account-abstraction",
103-
"chain:infra:insight": "insight",
104-
"chain:infra:rpc": "rpc-edge",
105-
};
106-
107-
const serviceKey = skuToServiceKey[product.sku];
108-
const chainService = chain.services.find(
109-
(s) => s.service === serviceKey,
110-
);
111-
const serviceEnabled =
112-
chainService?.enabled ?? chainService?.status === "enabled";
113-
114-
let status: "active" | "pending" | "inactive";
115-
if (hasSku && serviceEnabled) {
116-
status = "active";
117-
} else if (hasSku && !serviceEnabled) {
118-
status = "pending";
119-
} else {
120-
status = "inactive";
121-
}
122-
123-
return (
124-
<InfraServiceCard
125-
key={product.sku}
126-
status={status}
127-
title={product.title}
128-
/>
129-
);
130-
})}
131-
132-
<Separator />
133-
{/* Subscription summary */}
134-
<Card>
135-
<CardContent className="flex flex-col gap-4 p-6 md:flex-row md:items-center md:justify-between">
136-
{/* Left: header + info */}
137-
<div className="flex flex-col gap-2">
138-
<div className="flex gap-2">
139-
<h3 className="text-lg font-semibold">Subscription details </h3>
140-
{chainSubscription.isLegacy && (
141-
<Badge className="gap-0.5" variant="outline">
142-
<span>Enterprise</span>
143-
<ToolTipLabel
144-
label={
145-
<span className="text-xs font-normal">
146-
This subscription is part of an enterprise agreement and
147-
cannot be modified through the dashboard. Please contact
148-
your account executive for any modifications.
149-
</span>
150-
}
151-
>
152-
<InfoIcon className="size-3 ml-1 cursor-help" />
153-
</ToolTipLabel>
154-
</Badge>
155-
)}
69+
<div className="pb-20">
70+
<div className="border-b py-10">
71+
{/* header */}
72+
<div className="container max-w-7xl">
73+
<div className="flex mb-4">
74+
<div className="rounded-full border bg-card p-2">
75+
<ChainIconClient
76+
className="size-8"
77+
client={client}
78+
loading="lazy"
79+
src={chain.icon?.url}
80+
/>
15681
</div>
82+
</div>
83+
84+
<h2 className="text-3xl font-semibold tracking-tight">
85+
{cleanChainName(chain.name)} Infrastructure
86+
</h2>
87+
</div>
88+
</div>
15789

158-
<div className="flex flex-col gap-2 md:flex-row md:items-center md:gap-8">
159-
<div className="flex gap-2 items-center">
160-
<span className="text-muted-foreground">Renews on</span>
161-
<span>{formattedRenewalDate}</span>
90+
<div className="container max-w-7xl pt-8 flex flex-col gap-8">
91+
{PRODUCTS.map((product) => {
92+
const hasSku = chainSubscription.skus.includes(product.sku);
93+
94+
// Map sku to chain service key
95+
const skuToServiceKey: Record<string, string> = {
96+
"chain:infra:account_abstraction": "account-abstraction",
97+
"chain:infra:insight": "insight",
98+
"chain:infra:rpc": "rpc-edge",
99+
};
100+
101+
const serviceKey = skuToServiceKey[product.sku];
102+
const chainService = chain.services.find(
103+
(s) => s.service === serviceKey,
104+
);
105+
const serviceEnabled =
106+
chainService?.enabled ?? chainService?.status === "enabled";
107+
108+
let status: "active" | "pending" | "inactive";
109+
if (hasSku && serviceEnabled) {
110+
status = "active";
111+
} else if (hasSku && !serviceEnabled) {
112+
status = "pending";
113+
} else {
114+
status = "inactive";
115+
}
116+
117+
return (
118+
<InfraServiceCard
119+
key={product.sku}
120+
status={status}
121+
title={product.title}
122+
/>
123+
);
124+
})}
125+
126+
<Separator />
127+
{/* Subscription summary */}
128+
<Card>
129+
<CardContent className="flex flex-col gap-4 p-6 md:flex-row md:items-center md:justify-between">
130+
{/* Left: header + info */}
131+
<div className="flex flex-col gap-2">
132+
<div className="flex gap-2">
133+
<h3 className="text-lg font-semibold">Subscription details </h3>
134+
{chainSubscription.isLegacy && (
135+
<Badge className="gap-0.5" variant="outline">
136+
<span>Enterprise</span>
137+
<ToolTipLabel
138+
label={
139+
<span className="text-xs font-normal">
140+
This subscription is part of an enterprise agreement
141+
and cannot be modified through the dashboard. Please
142+
contact your account executive for any modifications.
143+
</span>
144+
}
145+
>
146+
<InfoIcon className="size-3 ml-1 cursor-help" />
147+
</ToolTipLabel>
148+
</Badge>
149+
)}
162150
</div>
163151

164-
<div className="flex gap-2 items-center">
165-
<span className="text-muted-foreground">Amount due</span>
166-
<span>{formattedAmountDue}</span>
152+
<div className="flex flex-col gap-2 md:flex-row md:items-center md:gap-8">
153+
<div className="flex gap-2 items-center">
154+
<span className="text-muted-foreground">Renews on</span>
155+
<span>{formattedRenewalDate}</span>
156+
</div>
157+
158+
<div className="flex gap-2 items-center">
159+
<span className="text-muted-foreground">Amount due</span>
160+
<span>{formattedAmountDue}</span>
161+
</div>
167162
</div>
168163
</div>
169-
</div>
170-
</CardContent>
171-
</Card>
164+
</CardContent>
165+
</Card>
166+
</div>
172167
</div>
173168
);
174169
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/infrastructure/deploy/[chain_id]/page.tsx

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { ArrowUpDownIcon } from "lucide-react";
2-
import Link from "next/link";
31
import { notFound, redirect } from "next/navigation";
42
import { getValidAccount } from "@/api/account/get-account";
53
import { getMembers } from "@/api/team/team-members";
6-
import { Badge } from "@/components/ui/badge";
7-
import { Button } from "@/components/ui/button";
8-
import { Card, CardContent } from "@/components/ui/card";
4+
import {
5+
Breadcrumb,
6+
BreadcrumbItem,
7+
BreadcrumbLink,
8+
BreadcrumbList,
9+
BreadcrumbSeparator,
10+
} from "@/components/ui/breadcrumb";
911
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
1012
import { ChainIconClient } from "@/icons/ChainIcon";
1113
import { getChain } from "../../../../../../../(dashboard)/(chain)/utils";
@@ -45,47 +47,55 @@ export default async function DeployInfrastructureOnChainPage(props: {
4547
const client = getClientThirdwebClient();
4648

4749
return (
48-
<div className="flex flex-col gap-8">
49-
<div className="flex flex-col items-center gap-4 md:flex-row">
50-
<h2 className="text-2xl font-bold flex items-center gap-2">
51-
Deploy Infrastructure on
52-
</h2>
53-
<Card>
54-
<CardContent className="flex gap-4 items-center p-4">
55-
<span className="flex gap-2 truncate text-left items-center">
56-
{chain.icon && (
57-
<ChainIconClient
58-
className="size-6"
59-
client={client}
60-
loading="lazy"
61-
src={chain.icon?.url}
62-
/>
63-
)}
64-
{cleanChainName(chain.name)}
65-
</span>
50+
<div className="flex flex-col pb-20">
51+
{/* breadcrumb */}
52+
<div className="border-b border-dashed py-3">
53+
<Breadcrumb className="container max-w-7xl">
54+
<BreadcrumbList>
55+
<BreadcrumbItem>
56+
<BreadcrumbLink
57+
href={`/team/${params.team_slug}/~/infrastructure/deploy`}
58+
>
59+
{" "}
60+
Deploy Infrastructure{" "}
61+
</BreadcrumbLink>
62+
</BreadcrumbItem>
63+
<BreadcrumbSeparator />
64+
<BreadcrumbItem>
65+
<BreadcrumbLink>{cleanChainName(chain.name)}</BreadcrumbLink>
66+
</BreadcrumbItem>
67+
</BreadcrumbList>
68+
</Breadcrumb>
69+
</div>
70+
71+
{/* header */}
72+
<div className="border-b py-10">
73+
<div className="container max-w-7xl">
74+
<div className="flex mb-4">
75+
<div className="p-2 rounded-full bg-card border">
76+
<ChainIconClient
77+
className="size-8"
78+
client={client}
79+
loading="lazy"
80+
src={chain.icon?.url}
81+
/>
82+
</div>
83+
</div>
84+
85+
<h2 className="text-3xl font-semibold tracking-tight">
86+
Deploy Infrastructure on {cleanChainName(chain.name)}
87+
</h2>
88+
</div>
89+
</div>
6690

67-
<Badge className="gap-2" variant="outline">
68-
<span className="text-muted-foreground">Chain ID</span>
69-
{chain.chainId}
70-
</Badge>
71-
<Button
72-
asChild
73-
className="text-muted-foreground p-0 hover:text-foreground size-4"
74-
size="icon"
75-
variant="link"
76-
>
77-
<Link href={`/team/${params.team_slug}/~/infrastructure/deploy`}>
78-
<ArrowUpDownIcon className="size-4" />
79-
</Link>
80-
</Button>
81-
</CardContent>
82-
</Card>
91+
{/* form */}
92+
<div className="container max-w-7xl pt-8">
93+
<DeployInfrastructureForm
94+
chain={chain}
95+
isOwner={accountMemberInfo.role === "OWNER"}
96+
teamSlug={params.team_slug}
97+
/>
8398
</div>
84-
<DeployInfrastructureForm
85-
chain={chain}
86-
isOwner={accountMemberInfo.role === "OWNER"}
87-
teamSlug={params.team_slug}
88-
/>
8999
</div>
90100
);
91101
}

0 commit comments

Comments
 (0)