Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple links per partner #1957

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
1e2142c
Support multiple links per partner
steven-tey Jan 30, 2025
7f1c601
Update link.prisma
steven-tey Jan 30, 2025
33b465e
Update route.ts
devkiran Jan 30, 2025
e6643aa
fix /api/partners/[partnerId]
devkiran Jan 30, 2025
300a1c6
Update route.ts
devkiran Jan 30, 2025
914f821
display the links on the partner sheet
devkiran Jan 30, 2025
a1dd975
Update partner-details-sheet.tsx
devkiran Jan 30, 2025
eb25ec7
add migration script
devkiran Jan 30, 2025
311f933
Update partner link tracking to support multiple links
devkiran Jan 30, 2025
72ef962
partners changes
devkiran Jan 30, 2025
446bce8
Update route.ts
devkiran Jan 30, 2025
f8996cc
Update analytics.ts
devkiran Jan 30, 2025
1d87cc9
Update partner program links to support multiple links
devkiran Jan 30, 2025
dcd29b3
filter by partnerId
devkiran Jan 30, 2025
d4cd3c9
Update route.ts
devkiran Jan 30, 2025
7b1badb
fix createSaleData
devkiran Jan 30, 2025
7f7effc
Update partner-details-sheet.tsx
devkiran Jan 30, 2025
29cfe21
Merge branch 'main' into multi-links
devkiran Jan 30, 2025
5fc5ed5
Update pnpm-lock.yaml
devkiran Jan 30, 2025
5fe0d49
Refactor partner query to use Prisma raw query and simplify partner d…
devkiran Jan 30, 2025
3395fc4
Update route.ts
devkiran Jan 30, 2025
5458f1c
fix the partners link lists
devkiran Jan 30, 2025
1d31e48
Update get-links-for-workspace.ts
devkiran Jan 30, 2025
80affb9
Update route.ts
devkiran Jan 30, 2025
7876a46
Update get-links-for-workspace.ts
devkiran Jan 30, 2025
4d538f3
Optimize leaderboard query and schema for partner performance data
devkiran Jan 30, 2025
8ee5d47
fix build
devkiran Jan 30, 2025
727d3dd
Update pnpm-lock.yaml
steven-tey Jan 30, 2025
6b1273c
Update record-link.ts
steven-tey Jan 30, 2025
6ae9336
include tenantId
steven-tey Jan 30, 2025
542cde7
update-process link
devkiran Jan 30, 2025
d0a4ffa
Update partner-details-sheet.tsx
steven-tey Jan 30, 2025
cead31a
Merge branch 'multi-links' of https://github.com/dubinc/dub into mult…
steven-tey Jan 30, 2025
70b5449
Merge branch 'main' into multi-links
steven-tey Jan 30, 2025
878f782
add support for filtering partners by tenantId
steven-tey Jan 30, 2025
cad368e
fix sort
devkiran Jan 30, 2025
fc0257d
Merge branch 'multi-links' of https://github.com/dubinc/dub into mult…
devkiran Jan 30, 2025
10a88ec
Update route.ts
devkiran Jan 30, 2025
910da0a
Update route.ts
devkiran Jan 30, 2025
3f91538
join to retrieve links that belong to the partner+program
steven-tey Jan 30, 2025
2c495a3
rearrange index
steven-tey Jan 30, 2025
695fdcb
fix types
steven-tey Jan 30, 2025
3b2be85
fix tests
steven-tey Jan 30, 2025
e39dfb2
fix tests again
steven-tey Jan 30, 2025
2cd394a
fix webhook sample events
steven-tey Jan 30, 2025
22caa90
make sure programId + partnerId + tenantId are all up to date
steven-tey Jan 30, 2025
b34b071
add support for embed data
steven-tey Jan 30, 2025
6c67d73
Update sale-created.json
steven-tey Jan 31, 2025
d46e802
referralLinkId → dubPartnerId
steven-tey Jan 31, 2025
cdcc29f
Update page-client.tsx
steven-tey Jan 31, 2025
0f87e41
allow for partnerId
steven-tey Jan 31, 2025
f78cd8c
only return partnerId
steven-tey Jan 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 0 additions & 50 deletions apps/web/app/api/analytics/client/route.ts

This file was deleted.

6 changes: 4 additions & 2 deletions apps/web/app/api/embed/analytics/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { withEmbedToken } from "@/lib/embed/auth";
import { NextResponse } from "next/server";

