Skip to content

Commit

Permalink
Merge branch 'main' into link-folders
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey authored Feb 4, 2025
2 parents 64885b3 + b0ab730 commit 05da63e
Show file tree
Hide file tree
Showing 12 changed files with 1,853 additions and 1,488 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import {
CarouselContent,
CarouselItem,
CarouselNavBar,
CarouselThumbnail,
CarouselThumbnails,
Logo,
MaxWidthWrapper,
Popover,
Tooltip,
TooltipContent,
useMediaQuery,
} from "@dub/ui";
import {
CircleWarning,
Expand All @@ -45,7 +48,7 @@ import {
} from "@dub/utils";
import { useAction } from "next-safe-action/hooks";
import Link from "next/link";
import { useState } from "react";
import { memo, useState } from "react";
import { toast } from "sonner";

const integrationSettings = {
Expand All @@ -60,6 +63,7 @@ export default function IntegrationPageClient({
integration: InstalledIntegrationInfoProps;
}) {
const { slug, id: workspaceId } = useWorkspace();
const { isMobile } = useMediaQuery();

const [openPopover, setOpenPopover] = useState(false);
const { execute, isPending } = useAction(getIntegrationInstallUrl, {
Expand Down Expand Up @@ -218,7 +222,7 @@ export default function IntegrationPageClient({
),
},
].map(({ label, content }) => (
<div className="flex flex-col gap-1">
<div key={label} className="flex flex-col gap-1">
<span className="text-xs uppercase text-neutral-500">
{label}
</span>
Expand Down Expand Up @@ -269,24 +273,58 @@ export default function IntegrationPageClient({

<div className="w-full rounded-lg border border-neutral-200 bg-white">
{integration.screenshots && integration.screenshots.length > 0 ? (
<Carousel
autoplay={{ delay: 5000 }}
className="rounded-t-lg bg-white p-4"
>
<CarouselContent>
{integration.screenshots.map((src, idx) => (
<CarouselItem key={idx}>
<BlurImage
src={src}
alt={`Screenshot of ${integration.name}`}
width={900}
height={580}
className="aspect-[900/580] w-[5/6] overflow-hidden rounded-md border border-neutral-200 object-cover object-top"
/>
</CarouselItem>
))}
</CarouselContent>
<CarouselNavBar variant="floating" />
<Carousel autoplay={{ delay: 5000 }}>
<div className="relative rounded-t-lg bg-white p-4">
<CarouselContent>
{integration.screenshots.map((src, idx) => (
<CarouselItem key={idx}>
<BlurImageMemo
src={src}
alt={`Screenshot ${idx + 1} of ${integration.name}`}
width={900}
height={580}
className="aspect-[900/580] w-[5/6] overflow-hidden rounded-md border border-neutral-200 object-cover object-top"
/>
</CarouselItem>
))}
</CarouselContent>
<CarouselNavBar
variant="simple"
className="absolute bottom-6 left-1/2 -translate-x-1/2"
/>
</div>
{!isMobile && (
<div className="relative">
<CarouselThumbnails className="py-0.5">
{integration.screenshots.map((src, idx) => (
<CarouselThumbnail
key={idx}
index={idx}
className={({ active }) =>
cn(
"aspect-[900/580] h-[100px] shrink-0 select-none overflow-hidden rounded-[6px] border",
"border-neutral-200 ring-2 ring-transparent transition-all duration-100",
active
? "border-neutral-300 ring-black/10"
: "hover:ring-black/5",
)
}
>
<BlurImageMemo
src={src}
alt={`Screenshot ${idx + 1} thumbnail`}
width={900}
height={580}
className="overflow-hidden rounded-[5px] object-cover object-top"
/>
</CarouselThumbnail>
))}
</CarouselThumbnails>

<div className="absolute inset-y-0 left-0 w-4 bg-gradient-to-r from-white" />
<div className="absolute inset-y-0 right-0 w-4 bg-gradient-to-l from-white" />
</div>
)}
</Carousel>
) : null}

Expand All @@ -297,3 +335,5 @@ export default function IntegrationPageClient({
</MaxWidthWrapper>
);
}

const BlurImageMemo = memo(BlurImage);
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { onboardPartnerAction } from "@/lib/actions/partners/onboard-partner";
import { onboardPartnerSchema } from "@/lib/zod/schemas/partners";
import { Partner } from "@dub/prisma/client";
import {
Button,
buttonVariants,
Expand All @@ -21,7 +22,11 @@ import ReactTextareaAutosize from "react-textarea-autosize";
import { toast } from "sonner";
import { z } from "zod";

export function OnboardingForm() {
export function OnboardingForm({
partner,
}: {
partner?: Pick<Partner, "bio" | "country" | "image"> | null;
}) {
const router = useRouter();
const { data: session } = useSession();
const { isMobile } = useMediaQuery();
Expand All @@ -34,7 +39,13 @@ export function OnboardingForm() {
setValue,
watch,
formState: { errors, isSubmitting, isSubmitSuccessful },
} = useForm<z.infer<typeof onboardPartnerSchema>>();
} = useForm<z.infer<typeof onboardPartnerSchema>>({
defaultValues: {
description: partner?.bio ?? undefined,
country: partner?.country ?? undefined,
image: partner?.image ?? undefined,
},
});

useEffect(() => {
if (session?.user) {
Expand Down
25 changes: 23 additions & 2 deletions apps/web/app/partners.dub.co/(onboarding)/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { getSession } from "@/lib/auth";
import { prisma } from "@dub/prisma";
import { ConnectedDots4 } from "@dub/ui/icons";
import { Suspense } from "react";
import { OnboardingForm } from "./onboarding-form";

export default function Onboarding() {
export default function PartnerOnboarding() {
return (
<div className="mx-auto my-10 flex w-full max-w-sm flex-col items-center md:mt-14">
<div className="animate-slide-up-fade flex size-10 items-center justify-center rounded-full border border-neutral-200 bg-white backdrop-blur-sm [--offset:8px] [animation-delay:250ms] [animation-duration:1s] [animation-fill-mode:both]">
Expand All @@ -11,8 +14,26 @@ export default function Onboarding() {
Create your Dub Partner profile
</h1>
<div className="animate-slide-up-fade mt-8 w-full [--offset:10px] [animation-delay:500ms] [animation-duration:1s] [animation-fill-mode:both]">
<OnboardingForm />
<Suspense fallback={<OnboardingForm />}>
<OnboardingFormRSC />
</Suspense>
</div>
</div>
);
}

async function OnboardingFormRSC() {
const { user } = await getSession();

const partner = await prisma.partner.findUnique({
where: {
email: user.email,
},
select: {
bio: true,
country: true,
image: true,
},
});
return <OnboardingForm partner={partner} />;
}
40 changes: 36 additions & 4 deletions apps/web/lib/dynadot/configure-dns.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { z } from "zod";
import { DubApiError } from "../api/errors";
import { DYNADOT_API_KEY, DYNADOT_BASE_URL } from "./constants";

const schema = z.object({
SetDnsResponse: z.object({
Status: z.string(),
Error: z.string().optional(),
}),
});

export const configureDNS = async ({ domain }: { domain: string }) => {
const searchParams = new URLSearchParams({
key: DYNADOT_API_KEY,
Expand All @@ -9,9 +18,32 @@ export const configureDNS = async ({ domain }: { domain: string }) => {
main_record0: "76.76.21.21",
});

return fetch(`${DYNADOT_BASE_URL}?${searchParams.toString()}`, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
const response = await fetch(
`${DYNADOT_BASE_URL}?${searchParams.toString()}`,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
},
}).then((res) => res.json());
);

if (!response.ok) {
throw new DubApiError({
code: "bad_request",
message: `Failed to configure DNS for domain "${domain}": ${response.statusText}`,
});
}

const data = schema.parse(await response.json());

const { Status, Error } = data.SetDnsResponse;

if (Status !== "success") {
throw new DubApiError({
code: "bad_request",
message: `Failed to configure DNS for domain "${domain}": ${Error}`,
});
}

return data;
};
4 changes: 2 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"react-dom-confetti": "^0.2.0",
"react-highlight-words": "^0.20.0",
"react-hook-form": "^7.52.1",
"react-markdown": "^9.0.1",
"react-markdown": "^9.0.3",
"react-parallax-tilt": "^1.7.70",
"react-pdf-tailwind": "^2.3.0",
"react-spring": "^9.5.5",
Expand All @@ -103,7 +103,7 @@
"rehype-autolink-headings": "^6.1.1",
"rehype-pretty-code": "^0.9.5",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"shiki": "^1.14.1",
"sonner": "^1.4.41",
"stripe": "^12.12.0",
Expand Down
63 changes: 63 additions & 0 deletions apps/web/scripts/delete-partners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { prisma } from "@dub/prisma";
import "dotenv-flow/config";
import { bulkDeleteLinks } from "../lib/api/links/bulk-delete-links";

async function main() {
const partners = await prisma.partner.findMany({
where: {
programs: {
some: {
programId: "prog_xxx",
},
},
},
include: {
programs: {
select: {
links: true,
},
},
},
take: 1,
});

for (const partner of partners) {
const programEnrollment = partner.programs[0];
const links = programEnrollment.links;

const deleteLinkCaches = await bulkDeleteLinks(links);
console.log("Deleted link caches", deleteLinkCaches);

const deleteLinks = await prisma.link.deleteMany({
where: {
id: {
in: links.map((link) => link.id),
},
},
});
console.log("Deleted links", deleteLinks);

const deleteSales = await prisma.sale.deleteMany({
where: {
partnerId: partner.id,
},
});
console.log("Deleted sales", deleteSales);

const deletePayouts = await prisma.payout.deleteMany({
where: {
partnerId: partner.id,
},
});
console.log("Deleted payouts", deletePayouts);

const deletePartner = await prisma.partner.delete({
where: {
id: partner.id,
},
});
console.log("Deleted partner", deletePartner);
}
}

main();
Loading

0 comments on commit 05da63e

Please sign in to comment.