Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e2e9fa6
WIP enrolled partner page
TWilson023 Sep 9, 2025
7cc03cc
Commission creation
TWilson023 Sep 9, 2025
ebe5b8a
Update links
TWilson023 Sep 9, 2025
845c0d3
Update layout.tsx
TWilson023 Sep 9, 2025
427d75f
Partner stats + layout improvements
TWilson023 Sep 9, 2025
d61b32a
Stats improvements
TWilson023 Sep 9, 2025
f7f17ae
Update app-sidebar-nav.tsx
TWilson023 Sep 9, 2025
f4414a0
Merge branch 'main' into enrolled-partner-page
TWilson023 Sep 10, 2025
b6c589b
Update partner-stats.tsx
TWilson023 Sep 10, 2025
197e701
Advanced settings modal
TWilson023 Sep 10, 2025
0b8a7dd
WIP individual partner page
TWilson023 Sep 10, 2025
09b7913
WIP partner info cards
TWilson023 Sep 11, 2025
1263e5b
Partner info
TWilson023 Sep 11, 2025
7e73283
Add links table
TWilson023 Sep 11, 2025
7a5ea44
Merge branch 'main' into enrolled-partner-page
TWilson023 Sep 12, 2025
173ed0f
Add payouts table
TWilson023 Sep 12, 2025
5f538a0
Add about section
TWilson023 Sep 12, 2025
790a1fc
Update partners-table.tsx
TWilson023 Sep 12, 2025
af6e224
Update partner-nav.tsx
TWilson023 Sep 12, 2025
8fe1282
WIP partner comments
TWilson023 Sep 12, 2025
26508a2
Tweaks from AI review
TWilson023 Sep 15, 2025
1fba211
Merge branch 'main' into enrolled-partner-page
TWilson023 Sep 15, 2025
478a157
Update partner-info.tsx
TWilson023 Sep 15, 2025
08e411c
Merge branch 'main' into enrolled-partner-page
TWilson023 Sep 15, 2025
440162d
Add comments
TWilson023 Sep 15, 2025
ba242c8
DB/schema updates
TWilson023 Sep 15, 2025
86fc211
Misc. tweaks
TWilson023 Sep 15, 2025
e9c6eb3
Add eligible bounties
TWilson023 Sep 15, 2025
63ec7b9
Merge branch 'main' into enrolled-partner-page
TWilson023 Sep 15, 2025
dee186b
Update partner-info.tsx
TWilson023 Sep 15, 2025
bd91664
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 15, 2025
05d9fbb
ProgramPartnerComment → Comment
steven-tey Sep 16, 2025
78fe9c5
Merge branch 'enrolled-partner-page' of github.com:dubinc/dub into en…
TWilson023 Sep 16, 2025
2957a1d
Comment → PartnerComment
steven-tey Sep 16, 2025
ff5ce0d
Merge branch 'enrolled-partner-page' of https://github.com/dubinc/dub…
steven-tey Sep 16, 2025
1d85e83
Add message link/button
TWilson023 Sep 16, 2025
4cc6697
fix links and button sizes
steven-tey Sep 16, 2025
64b76a1
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
b29150e
Update update-partner-enrollment.ts
devkiran Sep 16, 2025
1adec52
Use real comments count
TWilson023 Sep 16, 2025
865bc9b
Use middleware for redirect
TWilson023 Sep 16, 2025
05144f8
Update partner-enrolled.json
TWilson023 Sep 16, 2025
fcb7706
Update layout.tsx
TWilson023 Sep 16, 2025
76752de
Update layout.tsx
TWilson023 Sep 16, 2025
438a3bc
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
e5612b6
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
d5a4c5d
fix broken link
steven-tey Sep 16, 2025
74d34fe
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
020166b
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
02cd6a3
recordLink when partner tenantId updates
steven-tey Sep 16, 2025
4e5205e
Merge branch 'main' into enrolled-partner-page
steven-tey Sep 16, 2025
041fa3e
Renaming
TWilson023 Sep 16, 2025
eb98353
Merge branch 'enrolled-partner-page' of github.com:dubinc/dub into en…
TWilson023 Sep 16, 2025
259d5b5
Update comment schema/action
TWilson023 Sep 16, 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
34 changes: 33 additions & 1 deletion apps/web/app/(ee)/api/bounties/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createId } from "@/lib/api/create-id";
import { DubApiError } from "@/lib/api/errors";
import { throwIfInvalidGroupIds } from "@/lib/api/groups/throw-if-invalid-group-ids";
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { getProgramEnrollmentOrThrow } from "@/lib/api/programs/get-program-enrollment-or-throw";
import { parseRequestBody } from "@/lib/api/utils";
import { withWorkspace } from "@/lib/auth";
import { qstash } from "@/lib/cron";
Expand All @@ -13,6 +14,7 @@ import {
BountyListSchema,
BountySchema,
createBountySchema,
getBountiesQuerySchema,
} from "@/lib/zod/schemas/bounties";
import {
WORKFLOW_ACTION_TYPES,
Expand All @@ -26,12 +28,42 @@ import { NextResponse } from "next/server";

// GET /api/bounties - get all bounties for a program
export const GET = withWorkspace(
async ({ workspace }) => {
async ({ workspace, searchParams }) => {
const programId = getDefaultProgramIdOrThrow(workspace);

const { partnerId } = getBountiesQuerySchema.parse(searchParams);

const programEnrollment = partnerId
? await getProgramEnrollmentOrThrow({
partnerId,
programId,
includeProgram: true,
})
: null;

const bounties = await prisma.bounty.findMany({
where: {
programId,

// Filter only bounties the specified partner is eligible for
...(programEnrollment && {
OR: [
{
groups: {
none: {},
},
},
{
groups: {
some: {
groupId:
programEnrollment.groupId ||
programEnrollment.program.defaultGroupId,
},
},
},
],
}),
},
include: {
groups: {
Expand Down
31 changes: 31 additions & 0 deletions apps/web/app/(ee)/api/partners/[partnerId]/comments/count/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { withWorkspace } from "@/lib/auth";
import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";

// GET /api/partners/:id/comments/count – Get partner comments count
export const GET = withWorkspace(
async ({ workspace, params }) => {
const { partnerId } = params;
const programId = getDefaultProgramIdOrThrow(workspace);

const count = await prisma.partnerComment.count({
where: {
programId,
partnerId,
},
});

return NextResponse.json(count);
},
{
requiredPlan: [
"business",
"business plus",
"business extra",
"business max",
"advanced",
"enterprise",
],
},
);
39 changes: 39 additions & 0 deletions apps/web/app/(ee)/api/partners/[partnerId]/comments/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { withWorkspace } from "@/lib/auth";
import { PartnerCommentSchema } from "@/lib/zod/schemas/programs";
import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";
import { z } from "zod";

// GET /api/partners/:id/comments – Get partner comments
export const GET = withWorkspace(
async ({ workspace, params }) => {
const { partnerId } = params;
const programId = getDefaultProgramIdOrThrow(workspace);

const comments = await prisma.partnerComment.findMany({
where: {
programId,
partnerId,
},
include: {
user: true,
},
orderBy: {
createdAt: "desc",
},
});

return NextResponse.json(z.array(PartnerCommentSchema).parse(comments));
},
{
requiredPlan: [
"business",
"business plus",
"business extra",
"business max",
"advanced",
"enterprise",
],
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function CustomerPageClient() {
</h2>
<div className="flex flex-col gap-4">
<Link
href={`/${slug}/program/partners?partnerId=${customer.partner.id}`}
href={`/${slug}/program/partners/${customer.partner.id}`}
target="_blank"
className="border-border-subtle group flex items-center justify-between overflow-hidden rounded-lg border bg-neutral-100 px-4 py-3"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function BountySubmissionDetailsSheetContent({
<div className="border-b border-neutral-200 bg-neutral-50 p-6">
<PartnerInfoSection partner={partner} showPartnerStatus={false}>
<ButtonLink
href={`/${workspaceSlug}/program/partners?partnerId=${partner.id}`}
href={`/${workspaceSlug}/program/partners/${partner.id}`}
variant="secondary"
className="h-8 w-fit px-3 py-2 text-sm font-medium"
target="_blank"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Button, useMediaQuery } from "@dub/ui";
import { ChevronLeft, LoadingSpinner } from "@dub/ui/icons";
import { OG_AVATAR_URL, cn } from "@dub/utils";
import { useAction } from "next-safe-action/hooks";
import Link from "next/link";
import { redirect, useParams } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
Expand Down Expand Up @@ -215,17 +216,13 @@ export function ProgramMessagesPartnerPageClient() {
Profile
</h2>
<div className="flex items-center gap-2">
<Button
variant="secondary"
text="View profile"
className="h-8 rounded-lg px-3"
onClick={() =>
window.open(
`/${workspaceSlug}/program/partners?partnerId=${partnerId}`,
"_blank",
)
}
/>
<Link href={`/program/partners/${partnerId}`} target="_blank">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<Link href={`/program/partners/${partnerId}`} target="_blank">
<Link href={`/${workspaceSlug}/program/partners/${partnerId}`} target="_blank">

The "View profile" link is missing the workspace slug, which will result in a broken navigation link.

View Details

Analysis

Missing workspace slug in partner profile navigation link

What fails: Link component in ProgramMessagesPartnerPageClient (line 219) navigates to /program/partners/${partnerId} instead of /${workspaceSlug}/program/partners/${partnerId}, causing broken navigation

How to reproduce:

  1. Navigate to program messages for any partner
  2. Click "View profile" button in the right panel
  3. Link attempts to navigate to /program/partners/[partnerId] route which doesn't exist

Result: Navigation fails because Next.js App Router requires all dynamic segments including [slug] to be present in navigation hrefs

Expected: Should navigate to /${workspaceSlug}/program/partners/${partnerId} as confirmed by the route structure at apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/ and similar patterns in partner-nav.tsx

<Button
variant="secondary"
text="View profile"
className="h-8 rounded-lg px-3"
/>
</Link>
Comment on lines +219 to +225
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix route (missing slug) and avoid nested interactive elements.

  • href lacks the workspace slug → likely 404.
  • nests interactive elements; use “asChild” or style an anchor.

Preferred (if @dub/ui Button supports asChild):

- <Link href={`/program/partners/${partnerId}`} target="_blank">
-   <Button
-     variant="secondary"
-     text="View profile"
-     className="h-8 rounded-lg px-3"
-   />
- </Link>
+ <Button asChild variant="secondary" className="h-8 rounded-lg px-3">
+   <Link
+     href={`/${workspaceSlug}/program/partners/${partnerId}`}
+     target="_blank"
+     rel="noopener noreferrer"
+   >
+     View profile
+   </Link>
+ </Button>

Fallback (if asChild unsupported): render an with the same classes instead of Button.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/messages/[partnerId]/page-client.tsx
around lines 218-224, the Link href is missing the workspace slug and the Button
is nested inside Link (creating nested interactive elements); update the href to
include the current slug (e.g. `/program/${slug}/partners/${partnerId}`) and
remove the nested interactive by either using the Button's asChild prop so the
Button renders the anchor (Button asChild><Link .../>) or, if asChild is not
supported, render a plain <a> (or Link styled as an anchor) with the Button's
classes/appearance instead of nesting a Button inside Link.

<button
type="button"
onClick={() => setIsRightPanelOpen(false)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import usePartner from "@/lib/swr/use-partner";
import { OnlinePresenceSummary } from "@/ui/partners/online-presence-summary";
import { useParams } from "next/navigation";

export function ProgramPartnerAboutPageClient() {
const { partnerId } = useParams() as { partnerId: string };
const { partner, error } = usePartner({ partnerId });

return partner ? (
<>
<div className="flex flex-col gap-2">
<h3 className="text-content-emphasis text-xs font-semibold">
Description
</h3>
<p className="text-content-default text-xs">
{partner.description || (
<span className="italic text-neutral-400">
No description provided
</span>
)}
</p>
</div>

<div className="flex flex-col gap-2">
<h3 className="text-content-emphasis text-xs font-semibold">
Website and socials
</h3>
<OnlinePresenceSummary
partner={partner}
showLabels={false}
className="gap-y-2"
/>
</div>
</>
) : error ? (
<div className="flex justify-center py-16">
<span className="text-content-subtle text-sm">
Failed to load partner links
</span>
</div>
) : (
[...Array(2)].map((_, index) => (
<div key={index} className="flex flex-col gap-2">
<div className="h-4 w-20 animate-pulse rounded-md bg-neutral-200" />
<div className="h-4 w-40 animate-pulse rounded-md bg-neutral-200" />
</div>
))
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ProgramPartnerAboutPageClient } from "./page-client";

export default function ProgramPartnerAboutPage() {
return (
<>
<h2 className="text-content-emphasis text-lg font-semibold">About</h2>
<div className="mt-5 flex flex-col gap-5">
<ProgramPartnerAboutPageClient />
</div>
</>
);
}
Loading