Skip to content

Commit e7bdc60

Browse files
committed
finalize
1 parent 8bf672f commit e7bdc60

3 files changed

Lines changed: 92 additions & 67 deletions

File tree

apps/web/app/api/domains/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ export const POST = withWorkspace(
207207
slug: slug,
208208
projectId: workspace.id,
209209
primary: totalDomains === 0,
210+
...(slug.endsWith(".dub.link") && {
211+
verified: true,
212+
}),
210213
...(placeholder && { placeholder }),
211214
expiredUrl,
212215
notFoundUrl,

apps/web/app/app.dub.co/(onboarding)/onboarding/(steps)/products/product-selector.tsx

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

33
import { MarkdownDescription } from "@/ui/shared/markdown-description";
4-
import { Button, Crown, DubProductIcon, type Icon } from "@dub/ui";
4+
import { Button, DubProductIcon } from "@dub/ui";
55
import { capitalize } from "@dub/utils";
66
import { usePlausible } from "next-plausible";
77
import Image from "next/image";
@@ -10,20 +10,16 @@ import { useOnboardingProgress } from "../../use-onboarding-progress";
1010

1111
const products = {
1212
partners: {
13-
image: "https://assets.dub.co/cms/trophy.webp",
13+
image: "https://assets.dub.co/icons/trophy.webp",
1414
title: "Dub Partners",
1515
href: "https://dub.co/partners",
16-
planLabel: "Paid plan required",
17-
planIcon: Crown,
1816
description:
1917
"Modern [affiliate programs](https://dub.co/partners) with [global payouts](https://dub.co/help/article/partner-payouts) and [accurate attribution](https://dub.co/help/article/program-analytics).",
2018
},
2119
links: {
22-
image: "https://assets.dub.co/cms/link.webp",
20+
image: "https://assets.dub.co/icons/link.webp",
2321
title: "Dub Links",
2422
href: "https://dub.co/links",
25-
planLabel: "Free plan available",
26-
planIcon: undefined,
2723
description:
2824
"[Short links](https://dub.co/help/category/link-management), [QR codes](https://dub.co/help/article/custom-qr-codes), [real-time analytics](https://dub.co/help/article/dub-analytics), and [conversion tracking](https://dub.co/docs/conversions/quickstart).",
2925
},
@@ -37,8 +33,6 @@ export function ProductSelector() {
3733
key={key}
3834
product={key as "links" | "partners"}
3935
icon={product.image}
40-
planLabel={product.planLabel}
41-
planIcon={product.planIcon}
4236
title={
4337
<a
4438
href={product.href}
@@ -63,63 +57,53 @@ export function ProductSelector() {
6357
function ProductOption({
6458
product,
6559
icon,
66-
planLabel,
67-
planIcon: PlanIcon,
6860
title,
6961
description,
7062
cta,
7163
}: {
7264
product: "links" | "partners";
7365
icon: string;
74-
planLabel: string;
75-
planIcon?: Icon;
7666
title: ReactNode;
7767
description: string;
7868
cta: string;
7969
}) {
8070
const { continueTo, isLoading, isSuccessful } = useOnboardingProgress();
8171
const plausible = usePlausible();
8272
return (
83-
<div className="relative flex h-full flex-col items-center rounded-xl border border-neutral-200 bg-white p-3 transition-all">
84-
<div className="relative flex h-52 w-full items-center justify-center rounded-xl bg-neutral-50">
85-
<div className="absolute inset-x-2 top-2 flex h-6 items-center justify-center gap-2 rounded-lg border border-neutral-200 bg-white text-xs font-semibold text-neutral-600">
86-
{PlanIcon && <PlanIcon className="size-3.5 text-neutral-900" />}
87-
{planLabel}
88-
</div>
89-
<div className="relative mt-8 size-32">
90-
<Image
91-
src={icon}
92-
alt=""
93-
fill
94-
className="object-contain"
95-
fetchPriority="high"
96-
/>
97-
</div>
73+
<div className="relative flex h-full flex-col items-center gap-6 rounded-xl border border-neutral-300 p-6 pt-12 transition-all">
74+
<div className="relative size-36">
75+
<Image
76+
src={icon}
77+
alt=""
78+
fill
79+
className="object-contain"
80+
fetchPriority="high"
81+
/>
82+
</div>
83+
<div className="space-y-2 text-center">
84+
<span className="text-base font-semibold text-neutral-900">
85+
{title}
86+
</span>
87+
<MarkdownDescription className="text-sm text-neutral-500">
88+
{description}
89+
</MarkdownDescription>
9890
</div>
99-
<div className="p-2">
100-
<div className="mt-2 space-y-3 text-center">
101-
<span className="text-base font-semibold text-neutral-900">{title}</span>
102-
<MarkdownDescription className="text-sm leading-snug text-neutral-500">
103-
{description}
104-
</MarkdownDescription>
105-
</div>
106-
<div className="mt-6 flex w-full grow flex-col justify-end">
107-
<Button
108-
type="button"
109-
variant="primary"
110-
className="h-10 rounded-lg text-sm"
111-
onClick={() => {
112-
plausible("Selected Product", {
113-
props: {
114-
product: capitalize(product),
115-
},
116-
});
117-
continueTo("domain", { params: { product } });
118-
}}
119-
loading={isLoading || isSuccessful}
120-
text={cta}
121-
/>
122-
</div>
91+
<div className="flex w-full grow flex-col justify-end gap-2">
92+
<Button
93+
type="button"
94+
variant="primary"
95+
className="rounded-lg"
96+
onClick={() => {
97+
plausible("Selected Product", {
98+
props: {
99+
product: capitalize(product),
100+
},
101+
});
102+
continueTo("domain", { params: { product } });
103+
}}
104+
loading={isLoading || isSuccessful}
105+
text={cta}
106+
/>
123107
</div>
124108
</div>
125109
);

apps/web/ui/partners/program-link-configuration.tsx

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ import {
1818
Wordmark,
1919
} from "@dub/ui";
2020
import { ArrowTurnRight2, ChevronRight, LoadingSpinner } from "@dub/ui/icons";
21-
import { cn, fetcher, getApexDomain, getPrettyUrl, truncate } from "@dub/utils";
21+
import {
22+
cn,
23+
fetcher,
24+
getApexDomain,
25+
getPrettyUrl,
26+
isWorkspaceBillingTrialActive,
27+
truncate,
28+
} from "@dub/utils";
2229
import { AnimatePresence, motion } from "motion/react";
2330
import { FormEvent, useEffect, useMemo, useState } from "react";
2431
import { toast } from "sonner";
2532
import useSWRImmutable from "swr/immutable";
2633
import { useDebounce } from "use-debounce";
2734
import { useAddEditDomainModal } from "../modals/add-edit-domain-modal";
35+
import { useRegisterDomainModal } from "../modals/register-domain-modal";
2836

2937
type DomainProps = {
3038
domain: string | null;
@@ -131,9 +139,10 @@ export function ProgramLinkConfiguration({
131139
}
132140

133141
function DomainOnboarding({ domain, onDomainChange }: DomainProps) {
134-
const { slug } = useWorkspace();
142+
const { slug, trialEndsAt } = useWorkspace();
135143
const { allWorkspaceDomains: domains, loading: isLoadingDomains } =
136144
useDomains();
145+
const trialActive = isWorkspaceBillingTrialActive(trialEndsAt);
137146

138147
const [state, setState] = useState<"idle" | "select">(
139148
domain ? "select" : "idle",
@@ -148,6 +157,15 @@ function DomainOnboarding({ domain, onDomainChange }: DomainProps) {
148157
},
149158
});
150159

160+
const { RegisterDomainModal, setShowRegisterDomainModal } =
161+
useRegisterDomainModal({
162+
onSuccess: (domain) => {
163+
onDomainChange(domain);
164+
setState("select");
165+
},
166+
setRegisteredParam: false,
167+
});
168+
151169
const idleOptions = useMemo(
152170
() => [
153171
{
@@ -163,25 +181,38 @@ function DomainOnboarding({ domain, onDomainChange }: DomainProps) {
163181
},
164182
loading: isLoadingDomains,
165183
},
166-
{
167-
icon: <Wordmark className="h-3 text-neutral-900" />,
168-
title: "Use .dub.link subdomain",
169-
badge: "Instant setup",
170-
badgeClassName: "bg-bg-inverted/10 text-neutral-800",
171-
description: "A fast way to launch. Switch to a custom domain later.",
172-
onSelect: () => setShowSubdomainModal(true),
173-
},
184+
trialActive
185+
? {
186+
icon: <Wordmark className="h-3 text-neutral-900" />,
187+
title: "Use .dub.link subdomain",
188+
badge: "Instant setup",
189+
badgeClassName: "bg-bg-inverted/10 text-neutral-800",
190+
description:
191+
"A fast way to launch. Switch to a custom domain later.",
192+
onSelect: () => setShowSubdomainModal(true),
193+
}
194+
: {
195+
icon: "https://assets.dub.co/icons/crown.webp",
196+
title: "Claim a free .link domain",
197+
badge: "No setup",
198+
badgeClassName: "bg-green-100 text-green-800",
199+
description: "Free for one year with your paid account.",
200+
onSelect: () => setShowRegisterDomainModal(true),
201+
},
174202
],
175203
[
176204
domains,
177205
isLoadingDomains,
206+
trialActive,
178207
setShowAddEditDomainModal,
208+
setShowRegisterDomainModal,
179209
setShowSubdomainModal,
180210
],
181211
);
182212

183213
return (
184214
<>
215+
<RegisterDomainModal />
185216
<AddEditDomainModal />
186217
<Modal
187218
showModal={showSubdomainModal}
@@ -250,9 +281,17 @@ function DomainOnboarding({ domain, onDomainChange }: DomainProps) {
250281
>
251282
<div className="flex items-center gap-3">
252283
<div className="bg-bg-inverted/5 flex size-10 items-center justify-center rounded-lg">
253-
<div className="transition-transform ease-out group-hover:scale-105">
254-
{option.icon}
255-
</div>
284+
{typeof option.icon === "string" ? (
285+
<img
286+
src={option.icon}
287+
alt=""
288+
className="size-7 object-contain transition-transform ease-out group-hover:scale-105"
289+
/>
290+
) : (
291+
<div className="transition-transform ease-out group-hover:scale-105">
292+
{option.icon}
293+
</div>
294+
)}
256295
</div>
257296
<div className="flex flex-col">
258297
<div className="flex items-center gap-1">
@@ -548,7 +587,6 @@ function DubLinkSubdomainForm({
548587
function DomainOnboardingSelection({
549588
domain,
550589
onDomainChange,
551-
onBack,
552590
}: DomainProps & { onBack: () => void }) {
553591
const { id: workspaceId } = useWorkspace();
554592

@@ -570,7 +608,7 @@ function DomainOnboardingSelection({
570608
/>
571609

572610
<p className="mt-2 text-xs font-normal text-neutral-500">
573-
This domain will be used for your programs referral links
611+
This domain will be used for your program's referral links
574612
</p>
575613

576614
<AnimatePresence>

0 commit comments

Comments
 (0)