Skip to content

Commit de3136d

Browse files
authored
Merge branch 'main' into ub-and-webhooks
2 parents e7c7d3f + 59dbae0 commit de3136d

File tree

28 files changed

+263
-179
lines changed

28 files changed

+263
-179
lines changed

.changeset/many-hounds-wave.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@thirdweb-dev/service-utils": patch
3+
---
4+
5+
remove:
6+
- `starter_legacy` plan type
7+
- `billingPlanVersion` field
8+
- `growthTrialEligible` field

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161
uses: ./.github/composite-actions/install
6262

6363
- name: Setup Biome
64-
uses: biomejs/setup-biome@f382a98e582959e6aaac8e5f8b17b31749018780 # v2.5.0
64+
uses: biomejs/setup-biome@a9763ed3d2388f5746f9dc3e1a55df7f4609bc89 # v2.5.1
6565
with:
6666
version: latest
6767

apps/dashboard/src/@/components/billing.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ export function BillingPortalButton(props: {
8484
props.buttonProps?.onClick?.(e);
8585
}}
8686
>
87-
<Link
87+
<a
8888
href={buildBillingPortalUrl({ teamSlug: props.teamSlug })}
8989
target="_blank"
90+
rel="noreferrer"
9091
>
9192
{props.children}
92-
</Link>
93+
</a>
9394
</Button>
9495
);
9596
}

apps/dashboard/src/@/components/blocks/pricing-card.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ const billingPlanToSkuMap: Record<Team["billingPlan"], ProductSKU | undefined> =
192192
accelerate: undefined,
193193
free: undefined,
194194
growth_legacy: undefined,
195-
starter_legacy: undefined,
196195
};
197196

198197
type FeatureItemProps = {

apps/dashboard/src/@/components/ui/button.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,35 @@ export interface ButtonProps
4545
}
4646

