From 60862195f34fe7f2bdaeec8fb7fe3ae215f7e25f Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Thu, 4 Jun 2026 16:33:16 -0700 Subject: [PATCH 1/2] Show networkStatus badge in PartnerRowItem --- apps/web/app/(ee)/api/partners/route.ts | 16 ++--- .../application-referral-source-icon.tsx | 10 ++-- .../partners/applications/page-client.tsx | 12 ---- apps/web/lib/api/partners/get-partners.ts | 2 - apps/web/lib/zod/schemas/partners.ts | 6 -- .../partners/partner-application-source.tsx | 59 ------------------- apps/web/ui/partners/partner-info-cards.tsx | 24 +++----- .../partner-network-status-badge.tsx | 37 ++++++++++++ apps/web/ui/partners/partner-row-item.tsx | 34 +++++++---- .../web/ui/partners/trusted-partner-badge.tsx | 37 ++++++++---- 10 files changed, 103 insertions(+), 134 deletions(-) delete mode 100644 apps/web/ui/partners/partner-application-source.tsx create mode 100644 apps/web/ui/partners/partner-network/partner-network-status-badge.tsx diff --git a/apps/web/app/(ee)/api/partners/route.ts b/apps/web/app/(ee)/api/partners/route.ts index 99eafffdd1f..af6cc7c52c4 100644 --- a/apps/web/app/(ee)/api/partners/route.ts +++ b/apps/web/app/(ee)/api/partners/route.ts @@ -3,7 +3,6 @@ import { getPartners } from "@/lib/api/partners/get-partners"; import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw"; import { getProgramOrThrow } from "@/lib/api/programs/get-program-or-throw"; import { parseRequestBody } from "@/lib/api/utils"; -import { applicationEventSchema } from "@/lib/application-events/schema"; import { withWorkspace } from "@/lib/auth"; import { throwIfPartnersLimitExceeded } from "@/lib/partners/throw-if-partners-limit-exceeded"; import { polyfillSocialMediaFields } from "@/lib/social-utils"; @@ -102,16 +101,11 @@ export const GET = withWorkspace( saleAmount: z.number().default(0), }); - const responseSchema = - parsedParams.includeApplicationEvent || - parsedParams.includePartnerPlatforms - ? baseSchema.extend({ - platforms: z.array(partnerPlatformSchema), - applicationEvent: applicationEventSchema - .pick({ referralSource: true }) - .nullish(), - }) - : baseSchema; + const responseSchema = parsedParams.includePartnerPlatforms + ? baseSchema.extend({ + platforms: z.array(partnerPlatformSchema), + }) + : baseSchema; return NextResponse.json( z.array(responseSchema).parse( diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/applications/application-referral-source-icon.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/applications/application-referral-source-icon.tsx index e5e58a49b41..c8ea7831790 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/applications/application-referral-source-icon.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/analytics/applications/application-referral-source-icon.tsx @@ -3,17 +3,19 @@ import { Globe, Shop, UserArrowRight } from "@dub/ui/icons"; export function ApplicationReferralSourceIcon({ referralSource, + className, }: { referralSource: string; + className?: string; }) { switch (referralSource) { case "marketplace": - return ; + return ; case "direct": - return ; + return ; case "manual": - return ; + return ; default: - return ; + return ; } } diff --git a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx index 3259082c66d..88c1a25da3e 100644 --- a/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx +++ b/apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx @@ -103,7 +103,6 @@ export function ProgramPartnersApplicationsPageClient() { status: "pending", sortBy, sortOrder, - includeApplicationEvent: true, includePartnerPlatforms: true, }, { exclude: ["partnerId"] }, @@ -196,17 +195,6 @@ export function ProgramPartnersApplicationsPageClient() { header: "Applied", accessorFn: (d) => formatDate(d.createdAt, { month: "short" }), }, - // TODO: add source column back once we fix application source display - // { - // id: "source", - // header: "Source", - // minSize: 170, - // cell: ({ row }) => ( - // - // ), - // }, { id: "group", header: "Group", diff --git a/apps/web/lib/api/partners/get-partners.ts b/apps/web/lib/api/partners/get-partners.ts index c824f921612..2f9ae4d682f 100644 --- a/apps/web/lib/api/partners/get-partners.ts +++ b/apps/web/lib/api/partners/get-partners.ts @@ -19,7 +19,6 @@ export async function getPartners(filters: PartnerFilters) { sortBy, sortOrder, programId, - includeApplicationEvent, includePartnerPlatforms: _includePartnerPlatforms, includeGroup = false, ...enrollmentRest @@ -45,7 +44,6 @@ export async function getPartners(filters: PartnerFilters) { }, }, links: true, - ...(includeApplicationEvent ? { applicationEvent: true } : {}), ...(includeGroup ? { partnerGroup: { diff --git a/apps/web/lib/zod/schemas/partners.ts b/apps/web/lib/zod/schemas/partners.ts index 6faa6149947..45a0ce8a6de 100644 --- a/apps/web/lib/zod/schemas/partners.ts +++ b/apps/web/lib/zod/schemas/partners.ts @@ -212,7 +212,6 @@ export const getPartnersQuerySchemaExtended = getPartnersQuerySchema.extend({ country: z.union([z.string(), z.array(z.string())]).optional(), referredByPartnerId: z.string().optional(), includePartnerPlatforms: booleanQuerySchema.optional(), - includeApplicationEvent: booleanQuerySchema.optional(), // metric range query fields (TODO: Add to public API once we finalize the syntax) totalClicksMin: z.coerce .number() @@ -593,11 +592,6 @@ export const EnrolledPartnerSchemaExtended = EnrolledPartnerSchema.extend({ customerDataSharingEnabledAt: z.date().nullish(), groupMoveDisabledAt: z.date().nullish(), platforms: z.array(partnerPlatformSchema).nullable(), - applicationEvent: z - .object({ - referralSource: z.string(), - }) - .nullish(), discount: DiscountSchema.pick({ id: true, provider: true, diff --git a/apps/web/ui/partners/partner-application-source.tsx b/apps/web/ui/partners/partner-application-source.tsx deleted file mode 100644 index fa94546e081..00000000000 --- a/apps/web/ui/partners/partner-application-source.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { - getApplicationSourceLabelTooltip, - isMarketplaceReferralSource, -} from "@/lib/application-events/utils"; -import { BadgeCheck2Fill, Tooltip } from "@dub/ui"; -import { cn } from "@dub/utils"; - -export function PartnerApplicationSource({ - referralSource, - variant = "default", -}: { - referralSource: string | null | undefined; - variant?: "default" | "inline"; -}) { - const { label, tooltip } = getApplicationSourceLabelTooltip(referralSource); - - if (!label) { - return variant === "inline" ? null : "-"; - } - - const isMarketplace = isMarketplaceReferralSource(referralSource); - - const labelContent = ( - - {label} - - ); - - const labeledWithTooltip = tooltip ? ( - {labelContent} - ) : ( - labelContent - ); - - if (variant === "inline") { - return ( - - {isMarketplace && ( - - )} - {labeledWithTooltip} - - ); - } - - return ( -
- {isMarketplace && ( - - )} - {labeledWithTooltip} -
- ); -} diff --git a/apps/web/ui/partners/partner-info-cards.tsx b/apps/web/ui/partners/partner-info-cards.tsx index ce4181a8345..0746934c784 100644 --- a/apps/web/ui/partners/partner-info-cards.tsx +++ b/apps/web/ui/partners/partner-info-cards.tsx @@ -46,6 +46,7 @@ import { } from "./fraud-risks/partner-fraud-banner"; import { PartnerFraudIndicator } from "./fraud-risks/partner-fraud-indicator"; import { PartnerAvatar } from "./partner-avatar"; +import { PartnerNetworkStatusBadge } from "./partner-network/partner-network-status-badge"; import { PartnerInfoGroup } from "./partner-info-group"; import { PartnerStarButton } from "./partner-star-button"; import { PartnerStatusBadgeWithTooltip } from "./partner-status-badge-with-tooltip"; @@ -154,8 +155,6 @@ export function PartnerInfoCards({ if ((isEnrolled || isAdmin) && partner) { const isPendingApplication = "status" in partner && partner.status === "pending"; - const applicationReferralSource = - isEnrolled && partner.applicationEvent?.referralSource; basicFields = basicFields.concat([ { @@ -175,24 +174,11 @@ export function PartnerInfoCards({ > Applied {formatDate(partner.createdAt)} - {/* TODO: add source column back once we fix application source display */} - {/* {applicationReferralSource && ( - <> - via - - - )} */} ) : ( `${isPendingApplication ? "Applied" : "Partner since"} ${formatDate(partner.createdAt)}` ), - timestamp: - isPendingApplication && applicationReferralSource - ? undefined - : partner.createdAt, + timestamp: isPendingApplication ? undefined : partner.createdAt, }, { id: "payoutMethod" as const, @@ -306,6 +292,12 @@ export function PartnerInfoCards({ {partner.name} + {"networkStatus" in partner && partner.networkStatus && ( + + )} + {showFraudIndicator && ( )} diff --git a/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx b/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx new file mode 100644 index 00000000000..7c67423922d --- /dev/null +++ b/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx @@ -0,0 +1,37 @@ +import { PartnerNetworkStatus } from "@dub/prisma/client"; +import { BadgeCheck2Fill, Tooltip } from "@dub/ui"; +import { TrustedPartnerBadge } from "../trusted-partner-badge"; + +export function PartnerNetworkStatusBadge({ + networkStatus, +}: { + networkStatus: PartnerNetworkStatus; +}) { + if (networkStatus === "trusted") { + return ; + } + + if (networkStatus === "approved") { + return ( + + +
+ + Approved Partner + + + This partner is approved in the Dub Partner Network. + +
+ + } + > + +
+ ); + } + + return null; +} diff --git a/apps/web/ui/partners/partner-row-item.tsx b/apps/web/ui/partners/partner-row-item.tsx index 76eddf08cc7..fed163f6a82 100644 --- a/apps/web/ui/partners/partner-row-item.tsx +++ b/apps/web/ui/partners/partner-row-item.tsx @@ -1,5 +1,5 @@ import useProgram from "@/lib/swr/use-program"; -import { PartnerPayoutMethod } from "@dub/prisma/client"; +import { PartnerNetworkStatus, PartnerPayoutMethod } from "@dub/prisma/client"; import { CircleArrowRight, DynamicTooltipWrapper, GreekTemple } from "@dub/ui"; import { cn, formatDateTimeSmart } from "@dub/utils"; import { CircleMinus } from "lucide-react"; @@ -7,6 +7,7 @@ import Link from "next/link"; import { useParams } from "next/navigation"; import type { ReactNode } from "react"; import { PartnerFraudIndicator } from "./fraud-risks/partner-fraud-indicator"; +import { PartnerNetworkStatusBadge } from "./partner-network/partner-network-status-badge"; import { PartnerAvatar } from "./partner-avatar"; import { getPayoutMethodIconConfig, @@ -21,6 +22,7 @@ interface PartnerRowItemProps { id: string; name: string; image?: string | null; + networkStatus?: PartnerNetworkStatus | null; defaultPayoutMethod?: PartnerPayoutMethod | null; payoutsEnabledAt?: Date | null; }; @@ -208,19 +210,25 @@ export function PartnerRowItem({ - e.stopPropagation() : undefined} - onAuxClick={showPermalink ? (e) => e.stopPropagation() : undefined} - className={cn( - "min-w-0 truncate", - showPermalink && "cursor-alias decoration-dotted hover:underline", +
+ e.stopPropagation() : undefined} + onAuxClick={showPermalink ? (e) => e.stopPropagation() : undefined} + className={cn( + "min-w-0 truncate", + showPermalink && "cursor-alias decoration-dotted hover:underline", + )} + title={partner.name} + > + {partner.name} + + + {"networkStatus" in partner && partner.networkStatus && ( + )} - title={partner.name} - > - {partner.name} - +
{suffix} diff --git a/apps/web/ui/partners/trusted-partner-badge.tsx b/apps/web/ui/partners/trusted-partner-badge.tsx index 7fc3656180c..a113b8507dd 100644 --- a/apps/web/ui/partners/trusted-partner-badge.tsx +++ b/apps/web/ui/partners/trusted-partner-badge.tsx @@ -1,14 +1,29 @@ import { Tooltip } from "@dub/ui"; +import { cn } from "@dub/utils"; + +const TRUSTED_BADGE_SRC = "https://assets.dub.co/icons/trusted-badge.svg"; + +export function TrustedPartnerBadge({ + variant = "overlay", +}: { + variant?: "overlay" | "inline"; +}) { + const badge = ( + Trusted partner badge + ); -export function TrustedPartnerBadge() { return ( +
Trusted partner badge
@@ -22,13 +37,13 @@ export function TrustedPartnerBadge() {
} > -
- Trusted partner badge -
+ {variant === "inline" ? ( + {badge} + ) : ( +
+ {badge} +
+ )} ); } From d008178a318b5058776cdfbb181bb7b5f87e1e00 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Thu, 4 Jun 2026 16:44:20 -0700 Subject: [PATCH 2/2] finalize badge variants/sizes --- apps/web/ui/partners/partner-info-cards.tsx | 7 ++----- .../partner-network/partner-network-status-badge.tsx | 12 ++++++++++-- apps/web/ui/partners/trusted-partner-badge.tsx | 4 +++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/web/ui/partners/partner-info-cards.tsx b/apps/web/ui/partners/partner-info-cards.tsx index 0746934c784..bf347f2f15d 100644 --- a/apps/web/ui/partners/partner-info-cards.tsx +++ b/apps/web/ui/partners/partner-info-cards.tsx @@ -46,8 +46,8 @@ import { } from "./fraud-risks/partner-fraud-banner"; import { PartnerFraudIndicator } from "./fraud-risks/partner-fraud-indicator"; import { PartnerAvatar } from "./partner-avatar"; -import { PartnerNetworkStatusBadge } from "./partner-network/partner-network-status-badge"; import { PartnerInfoGroup } from "./partner-info-group"; +import { PartnerNetworkStatusBadge } from "./partner-network/partner-network-status-badge"; import { PartnerStarButton } from "./partner-star-button"; import { PartnerStatusBadgeWithTooltip } from "./partner-status-badge-with-tooltip"; import { PartnerTagsList } from "./partner-tags-list"; @@ -56,7 +56,6 @@ import { getPayoutMethodLabel, } from "./payouts/payout-method-config"; import { ProgramRewardList } from "./program-reward-list"; -import { TrustedPartnerBadge } from "./trusted-partner-badge"; import { UpdatePartnerTagsModal, useUpdatePartnerTagsModal, @@ -265,9 +264,6 @@ export function PartnerInfoCards({ ) : (
)} - {partner?.networkStatus === "trusted" && ( - - )}
@@ -295,6 +291,7 @@ export function PartnerInfoCards({ {"networkStatus" in partner && partner.networkStatus && ( )} diff --git a/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx b/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx index 7c67423922d..b1e7904dfd1 100644 --- a/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx +++ b/apps/web/ui/partners/partner-network/partner-network-status-badge.tsx @@ -1,14 +1,17 @@ import { PartnerNetworkStatus } from "@dub/prisma/client"; import { BadgeCheck2Fill, Tooltip } from "@dub/ui"; +import { cn } from "@dub/utils"; import { TrustedPartnerBadge } from "../trusted-partner-badge"; export function PartnerNetworkStatusBadge({ networkStatus, + size = "small", }: { networkStatus: PartnerNetworkStatus; + size?: "small" | "large"; }) { if (networkStatus === "trusted") { - return ; + return ; } if (networkStatus === "approved") { @@ -28,7 +31,12 @@ export function PartnerNetworkStatusBadge({
} > - + ); } diff --git a/apps/web/ui/partners/trusted-partner-badge.tsx b/apps/web/ui/partners/trusted-partner-badge.tsx index a113b8507dd..d9008043b89 100644 --- a/apps/web/ui/partners/trusted-partner-badge.tsx +++ b/apps/web/ui/partners/trusted-partner-badge.tsx @@ -5,14 +5,16 @@ const TRUSTED_BADGE_SRC = "https://assets.dub.co/icons/trusted-badge.svg"; export function TrustedPartnerBadge({ variant = "overlay", + size = "small", }: { variant?: "overlay" | "inline"; + size?: "small" | "large"; }) { const badge = ( Trusted partner badge );