// GET /api/embed/analytics – get timeseries analytics for a link from an embed token
export const GET = withEmbedToken(async ({ link }) => {
export const GET = withEmbedToken(async ({ programId, partnerId }) => {
const analytics = await getAnalytics({
event: "composite",
groupBy: "timeseries",
linkId: link.id,
interval: "1y",
programId,
partnerId,
});
console.log("analytics", analytics);

return NextResponse.json(analytics);
});
73 changes: 39 additions & 34 deletions apps/web/app/api/embed/leaderboard/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,44 @@ import { NextResponse } from "next/server";
import z from "node_modules/zod/lib";

// GET /api/embed/sales – get sales for a link from an embed token
export const GET = withEmbedToken(async ({ program, searchParams }) => {
const programEnrollments = await prisma.programEnrollment.findMany({
where: {
programId: program.id,
partner: {
showOnLeaderboard: true,
},
},
orderBy: [
{
link: {
saleAmount: "desc",
},
},
{
link: {
leads: "desc",
},
},
{
link: {
clicks: "desc",
},
},
],
select: {
partner: true,
link: true,
},
take: 20,
});
export const GET = withEmbedToken(async ({ program }) => {
const partners = await prisma.$queryRaw`
SELECT
p.id,
p.name,
pe.status,
pe.programId,
pe.partnerId,
pe.createdAt as enrollmentCreatedAt,
COALESCE(SUM(l.clicks), 0) as totalClicks,
COALESCE(SUM(l.leads), 0) as totalLeads,
COALESCE(SUM(l.sales), 0) as totalSales,
COALESCE(SUM(l.saleAmount), 0) as totalSaleAmount
FROM
ProgramEnrollment pe
INNER JOIN
Partner p ON p.id = pe.partnerId AND p.showOnLeaderboard = true
LEFT JOIN
Link l ON l.partnerId = pe.partnerId
WHERE
pe.programId = ${program.id}
GROUP BY
p.id, pe.id
ORDER BY
totalSaleAmount DESC,
totalLeads DESC,
totalClicks DESC
LIMIT 20`;

return NextResponse.json(
z.array(LeaderboardPartnerSchema).parse(programEnrollments),
);
// @ts-ignore
const response = partners.map((partner) => ({
id: partner.id,
name: partner.name,
clicks: Number(partner.totalClicks),
leads: Number(partner.totalLeads),
sales: Number(partner.totalSales),
saleAmount: Number(partner.totalSaleAmount),
}));

return NextResponse.json(z.array(LeaderboardPartnerSchema).parse(response));
});
61 changes: 32 additions & 29 deletions apps/web/app/api/embed/sales/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,39 @@ import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";

// GET /api/embed/sales – get sales for a link from an embed token
export const GET = withEmbedToken(async ({ link, searchParams }) => {
const { page } = z
.object({ page: z.coerce.number().optional().default(1) })
.parse(searchParams);
export const GET = withEmbedToken(
async ({ programId, partnerId, searchParams }) => {
const { page } = z
.object({ page: z.coerce.number().optional().default(1) })
.parse(searchParams);

const sales = await prisma.sale.findMany({
where: {
linkId: link.id,
},
select: {
id: true,
amount: true,
earnings: true,
currency: true,
status: true,
createdAt: true,
updatedAt: true,
customer: {
select: {
email: true,
avatar: true,
const sales = await prisma.sale.findMany({
where: {
programId,
partnerId,
},
select: {
id: true,
amount: true,
earnings: true,
currency: true,
status: true,
createdAt: true,
updatedAt: true,
customer: {
select: {
email: true,
avatar: true,
},
},
},
},
take: SALES_PAGE_SIZE,
skip: (page - 1) * SALES_PAGE_SIZE,
orderBy: {
createdAt: "desc",
},
});
take: SALES_PAGE_SIZE,
skip: (page - 1) * SALES_PAGE_SIZE,
orderBy: {
createdAt: "desc",
},
});

return NextResponse.json(z.array(PartnerSaleResponseSchema).parse(sales));
});
return NextResponse.json(z.array(PartnerSaleResponseSchema).parse(sales));
},
);
4 changes: 2 additions & 2 deletions apps/web/app/api/embed/token/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { withEmbedToken } from "@/lib/embed/auth";
import { NextResponse } from "next/server";

// GET /api/embed/token - get the embed token for the given link
export const GET = withEmbedToken(async ({ linkToken }) => {
return NextResponse.json(linkToken);
export const GET = withEmbedToken(async ({ embedToken }) => {
return NextResponse.json(embedToken);
});
42 changes: 0 additions & 42 deletions apps/web/app/api/events/client/route.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { NextResponse } from "next/server";
// GET /api/partner-profile/programs/[programId]/analytics – get analytics for a program enrollment link
export const GET = withPartnerProfile(
async ({ partner, params, searchParams }) => {
const { link, program } = await getProgramEnrollmentOrThrow({
const { program } = await getProgramEnrollmentOrThrow({
partnerId: partner.id,
programId: params.programId,
});
Expand All @@ -24,7 +24,7 @@ export const GET = withPartnerProfile(

const response = await getAnalytics({
...parsedParams,
linkId: link.id,
partnerId: partner.id,
});

let data;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/partner-profile/programs/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const GET = withPartnerProfile(async ({ partner }) => {
z.array(ProgramEnrollmentSchema).parse(
programEnrollments.map((enrollment) => ({
...enrollment,
link: null, // hacky way of not having to fetch link
links: null, // hacky way of not having to fetch link
})),
),
);
Expand Down
20 changes: 13 additions & 7 deletions apps/web/app/api/partners/[partnerId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,28 @@ export const GET = withWorkspace(
},
include: {
partner: true,
link: true,
program: true,
links: true,
},
});

const { program, link } = programEnrollment;
const { program, links } = programEnrollment;

const earnings = links?.reduce((acc, link) => {
return (
acc +
((program.commissionType === "percentage"
? link?.saleAmount
: link?.sales) ?? 0) *
(program.commissionAmount / 100)
);
}, 0);

const partner = {
...programEnrollment.partner,
...programEnrollment,
id: programEnrollment.partnerId,
earnings:
((program.commissionType === "percentage"
? link?.saleAmount
: link?.sales) ?? 0) *
(program.commissionAmount / 100),
earnings,
};

return NextResponse.json(EnrolledPartnerSchema.parse(partner));
Expand Down
Loading