Skip to content

Commit 486c41c

Browse files
committed
Dashboard: Chain Infra pages UI improvement
1 parent 1ffe360 commit 486c41c

File tree

4 files changed

+210
-189
lines changed

4 files changed

+210
-189
lines changed

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
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/infrastructure/deploy/_components/deploy-infrastructure-form.client.tsx

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"use client";
22

3+
import { CheckIcon, InfoIcon } from "lucide-react";
34
import { useQueryState } from "nuqs";
45
import { useMemo, useState, useTransition } from "react";
56
import { toast } from "sonner";
67
import { getChainInfraCheckoutURL } from "@/actions/billing";
78
import { reportChainInfraRpcOmissionAgreed } from "@/analytics/report";
9+
import { Alert, AlertTitle } from "@/components/ui/alert";
810
import { Badge } from "@/components/ui/badge";
911
import { Button } from "@/components/ui/button";
1012
import {
@@ -115,11 +117,11 @@ export function DeployInfrastructureForm(props: {
115117

116118
const bundleHint = useMemo(() => {
117119
if (selectedCount === 1) {
118-
return "Add one more add-on to unlock a 10% bundle discount.";
120+
return "Add one more add-on to unlock a 10% bundle discount";
119121
} else if (selectedCount === 2) {
120-
return "Add another add-on to increase your bundle discount to 15%.";
122+
return "Add another add-on to increase your bundle discount to 15%";
121123
} else if (selectedCount >= 3) {
122-
return "🎉 Congrats! You unlocked the maximum 15% bundle discount.";
124+
return "Congrats! You unlocked the maximum 15% bundle discount";
123125
}
124126
return null;
125127
}, [selectedCount]);
@@ -234,8 +236,8 @@ export function DeployInfrastructureForm(props: {
234236
return (
235237
<div className="flex flex-col gap-8 lg:flex-row">
236238
{/* Left column: service selection + frequency */}
237-
<div className="flex flex-col gap-4">
238-
<h3 className="text-lg font-semibold">Select Services</h3>
239+
<div className="flex flex-col gap-3">
240+
<h3 className="text-xl font-semibold tracking-tight">Services</h3>
239241

240242
{/* RPC (now optional) */}
241243
<div className="flex flex-col gap-2 mb-6">
@@ -258,13 +260,11 @@ export function DeployInfrastructureForm(props: {
258260

259261
{/* Optional add-ons */}
260262
<div className="flex flex-col gap-2">
261-
<div className="flex items-center justify-between">
262-
<p className="text-muted-foreground text-sm">Add-ons</p>
263-
{bundleHint && (
264-
<p className="text-xs font-medium text-primary">{bundleHint}</p>
265-
)}
263+
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between">
264+
<h3 className="text-xl font-semibold tracking-tight">Add-ons</h3>
266265
</div>
267-
<div className="grid gap-4 sm:grid-cols-2">
266+
267+
<div className="space-y-4">
268268
{/* Insight */}
269269
<ServiceCard
270270
description={SERVICE_CONFIG.insight.description}
@@ -304,14 +304,21 @@ export function DeployInfrastructureForm(props: {
304304
price={pricePerService.accountAbstraction}
305305
selected={includeAA}
306306
/>
307+
308+
{bundleHint && (
309+
<Alert variant="default" className="relative overflow-hidden">
310+
<InfoIcon className="size-5" />
311+
<AlertTitle>{bundleHint}</AlertTitle>
312+
</Alert>
313+
)}
307314
</div>
308315
</div>
309316
</div>
310317

311318
{/* Right column: order summary */}
312-
<div className="w-full lg:max-w-sm border rounded-md p-6 bg-muted/30 h-fit">
313-
<h3 className="font-medium mb-4">Order Summary</h3>
314-
<div className="space-y-2 text-sm">
319+
<div className="w-full lg:max-w-sm border rounded-xl p-4 bg-card h-fit">
320+
<h3 className="font-semibold mb-4 text-lg">Order Summary</h3>
321+
<div className="space-y-3 text-sm">
315322
{selectedOrder.map((key) => (
316323
<div className="flex justify-between" key={key}>
317324
<span>{SERVICE_CONFIG[key].label}</span>
@@ -329,15 +336,16 @@ export function DeployInfrastructureForm(props: {
329336
</div>
330337
))}
331338

332-
<div className="flex justify-between pt-2 border-t mt-2">
339+
<div className="flex justify-between pt-3 border-t mt-3">
333340
<span>Subtotal</span>
334341
<span>
335342
{formatUSD(subtotal)}
336343
{periodLabel}
337344
</span>
338345
</div>
346+
339347
{bundleDiscount > 0 && (
340-
<div className="flex justify-between text-green-600">
348+
<div className="flex justify-between text-green-600 font-medium">
341349
<span>
342350
Bundle Discount (
343351
{Object.values(selectedServices).filter(Boolean).length === 2
@@ -485,50 +493,52 @@ function ServiceCard(props: {
485493
icon,
486494
onToggle,
487495
} = props;
496+
497+
const IconComp = getIcon(icon);
488498
return (
489499
<button
490500
className={cn(
491-
"flex flex-col items-start gap-3 rounded-lg p-4 text-left transition-colors border",
492-
disabled
493-
? "border-primary bg-primary/10"
494-
: selected
495-
? "border-primary bg-primary/10 hover:bg-primary/10 hover:border-primary/50"
496-
: "hover:border-primary/50 hover:bg-muted/40",
501+
"flex bg-card flex-col items-start rounded-xl p-4 text-left transition-colors border relative",
502+
disabled && "opacity-50",
497503
)}
498504
disabled={disabled}
499505
onClick={() => !disabled && onToggle()}
500506
type="button"
501507
>
502-
<div className="flex items-center justify-between w-full">
503-
<h4 className="font-semibold text-lg leading-none flex gap-2 items-center">
504-
{(() => {
505-
const IconComp = getIcon(icon);
506-
return <IconComp className="size-4" />;
507-
})()}
508-
{label}
509-
{required && <Badge variant="outline">Always Included</Badge>}
510-
</h4>
511-
{!disabled && (
512-
<span
513-
className={cn(
514-
"size-4 rounded-full border flex items-center justify-center transition-colors",
515-
selected ? "bg-primary border-primary" : "",
516-
)}
517-
>
518-
{selected && <span className="size-2 bg-card rounded-full" />}
519-
</span>
520-
)}
508+
<div className="flex mb-4">
509+
<div className="rounded-full p-2 bg-card border">
510+
<IconComp className="size-4 text-muted-foreground" />
511+
</div>
521512
</div>
513+
514+
{!disabled && (
515+
<span
516+
className={cn(
517+
"absolute top-4 right-4 size-6 rounded-full border-2 border-active-border flex items-center justify-center transition-colors",
518+
selected && "border-foreground bg-foreground",
519+
)}
520+
>
521+
{selected && (
522+
<CheckIcon className="size-4 text-inverted-foreground" />
523+
)}
524+
</span>
525+
)}
526+
527+
<h4 className="font-semibold text-lg mb-1 flex gap-2 items-center">
528+
{label}
529+
{required && <Badge variant="outline">Always Included</Badge>}
530+
</h4>
522531
<p className="text-muted-foreground text-sm min-h-[48px]">
523532
{description}
524533
</p>
525-
<p className="mt-auto font-medium flex items-center gap-2">
534+
535+
<p className="mt-auto pt-3 font-medium flex items-center gap-2">
526536
{originalPrice && (
527537
<span className="text-muted-foreground line-through text-xs">
528538
{formatUSD(originalPrice)}
529539
</span>
530540
)}
531-
<span>
541+
<span className="font-medium">
532542
{formatUSD(price)}
533543
{periodLabel}
534544
</span>

0 commit comments

Comments
 (0)