4747
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
48-
({ className, variant, size, asChild = false, ...props }, ref) => {
48+
({ className, variant, size, asChild = false, disabled, ...props }, ref) => {
4949
const Comp = asChild ? Slot : "button";
50+
51+
// "button" elements automatically handle the `disabled` attribute.
52+
// For non-button elements rendered via `asChild` (e.g. <a>), we still want
53+
// to visually convey the disabled state and prevent user interaction.
54+
// We do that by conditionally adding the same utility classes that the
55+
// `disabled:` pseudo-variant would normally apply and by setting
56+
// `aria-disabled` for accessibility.
57+
const disabledClass = disabled ? "pointer-events-none opacity-50" : "";
58+
5059
const btnOnlyProps =
5160
Comp === "button"
52-
? { type: props.type || ("button" as const) }
61+
? {
62+
type:
63+
(props as React.ButtonHTMLAttributes<HTMLButtonElement>).type ||
64+
("button" as const),
65+
}
5366
: undefined;
5467

5568
return (
5669
<Comp
57-
className={cn(buttonVariants({ variant, size, className }))}
70+
className={cn(
71+
buttonVariants({ variant, size, className }),
72+
disabledClass,
73+
)}
5874
ref={ref}
75+
aria-disabled={disabled ? true : undefined}
76+
disabled={disabled}
5977
{...props}
6078
{...btnOnlyProps}
6179
/>

apps/dashboard/src/app/(app)/components/TeamPlanBadge.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const teamPlanToBadgeVariant: Record<
1414
free: "secondary",
1515
// yellow
1616
starter: "warning",
17-
starter_legacy: "warning",
17+
1818
growth_legacy: "warning",
1919
// green
2020
accelerate: "success",
@@ -28,9 +28,7 @@ export function getTeamPlanBadgeLabel(plan: Team["billingPlan"]) {
2828
if (plan === "growth_legacy") {
2929
return "Growth - Legacy";
3030
}
31-
if (plan === "starter_legacy") {
32-
return "Starter - Legacy";
33-
}
31+
3432
return plan;
3533
}
3634

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.client.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ export function PlanInfoCardClient(props: {
99
team: Team;
1010
openPlanSheetButtonByDefault: boolean;
1111
highlightPlan: Team["billingPlan"] | undefined;
12+
isOwnerAccount: boolean;
1213
}) {
1314
return (
1415
<PlanInfoCardUI
1516
openPlanSheetButtonByDefault={props.openPlanSheetButtonByDefault}
1617
team={props.team}
1718
subscriptions={props.subscriptions}
19+
isOwnerAccount={props.isOwnerAccount}
1820
getTeam={async () => {
1921
const res = await apiServerProxy<{
2022
result: Team;

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.stories.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ export const Free: Story = {
2424
},
2525
};
2626

27-
export const StarterLegacy: Story = {
28-
args: {
29-
plan: "starter_legacy",
30-
},
31-
};
32-
3327
export const Starter: Story = {
3428
args: {
3529
plan: "starter",
@@ -120,6 +114,7 @@ function Story(props: {
120114
getTeam={teamTeamStub}
121115
highlightPlan={undefined}
122116
openPlanSheetButtonByDefault={false}
117+
isOwnerAccount={true}
123118
/>
124119
</BadgeContainer>
125120

@@ -133,6 +128,7 @@ function Story(props: {
133128
getTeam={teamTeamStub}
134129
highlightPlan={undefined}
135130
openPlanSheetButtonByDefault={false}
131+
isOwnerAccount={true}
136132
/>
137133
</BadgeContainer>
138134

@@ -143,6 +139,7 @@ function Story(props: {
143139
getTeam={teamTeamStub}
144140
highlightPlan={undefined}
145141
openPlanSheetButtonByDefault={false}
142+
isOwnerAccount={true}
146143
/>
147144
</BadgeContainer>
148145

@@ -153,6 +150,7 @@ function Story(props: {
153150
getTeam={teamTeamStub}
154151
highlightPlan={undefined}
155152
openPlanSheetButtonByDefault={false}
153+
isOwnerAccount={true}
156154
/>
157155
</BadgeContainer>
158156

@@ -163,6 +161,7 @@ function Story(props: {
163161
getTeam={teamTeamStub}
164162
highlightPlan={undefined}
165163
openPlanSheetButtonByDefault={false}
164+
isOwnerAccount={true}
166165
/>
167166
</BadgeContainer>
168167
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx

Lines changed: 93 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
SheetHeader,
1414
SheetTitle,
1515
} from "@/components/ui/sheet";
16+
import { ToolTipLabel } from "@/components/ui/tooltip";
1617
import { CancelPlanButton } from "components/settings/Account/Billing/CancelPlanModal/CancelPlanModal";
1718
import { BillingPricing } from "components/settings/Account/Billing/Pricing";
1819
import { differenceInDays, isAfter } from "date-fns";
@@ -30,6 +31,7 @@ export function PlanInfoCardUI(props: {
3031
getTeam: () => Promise<Team>;
3132
openPlanSheetButtonByDefault: boolean;
3233
highlightPlan: Team["billingPlan"] | undefined;
34+
isOwnerAccount: boolean;
3335
}) {
3436
const { subscriptions, team, openPlanSheetButtonByDefault } = props;
3537
const validPlan = getValidTeamPlan(team);
@@ -66,12 +68,7 @@ export function PlanInfoCardUI(props: {
6668
<div className="flex flex-col items-start gap-0.5">
6769
<div className="flex items-center gap-2">
6870
<h3 className="font-semibold text-2xl capitalize tracking-tight">
69-
{validPlan === "growth_legacy"
70-
? "Growth"
71-
: validPlan === "starter_legacy"
72-
? "Starter"
73-
: validPlan}{" "}
74-
Plan
71+
{validPlan === "growth_legacy" ? "Growth" : validPlan} Plan
7572
</h3>
7673
{validPlan.includes("legacy") && (
7774
<Badge variant="warning">Legacy</Badge>
@@ -112,32 +109,57 @@ export function PlanInfoCardUI(props: {
112109

113110
{props.team.billingPlan !== "free" && (
114111
<div className="flex items-center gap-3">
115-
<Button
116-
variant="outline"
117-
size="sm"
118-
className="gap-2 bg-background"
119-
onClick={() => {
120-
setIsPlanSheetOpen(true);
121-
}}
112+
<ToolTipLabel
113+
label={
114+
props.isOwnerAccount
115+
? null
116+
: "Only team owners can change plans."
117+
}
122118
>
123-
<SquarePenIcon className="size-4 text-muted-foreground" />
124-
Change Plan
125-
</Button>
119+
<div>
120+
<Button
121+
variant="outline"
122+
size="sm"
123+
className="gap-2 bg-background"
124+
onClick={() => {
125+
setIsPlanSheetOpen(true);
126+
}}
127+
disabled={!props.isOwnerAccount}
128+
>
129+
<SquarePenIcon className="size-4 text-muted-foreground" />
130+
Change Plan
131+
</Button>
132+
</div>
133+
</ToolTipLabel>
126134

127-
{props.team.planCancellationDate ? (
128-
<RenewSubscriptionButton
129-
teamId={props.team.id}
130-
getTeam={props.getTeam}
131-
/>
132-
) : (
133-
<CancelPlanButton
134-
teamId={props.team.id}
135-
teamSlug={props.team.slug}
136-
billingStatus={props.team.billingStatus}
137-
currentPlan={props.team.billingPlan}
138-
getTeam={props.getTeam}
139-
/>
140-
)}
135+
<ToolTipLabel
136+
label={
137+
props.isOwnerAccount
138+
? null
139+
: props.team.planCancellationDate
140+
? "Only team owners can renew plans."
141+
: "Only team owners can cancel plans."
142+
}
143+
>
144+
<div>
145+
{props.team.planCancellationDate ? (
146+
<RenewSubscriptionButton
147+
teamId={props.team.id}
148+
getTeam={props.getTeam}
149+
disabled={!props.isOwnerAccount}
150+
/>
151+
) : (
152+
<CancelPlanButton
153+
teamId={props.team.id}
154+
teamSlug={props.team.slug}
155+
billingStatus={props.team.billingStatus}
156+
currentPlan={props.team.billingPlan}
157+
getTeam={props.getTeam}
158+
disabled={!props.isOwnerAccount}
159+
/>
160+
)}
161+
</div>
162+
</ToolTipLabel>
141163
</div>
142164
)}
143165
</div>
@@ -153,16 +175,28 @@ export function PlanInfoCardUI(props: {
153175
To unlock additional usage, upgrade your plan to Starter or
154176
Growth.
155177
</p>
178+
156179
<div className="mt-4">
157-
<Button
158-
variant="default"
159-
size="sm"
160-
onClick={() => {
161-
setIsPlanSheetOpen(true);
162-
}}
180+
<ToolTipLabel
181+
label={
182+
props.isOwnerAccount
183+
? null
184+
: "Only team owners can change plans."
185+
}
163186
>
164-
Select a plan
165-
</Button>
187+
<div>
188+
<Button
189+
disabled={!props.isOwnerAccount}
190+
variant="default"
191+
size="sm"
192+
onClick={() => {
193+
setIsPlanSheetOpen(true);
194+
}}
195+
>
196+
Select a plan
197+
</Button>
198+
</div>
199+
</ToolTipLabel>
166200
</div>
167201
</div>
168202
) : (
@@ -203,17 +237,28 @@ export function PlanInfoCardUI(props: {
203237
</Button>
204238

205239
{/* manage team billing */}
206-
<BillingPortalButton
207-
teamSlug={team.slug}
208-
buttonProps={{
209-
variant: "outline",
210-
size: "sm",
211-
className: "bg-background gap-2",
212-
}}
240+
<ToolTipLabel
241+
label={
242+
props.isOwnerAccount
243+
? null
244+
: "Only team owners can manage billing."
245+
}
213246
>
214-
<CreditCardIcon className="size-4 text-muted-foreground" />
215-
Manage Billing
216-
</BillingPortalButton>
247+
<div>
248+
<BillingPortalButton
249+
teamSlug={team.slug}
250+
buttonProps={{
251+
variant: "outline",
252+
size: "sm",
253+
className: "bg-background gap-2",
254+
disabled: !props.isOwnerAccount,
255+
}}
256+
>
257+
<CreditCardIcon className="size-4 text-muted-foreground" />
258+
Manage Billing
259+
</BillingPortalButton>
260+
</div>
261+
</ToolTipLabel>
217262
</div>
218263
</div>
219264
)}

0 commit comments

Comments
 (0)