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

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Oct 15, 2025

Summary by CodeRabbit

  • New Features

    • Program similarity scoring with scheduled background calculation
    • Partner categories surfaced in network and partner profiles
    • Arrow-key navigation in partner detail sheet
    • New partner performance metrics (conversion rate, LTV, consistency)
  • Updates

    • Improved partner ranking and discovery scoring with weighted signals
    • Simplified network filters (removed several multi-filter options)
    • Refined discoverability requirements and unified payout holding periods
    • Network endpoints and access broadened to include advanced plan users

@vercel
Copy link
Contributor

vercel bot commented Oct 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 10, 2025 2:39am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 18 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 02398f3 and 3e2ce8d.

📒 Files selected for processing (34)
  • apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-category-similarity.ts (1 hunks)
  • apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-partner-similarity.ts (1 hunks)
  • apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-performance-similarity.ts (1 hunks)
  • apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts (1 hunks)
  • apps/web/app/(ee)/api/cron/streams/update-partner-stats/route.ts (7 hunks)
  • apps/web/app/(ee)/api/network/partners/count/route.ts (3 hunks)
  • apps/web/app/(ee)/api/network/partners/invites-usage/route.ts (1 hunks)
  • apps/web/app/(ee)/api/network/partners/route.ts (2 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/analytics/route.ts (2 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (2 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (2 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/how-you-work-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-discovery-guide.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/use-partner-discovery-requirements.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/page-client.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/network/use-partner-network-filters.tsx (3 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/program-payout-settings-sheet.tsx (2 hunks)
  • apps/web/lib/actions/partners/update-partner-profile.ts (3 hunks)
  • apps/web/lib/api/network/calculate-partner-ranking.ts (1 hunks)
  • apps/web/lib/constants/payouts.ts (1 hunks)
  • apps/web/lib/constants/program.ts (1 hunks)
  • apps/web/lib/partners/discoverability.ts (0 hunks)
  • apps/web/lib/partners/get-discoverability-requirements.ts (1 hunks)
  • apps/web/lib/plan-capabilities.ts (1 hunks)
  • apps/web/lib/zod/schemas/partner-network.ts (1 hunks)
  • apps/web/lib/zod/schemas/programs.ts (2 hunks)
  • apps/web/scripts/migrations/backfill-program-categories.ts (1 hunks)
  • apps/web/scripts/migrations/update-discoverable-partners.ts (1 hunks)
  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx (1 hunks)
  • apps/web/vercel.json (1 hunks)
  • packages/prisma/schema/network.prisma (1 hunks)
  • packages/prisma/schema/partner.prisma (1 hunks)
  • packages/prisma/schema/program.prisma (3 hunks)

Walkthrough

Adds program similarity calculations (category, partner, performance), a cron route to batch and persist similarity scores, partner-ranking refactor to use similarities and categories, Prisma schema updates for categories and program similarities, migration/backfill scripts, discoverability changes, UI/schema/constant updates, and related route permission adjustments.

Changes

Cohort / File(s) Summary
Program similarity calculators & cron
apps/web/app/(ee)/api/cron/calculate-program-similarities/*
New calculators: calculate-category-similarity.ts, calculate-partner-similarity.ts, calculate-performance-similarity.ts; and route.ts to batch compute weighted similarity (0.5/0.3/0.2), threshold, persist ProgramSimilarity rows, and schedule next batch via QStash.
Partner ranking & network APIs
apps/web/lib/api/network/calculate-partner-ranking.ts, apps/web/app/(ee)/api/network/partners/route.ts, apps/web/app/(ee)/api/network/partners/count/route.ts
New calculatePartnerRanking with PartnerRanking* types; routes updated to call ranking, remove multi-filter fields (industryInterests, salesChannels, preferredEarningStructures), add categories to partner output, and use PROGRAM_SIMILARITY_SCORE_THRESHOLD.
Prisma schema changes
packages/prisma/schema/program.prisma, packages/prisma/schema/network.prisma, packages/prisma/schema/partner.prisma
Add Category enum and ProgramCategory model; add ProgramSimilarity model; extend Program relations and enrollment-derived fields; add @@index(country) on Partner; expand IndustryInterest enum.
Backfill & migration scripts
apps/web/scripts/migrations/backfill-program-categories.ts, apps/web/scripts/migrations/update-discoverable-partners.ts
New backfill using FireCrawl + AI to tag programs with categories and a migration to recalc/set discoverableAt per new discoverability rules.
Discoverability & helpers
apps/web/lib/partners/get-discoverability-requirements.ts, apps/web/lib/partners/discoverability.ts, apps/web/lib/actions/partners/update-partner-profile.ts
New getDiscoverabilityRequirements; discovery logic refactored to use programEnrollments and preferred earning structures; ACME handling adjusted and calling sites updated.
Partner stats stream & enrollment metrics
apps/web/app/(ee)/api/cron/streams/update-partner-stats/route.ts
Adds enrollment-derived metrics (lastConversionAt, conversionRate, averageLifetimeValue, leadConversionRate, daysSinceLastConversion, consistencyScore) and formats Date values for DB updates.
UI / schemas / constants
apps/web/lib/zod/schemas/partner-network.ts, apps/web/app/app.dub.co/.../program/network/*, apps/web/app/app.dub.co/.../program/payouts/*, apps/web/lib/constants/*, apps/web/lib/zod/schemas/programs.ts
Remove multi-filters (industryInterests, salesChannels, preferredEarningStructures); add categories to public schema; UI replaces industry/sales sections with categories; replace HOLDING_PERIOD_DAYS usage with PAYOUT_HOLDING_PERIOD_DAYS; add PROGRAM_SIMILARITY_SCORE_THRESHOLD and LARGE_PROGRAM_IDS.
Routes & permissions
apps/web/app/(ee)/api/network/partners/invites-usage/route.ts, others
Several network routes updated to allow ["enterprise","advanced"] where previously only ["enterprise"].
Minor UI/UX & copy tweaks
apps/web/app/(ee)/partners.dub.co/.../profile/*, apps/web/ui/partners/partner-network/network-partner-sheet.tsx
Small DOM id additions, copy change, and keyboard navigation (ArrowLeft/ArrowRight) for partner sheet.
Vercel cron
apps/web/vercel.json
Added cron entry for /api/cron/calculate-program-similarities (schedule 0 */12 * * *).

Sequence Diagram(s)

sequenceDiagram
    participant Cron as Cron Job
    participant Route as /api/cron/calc-program-similarities
    participant DB as Prisma
    participant Calc as Similarity Calculators
    participant Rank as PartnerRanking
    participant QStash as QStash

    Cron->>Route: GET (signed)
    Route->>DB: findNextProgram()
    DB-->>Route: currentProgram
    Route->>DB: fetch candidatePrograms (batch)

    loop per candidate pair
        Route->>Calc: calculateCategorySimilarity(p1,p2)
        Calc->>DB: fetch program categories (parallel)
        DB-->>Calc: categories
        Calc-->>Route: categoryScore

        Route->>Calc: calculatePartnerSimilarity(p1,p2)
        Calc->>DB: raw SQL partner counts
        DB-->>Calc: partnerStats
        Calc-->>Route: partnerScore

        Route->>Calc: calculatePerformanceSimilarity(p1,p2)
        Calc->>DB: fetch performance aggregates
        DB-->>Calc: metrics
        Calc-->>Route: performanceScore

        Route->>Route: compute weightedScore (0.5/0.3/0.2)
        alt score >= PROGRAM_SIMILARITY_SCORE_THRESHOLD
            Route->>DB: collect for insert
        end
    end

    Route->>DB: delete existing similarities (batch)
    Route->>DB: insert ProgramSimilarity rows (transaction)
    DB-->>Route: persisted
    Route->>QStash: schedule next run (cursor/payload)
    QStash-->>Route: scheduled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas to focus during review:

  • calculate-category/partner/performance-similarity — correctness, parallel queries, zero/null guards.
  • calculate-partner-ranking — SQL fragment correctness, weights, filters, and pagination.
  • Cron route (route.ts) — batching, cursor/resume logic, transactional delete/insert, QStash payload scheduling.
  • Prisma schema changes and migrations — new enums/models/relations and migration/data-backfill implications.
  • Backfill script — external scraping, AI prompt/validation, and enum conformity.

Possibly related PRs

Suggested reviewers

  • TWilson023
  • devkiran

Poem

🐇 I hopped through programs, sniffed each name,

Jaccard bites and cosine games, I came.
I scored the pairs, queued the next run bright,
Inserted rows by moon and morning light.
Networks hum now — a rabbit's tidy flight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Network ranking algorithm v2' directly describes the main objective of this PR, which implements a new partner ranking calculation system (calculatePartnerRanking) with improved similarity scoring for the network feature.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts (1)

173-188: Incomplete deletion leaves stale forward references.

The deletion only removes similarities where programId is in the batch (lines 176-182), but doesn't delete the "forward" references where programId = currentProgram.id and similarProgramId is in the batch. With skipDuplicates: true on insert, old entries with outdated similarity scores will never be updated.

Example: When processing program A against batch [B, C]:

  • Creates: A→B, B→A, A→C, C→A
  • Deletes: WHERE programId IN [B, C] (removes B→*, C→*)
  • Leaves: A→B, A→C from previous runs (stale scores)

Apply this diff to delete both directions:

     await prisma.$transaction(async (tx) => {
       const programIds = programs.map((program) => program.id);

       await tx.programSimilarity.deleteMany({
         where: {
-          programId: {
-            in: programIds,
-          },
+          OR: [
+            { programId: { in: programIds } },
+            { programId: currentProgram.id, similarProgramId: { in: programIds } },
+          ],
         },
       });
🧹 Nitpick comments (1)
apps/web/app/(ee)/api/network/partners/route.ts (1)

70-72: Refine categories parsing to handle edge cases.

While the current code correctly handles truly empty strings (which are falsy), it could produce [""] for whitespace-only strings like " " or comma-separated lists with empty segments like "a,,b".

Apply this diff to handle these edge cases:

-          categories: partner.categories
-            ? partner.categories.split(",").map((c: string) => c.trim())
-            : [],
+          categories: partner.categories?.trim()
+            ? partner.categories.split(",").map((c: string) => c.trim()).filter(Boolean)
+            : [],

The .filter(Boolean) removes any empty strings, and checking .trim() first ensures whitespace-only strings are treated as empty.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f3ef0f and d92f564.

📒 Files selected for processing (6)
  • apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts (1 hunks)
  • apps/web/app/(ee)/api/network/partners/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/program-payout-settings-sheet.tsx (2 hunks)
  • apps/web/lib/constants/payouts.ts (1 hunks)
  • apps/web/lib/constants/program.ts (1 hunks)
  • apps/web/lib/zod/schemas/programs.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/zod/schemas/programs.ts
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.

Applied to files:

  • apps/web/lib/constants/program.ts
📚 Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/program-payout-settings-sheet.tsx
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts
📚 Learning: 2025-10-28T19:17:28.085Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:20-27
Timestamp: 2025-10-28T19:17:28.085Z
Learning: In this codebase, Prisma's extendedWhereUnique feature is enabled (or Prisma 5.x is used), allowing findUniqueOrThrow to accept additional non-unique filters alongside unique fields like slug. This pattern is valid and acceptable.

Applied to files:

  • apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts
🧬 Code graph analysis (3)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/program-payout-settings-sheet.tsx (1)
apps/web/lib/constants/payouts.ts (1)
  • PAYOUT_HOLDING_PERIOD_DAYS (9-9)
apps/web/app/(ee)/api/network/partners/route.ts (4)
apps/web/lib/constants/program.ts (1)
  • PROGRAM_SIMILARITY_SCORE_THRESHOLD (3-3)
apps/web/lib/zod/schemas/partner-network.ts (2)
  • getNetworkPartnersQuerySchema (36-50)
  • NetworkPartnerSchema (63-102)
apps/web/lib/api/network/partner-ranking.ts (1)
  • calculatePartnerRanking (40-213)
apps/web/lib/actions/partners/get-conversion-score.ts (1)
  • getConversionScore (4-18)
apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts (7)
apps/web/lib/cron/verify-vercel.ts (1)
  • verifyVercelSignature (3-20)
apps/web/lib/api/errors.ts (1)
  • handleApiError (124-173)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-category-similarity.ts (1)
  • calculateCategorySimilarity (4-43)
apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-partner-similarity.ts (1)
  • calculatePartnerSimilarity (10-44)
apps/web/app/(ee)/api/cron/calculate-program-similarities/calculate-performance-similarity.ts (1)
  • calculatePerformanceSimilarity (12-65)
apps/web/lib/constants/program.ts (1)
  • PROGRAM_SIMILARITY_SCORE_THRESHOLD (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (9)
apps/web/lib/constants/payouts.ts (1)

9-9: LGTM!

The new constant is well-defined and appropriately placed in the payouts constants module. The values represent reasonable holding period options for payout settings.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/program-payout-settings-sheet.tsx (1)

5-8: LGTM!

The refactoring to use PAYOUT_HOLDING_PERIOD_DAYS is implemented correctly. The import is properly structured and the usage at line 110 is consistent with the change.

Also applies to: 110-110

apps/web/lib/constants/program.ts (1)

3-3: LGTM! Well-defined similarity threshold constant.

Centralizing the 0.3 threshold as a named constant improves maintainability and ensures consistent filtering across the similarity calculation and ranking workflows.

apps/web/app/(ee)/api/network/partners/route.ts (2)

25-32: LGTM! Similarity filtering correctly applied.

The similarPrograms query correctly filters by the threshold and limits results for efficient ranking. The use of gt (strict greater than) is consistent with the threshold filtering in the cron job.


46-60: LGTM! Ranking integration is correct.

The similarPrograms mapping and calculatePartnerRanking invocation properly forward all parameters. The starred ?? undefined conversion correctly handles nullish values.

apps/web/app/(ee)/api/cron/calculate-program-similarities/route.ts (4)

29-62: LGTM! Proper cron handler implementation.

Both GET and POST handlers correctly implement signature verification (Vercel for initial trigger, QStash for subsequent calls) and handle errors appropriately.


119-171: LGTM! Similarity calculation is well-structured.

The parallel computation of the three similarity components and their weighted combination (category 0.5, partner 0.3, performance 0.2) is efficient and logical. Bidirectional pairs are correctly created for scores above the threshold.


191-218: LGTM! Batch scheduling logic is sound.

The logic correctly determines whether to continue with the same current program (if batch is full) or advance to the next program (if batch is incomplete), and properly schedules the next invocation via QStash.


221-261: LGTM! findNextProgram helper is correctly implemented.

The helper properly handles both specific program lookup and sequential program discovery. The ACME program exclusion is now consistently applied regardless of whether afterProgramId is provided, and workspace plan filtering is appropriate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/app/(ee)/api/network/partners/route.ts (1)

72-74: Handle empty or whitespace-only categories strings.

This still returns [""] when partner.categories is an empty string. Trim first and filter falsy values so consumers always receive either meaningful categories or an empty array.

-          categories: partner.categories
-            ? partner.categories.split(",").map((c: string) => c.trim())
-            : [],
+          categories: partner.categories?.trim()
+            ? partner.categories
+                .split(",")
+                .map((c: string) => c.trim())
+                .filter(Boolean)
+            : [],
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d92f564 and 465df04.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/network/partners/route.ts (2 hunks)
  • apps/web/lib/api/network/partner-ranking.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/app/(ee)/api/network/partners/route.ts (4)
apps/web/lib/constants/program.ts (1)
  • PROGRAM_SIMILARITY_SCORE_THRESHOLD (3-3)
apps/web/lib/zod/schemas/partner-network.ts (2)
  • getNetworkPartnersQuerySchema (36-50)
  • NetworkPartnerSchema (63-102)
apps/web/lib/api/network/partner-ranking.ts (1)
  • calculatePartnerRanking (40-290)
apps/web/lib/actions/partners/get-conversion-score.ts (1)
  • getConversionScore (4-18)
apps/web/lib/api/network/partner-ranking.ts (1)
packages/prisma/index.ts (1)
  • prisma (3-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/ui/partners/partner-network/network-partner-sheet.tsx (2)

45-54: Consider adding enabled option for consistency.

The keyboard shortcut could benefit from the enabled option to prevent registration when onNext is unavailable, aligning with the pattern used elsewhere in this file (line 227-230).

Apply this diff:

-  useKeyboardShortcut(
-    "ArrowRight",
-    () => {
-      if (onNext) {
-        onNext();
-      }
-    },
-    { sheet: true },
-  );
+  useKeyboardShortcut(
+    "ArrowRight",
+    () => onNext?.(),
+    { sheet: true, enabled: !!onNext },
+  );

56-65: Consider adding enabled option for consistency.

Similar to the ArrowRight shortcut, this could benefit from the enabled option to prevent registration when onPrevious is unavailable.

Apply this diff:

-  useKeyboardShortcut(
-    "ArrowLeft",
-    () => {
-      if (onPrevious) {
-        onPrevious();
-      }
-    },
-    { sheet: true },
-  );
+  useKeyboardShortcut(
+    "ArrowLeft",
+    () => onPrevious?.(),
+    { sheet: true, enabled: !!onPrevious },
+  );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 465df04 and 6717e94.

📒 Files selected for processing (7)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-discovery-guide.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/use-partner-discovery-requirements.ts (1 hunks)
  • apps/web/lib/actions/partners/update-partner-profile.ts (1 hunks)
  • apps/web/lib/partners/discoverability.ts (3 hunks)
  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx (1 hunks)
  • packages/prisma/schema/program.prisma (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/about-you-form.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/prisma/schema/program.prisma
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-05-29T04:45:18.504Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.

Applied to files:

  • apps/web/lib/partners/discoverability.ts
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.

Applied to files:

  • apps/web/lib/actions/partners/update-partner-profile.ts
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.

Applied to files:

  • apps/web/lib/actions/partners/update-partner-profile.ts
📚 Learning: 2025-10-17T08:18:19.278Z
Learnt from: devkiran
Repo: dubinc/dub PR: 0
File: :0-0
Timestamp: 2025-10-17T08:18:19.278Z
Learning: In the apps/web codebase, `@/lib/zod` should only be used for places that need OpenAPI extended zod schema. All other places should import from the standard `zod` package directly using `import { z } from "zod"`.

Applied to files:

  • apps/web/lib/actions/partners/update-partner-profile.ts
📚 Learning: 2025-09-18T17:03:06.200Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2858
File: apps/web/ui/partners/partner-application-sheet.tsx:262-266
Timestamp: 2025-09-18T17:03:06.200Z
Learning: The useKeyboardShortcut hook with context options like {sheet: true, modal: true} requires ALL specified contexts to be present simultaneously. The shortcut will only trigger when both existingSheetBackdrop and existingModalBackdrop are present, using strict equality matching in the implementation.

Applied to files:

  • apps/web/ui/partners/partner-network/network-partner-sheet.tsx
🧬 Code graph analysis (1)
apps/web/lib/partners/discoverability.ts (2)
apps/web/lib/types.ts (2)
  • PartnerProps (441-444)
  • EnrolledPartnerProps (453-453)
apps/web/lib/partners/online-presence.ts (1)
  • PartnerOnlinePresenceFields (13-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-discovery-guide.tsx (1)

70-71: LGTM! Clear and aligned with the PR's Partner Network theme.

The updated copy effectively communicates the value proposition to partners and aligns with the broader network ranking and categorization changes introduced in this PR.

apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/use-partner-discovery-requirements.ts (1)

10-19: LGTM!

The refactoring correctly passes programEnrollments from the SWR hook to getPartnerDiscoveryRequirements, and the memoization dependencies are properly updated.

apps/web/lib/partners/discoverability.ts (3)

44-46: Verify the business logic change from industryInterests to monthlyTraffic.

The discovery requirement has changed from "Specify your industry interests" to "Specify your estimated monthly traffic". This is a significant change in what partners need to provide for discoverability. Please confirm this aligns with the product requirements for partner discovery.


54-54: Improved label with formatted currency.

The use of currencyFormatter with trailingZeroDisplay: "stripIfInteger" provides a cleaner user-facing label for the commission requirement.


54-61: Confirmed: Logic change is as described—verify product alignment.

Your analysis is accurate. The refactoring changes the requirement from:

  • Previous: Total commissions ≥ $100 across all programs (combined)
  • Current: At least 2 approved programs (excluding ACME), each with ≥ $100

This is a deliberate architectural change (see commit: "improve getPartnerDiscoveryRequirements") that shifts from aggregated to per-program thresholds. A partner earning $200 from one program would have qualified before but now fails.

Recommend confirming this stricter threshold aligns with current product goals for partner discoverability.

apps/web/lib/actions/partners/update-partner-profile.ts (1)

186-186: Update type safety concern - no issues found.

The code is type-safe. The updatedPartner.programs relation (fetched at line 144 with programs: true) contains full ProgramEnrollment objects that include all required fields:

  • programId
  • status
  • totalCommissions

The function accepts Pick<EnrolledPartnerProps, "programId" | "status" | "totalCommissions">[], which is satisfied by the Prisma relation. TypeScript Pick is a type utility that selects specific fields; passing objects with additional fields is valid since those three fields are present in ProgramEnrollment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/app/(ee)/api/network/partners/route.ts (2)

25-32: Add orderBy when using take on similarPrograms.

Prisma will throw The query includes "take" but it does not include an "orderBy" at runtime. Add an explicit sort (e.g., highest similarity first) before shipping.

         similarPrograms: {
           where: {
             similarityScore: {
               gt: PROGRAM_SIMILARITY_SCORE_THRESHOLD,
             },
           },
+          orderBy: {
+            similarityScore: "desc",
+          },
           take: 5,
         },

72-74: Strip whitespace-only category entries.

When partner.categories is whitespace, split(",") yields [""], so callers receive an empty string category. Trim first and filter out blank items to avoid rendering ghost chips.

-          categories: partner.categories
-            ? partner.categories.split(",").map((c: string) => c.trim())
-            : [],
+          categories: partner.categories?.trim()
+            ? partner.categories
+                .split(",")
+                .map((c: string) => c.trim())
+                .filter((c: string) => c.length > 0)
+            : [],
🧹 Nitpick comments (1)
apps/web/app/(ee)/api/network/partners/route.ts (1)

51-62: Remove leftover console.time instrumentation.

console.time/console.timeEnd inside the API route will spam logs in production. Drop these or gate them behind an explicit debug flag.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c98359 and bfd5f5b.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/api/network/partners/route.ts (2 hunks)
  • apps/web/lib/api/network/calculate-partner-ranking.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-28T19:17:28.085Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2985
File: apps/web/app/(ee)/api/network/programs/[programSlug]/route.ts:20-27
Timestamp: 2025-10-28T19:17:28.085Z
Learning: In this codebase, Prisma's extendedWhereUnique feature is enabled (or Prisma 5.x is used), allowing findUniqueOrThrow to accept additional non-unique filters alongside unique fields like slug. This pattern is valid and acceptable.

Applied to files:

  • apps/web/app/(ee)/api/network/partners/route.ts
🧬 Code graph analysis (2)
apps/web/app/(ee)/api/network/partners/route.ts (4)
apps/web/lib/constants/program.ts (1)
  • PROGRAM_SIMILARITY_SCORE_THRESHOLD (3-3)
apps/web/lib/zod/schemas/partner-network.ts (2)
  • getNetworkPartnersQuerySchema (36-50)
  • NetworkPartnerSchema (63-102)
apps/web/lib/api/network/calculate-partner-ranking.ts (1)
  • calculatePartnerRanking (40-290)
apps/web/lib/actions/partners/get-conversion-score.ts (1)
  • getConversionScore (4-18)
apps/web/lib/api/network/calculate-partner-ranking.ts (2)
packages/prisma/client.ts (1)
  • Prisma (29-29)
packages/prisma/index.ts (1)
  • prisma (3-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
apps/web/scripts/migrations/update-discoverable-partners.ts (1)

22-83: Remove or document the commented-out code.

This 61-line commented-out block adds significant noise to the migration script. If these social field checks are not needed, remove them. If they might be needed in the future, consider documenting why they're commented out or moving them to a separate reference file.

Apply this diff to remove the commented-out code:

       },
-      //   AND: [
-      //     {
-      //       OR: [
-      //         {
-      //           website: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { website: null },
-      //       ],
-      //     },
-      //     {
-      //       OR: [
-      //         {
-      //           youtube: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { youtube: null },
-      //       ],
-      //     },
-      //     {
-      //       OR: [
-      //         {
-      //           twitter: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { twitter: null },
-      //       ],
-      //     },
-      //     {
-      //       OR: [
-      //         {
-      //           linkedin: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { linkedin: null },
-      //       ],
-      //     },
-      //     {
-      //       OR: [
-      //         {
-      //           instagram: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { instagram: null },
-      //       ],
-      //     },
-      //     {
-      //       OR: [
-      //         {
-      //           tiktok: {
-      //             equals: "",
-      //           },
-      //         },
-      //         { tiktok: null },
-      //       ],
-      //     },
-      //   ],
     },
apps/web/lib/partners/get-discoverability-requirements.ts (2)

9-9: Consider extracting the commission threshold to a shared constant.

The threshold PARTNER_DISCOVERY_MIN_COMMISSIONS = 10_00 is duplicated between this file and the migration script. To ensure consistency and make future updates easier, consider extracting this to apps/web/lib/constants/program.ts alongside LARGE_PROGRAM_IDS.

In apps/web/lib/constants/program.ts, add:

export const LARGE_PROGRAM_IDS = [
  "prog_CYCu7IMAapjkRpTnr8F1azjN",
  "prog_1K0QHV7MP3PR05CJSCF5VN93X",
];

export const PARTNER_DISCOVERY_MIN_COMMISSIONS = 10_00;

Then update both files to import it:

-const PARTNER_DISCOVERY_MIN_COMMISSIONS = 10_00;
+import { LARGE_PROGRAM_IDS, PARTNER_DISCOVERY_MIN_COMMISSIONS } from "../constants/program";

36-38: Address or track the TODO comment.

The TODO indicates that the online presence requirement should also verify that accounts are verified, not just connected. This could affect discoverability quality if unverified accounts are accepted.

Should this verification check be implemented now, or would you like me to open an issue to track this enhancement?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfd5f5b and 773ba68.

📒 Files selected for processing (6)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/analytics/route.ts (2 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (2 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (2 hunks)
  • apps/web/lib/constants/program.ts (1 hunks)
  • apps/web/lib/partners/get-discoverability-requirements.ts (1 hunks)
  • apps/web/scripts/migrations/update-discoverable-partners.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/lib/constants/program.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.

Applied to files:

  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts
  • apps/web/scripts/migrations/update-discoverable-partners.ts
📚 Learning: 2025-07-17T06:41:45.620Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2637
File: apps/web/app/(ee)/api/singular/webhook/route.ts:0-0
Timestamp: 2025-07-17T06:41:45.620Z
Learning: In the Singular integration (apps/web/app/(ee)/api/singular/webhook/route.ts), the event names in the singularToDubEvent object have intentionally different casing: "Copy GAID" and "copy IDFA". This casing difference is valid and should not be changed, as these are the correct event names expected from Singular.

Applied to files:

  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts
📚 Learning: 2025-06-16T19:21:23.506Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.

Applied to files:

  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/analytics/route.ts
🧬 Code graph analysis (5)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (1)
apps/web/lib/constants/program.ts (1)
  • LARGE_PROGRAM_IDS (2-5)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/analytics/route.ts (1)
apps/web/lib/constants/program.ts (1)
  • LARGE_PROGRAM_IDS (2-5)
apps/web/scripts/migrations/update-discoverable-partners.ts (2)
packages/prisma/index.ts (1)
  • prisma (3-9)
apps/web/lib/constants/program.ts (1)
  • LARGE_PROGRAM_IDS (2-5)
apps/web/lib/partners/get-discoverability-requirements.ts (3)
apps/web/lib/types.ts (2)
  • PartnerProps (441-444)
  • EnrolledPartnerProps (453-453)
apps/web/lib/partners/online-presence.ts (2)
  • PartnerOnlinePresenceFields (13-27)
  • ONLINE_PRESENCE_FIELDS (29-103)
apps/web/lib/constants/program.ts (1)
  • LARGE_PROGRAM_IDS (2-5)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (1)
apps/web/lib/constants/program.ts (1)
  • LARGE_PROGRAM_IDS (2-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
apps/web/app/(ee)/api/partner-profile/programs/[programId]/events/route.ts (2)

5-8: LGTM! Clean import of centralized constants.

The import correctly brings in LARGE_PROGRAM_IDS to support multiple large programs, improving maintainability.


31-31: LGTM! Refactoring to dynamic program ID check.

The change from a hardcoded ID to LARGE_PROGRAM_IDS.includes(program.id) is backward-compatible and makes it easy to manage multiple large programs centrally.

apps/web/app/(ee)/api/partner-profile/programs/[programId]/customers/[customerId]/route.ts (2)

6-9: LGTM! Consistent with the refactoring pattern.

The import correctly adds LARGE_PROGRAM_IDS for centralized large program management, consistent with the other route handlers.


31-31: LGTM! Dynamic check properly implemented.

The refactoring from hardcoded ID to LARGE_PROGRAM_IDS.includes(program.id) is correctly applied and maintains backward compatibility.

apps/web/app/(ee)/api/partner-profile/programs/[programId]/analytics/route.ts (1)

5-8: LGTM! Consistent import pattern.

The import correctly adds LARGE_PROGRAM_IDS to enable centralized management of large program IDs across all partner profile routes.

apps/web/scripts/migrations/update-discoverable-partners.ts (1)

15-15: No issues found. The $10 commission threshold is intentional.

The threshold is defined as a centralized constant PARTNER_DISCOVERY_MIN_COMMISSIONS = 10_00 in apps/web/lib/partners/get-discoverability-requirements.ts and is displayed to partners as part of the discovery requirements: "Earn $10 in commissions." This is a deliberate, well-designed barrier to filter inactive partners while keeping the threshold achievable, and it's used consistently throughout the eligibility logic.

@steven-tey
Copy link
Collaborator

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

✅ Actions performed

Full review triggered.

@steven-tey steven-tey merged commit 160bfdd into main Nov 10, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the network-v2 branch November 10, 2025 02:47
@coderabbitai coderabbitai bot mentioned this pull request Nov 10, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 24, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants