Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f8352f8
add Events to nav bar
devkiran Apr 25, 2025
187dd9c
Add API endpoint to fetch events for program enrollment
devkiran Apr 25, 2025
0c44fe1
Update route.ts
devkiran Apr 25, 2025
f9787ac
Merge branch 'main' into partner-events
devkiran Apr 28, 2025
33ff5e3
re-arrange schemas
devkiran Apr 28, 2025
551d5bd
fix the events table
devkiran Apr 28, 2025
0147c70
Update route.ts
devkiran Apr 28, 2025
f21f36f
Merge branch 'main' into partner-events
steven-tey Apr 28, 2025
0a3ca08
Update partners-sidebar-nav.tsx
steven-tey Apr 28, 2025
0d67f3d
add back "switch to" buttons on partnerPage
steven-tey Apr 28, 2025
b8ff151
add CustomerFilterItem and hideInFilterDropdown
steven-tey Apr 28, 2025
6fa3895
add optionPermalink
steven-tey Apr 28, 2025
f83f33f
Update toggle.tsx
steven-tey Apr 28, 2025
7a115da
timestamp in first column, enable hiding eventname/click
steven-tey Apr 28, 2025
ca2fd99
WIP product nav updates
TWilson023 Apr 28, 2025
138adab
rearrange events columns
steven-tey Apr 28, 2025
59d4b87
More product nav updates
TWilson023 Apr 28, 2025
17956e9
Add missing keys
TWilson023 Apr 28, 2025
d3c5235
Update analytics-graphic.tsx
TWilson023 Apr 28, 2025
412325f
Update analytics-graphic.tsx
TWilson023 Apr 28, 2025
0490818
final changes
steven-tey Apr 28, 2025
88eca1c
bump @dub/ui
steven-tey Apr 28, 2025
30653a3
Merge pull request #2359 from dubinc/partner-events
steven-tey Apr 28, 2025
3c1737c
Merge branch 'main' into product-nav-updates
steven-tey Apr 28, 2025
fca0620
Merge pull request #2364 from dubinc/product-nav-updates
steven-tey Apr 28, 2025
fcad899
rearrange nav
steven-tey Apr 28, 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAnalytics } from "@/lib/analytics/get-analytics";
import { getProgramEnrollmentOrThrow } from "@/lib/api/programs/get-program-enrollment-or-throw";
import { withPartnerProfile } from "@/lib/auth/partner";
import { analyticsQuerySchema } from "@/lib/zod/schemas/analytics";
import { partnerProfileAnalyticsQuerySchema } from "@/lib/zod/schemas/partner-profile";
import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";

Expand All @@ -13,15 +13,8 @@ export const GET = withPartnerProfile(
programId: params.programId,
});

const parsedParams = analyticsQuerySchema
.omit({
workspaceId: true,
externalId: true,
tenantId: true,
})
.parse(searchParams);

let { linkId, domain, key, ...rest } = parsedParams;
let { linkId, domain, key, ...rest } =
partnerProfileAnalyticsQuerySchema.parse(searchParams);

if (!linkId && domain && key) {
const link = await prisma.link.findUnique({
Expand All @@ -41,10 +34,10 @@ export const GET = withPartnerProfile(
}

const response = await getAnalytics({
...rest,
linkId,
programId: program.id,
partnerId: partner.id,
linkId,
...rest,
dataAvailableFrom: program.createdAt,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { getEvents } from "@/lib/analytics/get-events";
import { getProgramEnrollmentOrThrow } from "@/lib/api/programs/get-program-enrollment-or-throw";
import { withPartnerProfile } from "@/lib/auth/partner";
import {
PartnerProfileLinkSchema,
partnerProfileEventsQuerySchema,
} from "@/lib/zod/schemas/partner-profile";

import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";
import { z } from "zod";

const CustomerSchema = z.object({
id: z.string(),
email: z
.string()
.transform((email) => email.replace(/(?<=^.).+(?=.@)/, "****")),
});

// GET /api/partner-profile/programs/[programId]/events – get events for a program enrollment link
export const GET = withPartnerProfile(
async ({ partner, params, searchParams }) => {
const { program } = await getProgramEnrollmentOrThrow({
partnerId: partner.id,
programId: params.programId,
});

let { linkId, domain, key, ...rest } =
partnerProfileEventsQuerySchema.parse(searchParams);

if (!linkId && domain && key) {
const link = await prisma.link.findUnique({
where: {
domain_key: {
domain,
key,
},
},
});

if (!link || link.partnerId !== partner.id) {
return NextResponse.json({ error: "Link not found" }, { status: 404 });
}

linkId = link.id;
}

const events = await getEvents({
...rest,
linkId,
programId: program.id,
partnerId: partner.id,
dataAvailableFrom: program.createdAt,
});

const response = events.map((event) => {
return {
...event,
link: event?.link ? PartnerProfileLinkSchema.parse(event.link) : null,
// @ts-expect-error - customer is not always present
...(event?.customer && {
customer: CustomerSchema
// @ts-expect-error - customer is not always present
.parse(event.customer),
}),
};
});

return NextResponse.json(response);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { CustomerDetailsColumn } from "@/ui/customers/customer-details-column";
import { CustomerPartnerEarningsTable } from "@/ui/customers/customer-partner-earnings-table";
import { CustomerSalesTable } from "@/ui/customers/customer-sales-table";
import { BackLink } from "@/ui/shared/back-link";
import { ArrowUpRight, CopyButton } from "@dub/ui";
import { ArrowUpRight, Button, CopyButton } from "@dub/ui";
import { OG_AVATAR_URL, fetcher } from "@dub/utils";
import Link from "next/link";
import { notFound, useParams } from "next/navigation";
Expand Down Expand Up @@ -135,9 +135,20 @@ export function CustomerPageClient() {
)}

<section className="flex flex-col">
<h2 className="py-3 text-lg font-semibold text-neutral-900">
Activity
</h2>
<div className="flex items-center justify-between">
<h2 className="py-3 text-lg font-semibold text-neutral-900">
Activity
</h2>
<Link
href={`/${slug}/events?interval=all&customerId=${customerId}`}
>
<Button
variant="secondary"
text="View all"
className="h-7 px-2"
/>
</Link>
</div>
<CustomerActivityList
activity={customerActivity}
isLoading={!customer || isCustomerActivityLoading}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
"use client";

import { CUSTOMER_PAGE_EVENTS_LIMIT } from "@/lib/partners/constants";
import usePartnerCustomer from "@/lib/swr/use-partner-customer";
import useProgramEnrollment from "@/lib/swr/use-program-enrollment";
import {
PartnerEarningsResponse,
PartnerProfileCustomerProps,
} from "@/lib/types";
import { PartnerEarningsResponse } from "@/lib/types";
import { CustomerActivityList } from "@/ui/customers/customer-activity-list";
import { CustomerDetailsColumn } from "@/ui/customers/customer-details-column";
import { CustomerSalesTable } from "@/ui/customers/customer-sales-table";
import { ProgramRewardList } from "@/ui/partners/program-reward-list";
import { BackLink } from "@/ui/shared/back-link";
import { MoneyBill2, Tooltip } from "@dub/ui";
import { Button, MoneyBill2, Tooltip } from "@dub/ui";
import { fetcher, OG_AVATAR_URL } from "@dub/utils";
import Link from "next/link";
import { notFound, useParams } from "next/navigation";
import { memo } from "react";
import useSWR from "swr";
Expand All @@ -24,10 +23,9 @@ export function ProgramCustomerPageClient() {
customerId: string;
}>();

const { data: customer, isLoading } = useSWR<PartnerProfileCustomerProps>(
`/api/partner-profile/programs/${programSlug}/customers/${customerId}`,
fetcher,
);
const { data: customer, isLoading } = usePartnerCustomer({
customerId,
});

if (!customer && !isLoading) notFound();

Expand Down Expand Up @@ -86,9 +84,20 @@ export function ProgramCustomerPageClient() {
</section>

<section className="flex flex-col">
<h2 className="py-3 text-lg font-semibold text-neutral-900">
Activity
</h2>
<div className="flex items-center justify-between">
<h2 className="py-3 text-lg font-semibold text-neutral-900">
Activity
</h2>
<Link
href={`/programs/${programSlug}/events?interval=all&customerId=${customerId}`}
>
<Button
variant="secondary"
text="View all"
className="h-7 px-2"
/>
</Link>
</div>
<CustomerActivityList
activity={customer?.activity}
isLoading={!customer}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Events from "@/ui/analytics/events";
import { PageContent } from "@/ui/layout/page-content";

export default function ProgramEvents() {
return (
<PageContent title="Events" hideReferButton>
<Events />
</PageContent>
);
}
2 changes: 1 addition & 1 deletion apps/web/lib/swr/use-customer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function useCustomer<
const { id: workspaceId } = useWorkspace();

const { data, error, isLoading } = useSWR<T>(
customerId && workspaceId
workspaceId && customerId
? `/api/customers/${customerId}?${new URLSearchParams({
workspaceId: workspaceId,
...query,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/lib/swr/use-customers-count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function useCustomersCount() {
const { id: workspaceId } = useWorkspace();

const { data, error } = useSWR<number>(
`/api/customers/count?workspaceId=${workspaceId}`,
workspaceId && `/api/customers/count?workspaceId=${workspaceId}`,
fetcher,
);

Expand Down
23 changes: 23 additions & 0 deletions apps/web/lib/swr/use-partner-customer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fetcher } from "@dub/utils";
import { useParams } from "next/navigation";
import useSWR from "swr";
import { PartnerProfileCustomerProps } from "../types";

export default function usePartnerCustomer({
customerId,
}: {
customerId: string;
}) {
const { programSlug } = useParams<{ programSlug: string }>();

const { data, isLoading } = useSWR<PartnerProfileCustomerProps>(
programSlug &&
`/api/partner-profile/programs/${programSlug}/customers/${customerId}`,
fetcher,
);

return {
data,
isLoading,
};
}
23 changes: 23 additions & 0 deletions apps/web/lib/zod/schemas/partner-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
DUB_PARTNERS_ANALYTICS_INTERVAL,
} from "@/lib/analytics/constants";
import { z } from "zod";
import { analyticsQuerySchema, eventsQuerySchema } from "./analytics";
import {
CommissionSchema,
getCommissionsCountQuerySchema,
Expand Down Expand Up @@ -93,3 +94,25 @@ export const PartnerProfileCustomerSchema = CustomerEnrichedSchema.pick({
.transform((email) => email.replace(/(?<=^.).+(?=.@)/, "****")),
activity: customerActivityResponseSchema,
});

export const partnerProfileAnalyticsQuerySchema = analyticsQuerySchema.omit({
workspaceId: true,
externalId: true,
tenantId: true,
programId: true,
partnerId: true,
tagId: true,
tagIds: true,
folderId: true,
});

export const partnerProfileEventsQuerySchema = eventsQuerySchema.omit({
workspaceId: true,
externalId: true,
tenantId: true,
programId: true,
partnerId: true,
tagId: true,
tagIds: true,
folderId: true,
});
2 changes: 1 addition & 1 deletion apps/web/ui/analytics/analytics-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function AnalyticsProvider({
}>) {
const searchParams = useSearchParams();
const pathname = usePathname();
const { id: workspaceId, slug, domains } = useWorkspace();
const { id: workspaceId, slug } = useWorkspace();

const [requiresUpgrade, setRequiresUpgrade] = useState(false);

Expand Down
Loading