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

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Dec 4, 2025

Summary by CodeRabbit

Bug Fixes

  • Improved accuracy of date-based filters for earnings and commission reporting
  • Enhanced date range calculations for analytics queries
  • Fixed email notification formatting for clearer partner communication

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Dec 4, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Dec 4, 2025 10:05pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

This PR updates date handling across multiple files by switching from ISO string representations to native Date objects in database queries and enhances email formatting with dynamic pluralization in the payout notification system.

Changes

Cohort / File(s) Summary
Date Filter Updates in Commissions & Earnings
apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts, apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts, apps/web/lib/api/commissions/get-commissions-count.ts, apps/web/lib/api/commissions/get-commissions.ts
Modified createdAt filter in database queries to pass Date objects directly instead of .toISOString() representations; in get-commissions.ts, also added CommissionStatus filter to exclude duplicate, fraud, and canceled statuses when status is not provided
Date Utility Function Updates
apps/web/lib/analytics/utils/get-start-end-dates.ts, apps/web/lib/analytics/utils/valid-date-range-for-plan.ts
Updated get-start-end-dates.ts to use endOfDay(end ?? Date.now()) for endDate calculation; modified valid-date-range-for-plan.ts to pass Date objects directly to getDaysDifference instead of wrapping with new Date()
Query String Construction
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx
Changed query string construction to include start and end Date objects directly instead of converting them to ISO strings with new Date(...).toISOString()
Email Notification Enhancement
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts
Added pluralize utility import and updated email subject to dynamically pluralize "partner" label based on payout count instead of hard-coded format

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Date object parameter conversion: Multiple files switching from ISO strings to Date objects requires verification that Prisma queries and downstream date operations handle the change correctly, particularly around timezone handling
  • CommissionStatus filtering logic: New default filtering logic in get-commissions.ts that excludes specific statuses needs review to ensure it doesn't inadvertently filter valid data in unintended scenarios
  • endOfDay behavior change: The shift to endOfDay() in get-start-end-dates.ts could affect query ranges and should be verified against expected analytics window behavior

Possibly related PRs

Suggested reviewers

  • TWilson023

Poem

🐰 From strings to dates, our queries now flow,
EndOfDay arrives with a calendar glow,
Commissions are filtered, plurals align,
Date objects dancing—such elegant design! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'Fix getStartEndDates' is specific and directly related to the main change. The PR modifies the getStartEndDates function and how dates are handled across the codebase, making it an accurate summary of the primary focus.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-start-end

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc3efc and 8c67d01.

📒 Files selected for processing (3)
  • apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (2 hunks)
  • apps/web/lib/analytics/utils/get-start-end-dates.ts (2 hunks)
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/lib/analytics/utils/get-start-end-dates.ts
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts
⏰ 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 (2)
apps/web/app/(ee)/api/cron/payouts/process/process-payouts.ts (2)

20-20: LGTM!

The import of pluralize is correctly added to support dynamic pluralization in the email subject.


295-295: LGTM! Good improvement for grammatical correctness.

The use of pluralize("partner", res.count) ensures the email subject is grammatically correct regardless of whether one or multiple partners are being paid.


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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/lib/analytics/utils/get-start-end-dates.ts (1)

25-31: Critical bug: differenceInDays arguments are in wrong order.

differenceInDays(startDate, endDate) returns startDate - endDate, which yields a negative value when startDate < endDate (the normal case). This breaks the granularity logic:

  • daysDifference <= 2 will almost always be true (negatives are ≤ 2)
  • daysDifference > 180 will almost never be true

Swap the arguments to get the positive difference:

-    const daysDifference = differenceInDays(startDate, endDate);
+    const daysDifference = differenceInDays(endDate, startDate);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3242721 and 36107c8.

📒 Files selected for processing (6)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts (1 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx (1 hunks)
  • apps/web/lib/analytics/utils/get-start-end-dates.ts (2 hunks)
  • apps/web/lib/api/commissions/get-commissions-count.ts (1 hunks)
  • apps/web/lib/api/commissions/get-commissions.ts (2 hunks)
🧰 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]/earnings/count/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts
📚 Learning: 2025-10-31T19:45:25.702Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3044
File: apps/web/lib/analytics/utils/get-interval-data.ts:31-0
Timestamp: 2025-10-31T19:45:25.702Z
Learning: In apps/web/lib/analytics/utils/get-interval-data.ts, the `mtd` and `qtd` interval functions are designed to work in both server and client contexts. When no timezone is provided, they intentionally fall back to `new Date()` (local timezone) because front-end code calls these methods without passing a timezone and should use the browser's local timezone for the user's context.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts
  • apps/web/lib/api/commissions/get-commissions-count.ts
  • apps/web/lib/analytics/utils/get-start-end-dates.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/page-client.tsx
⏰ 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]/earnings/count/route.ts (1)

48-51: LGTM!

Passing Date objects directly to Prisma's DateTime fields is valid and cleaner than converting to ISO strings. This aligns with the broader refactoring across the codebase.

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

60-63: LGTM!

Consistent with the pattern used in the count route. Prisma natively handles Date objects for DateTime fields.

apps/web/lib/api/commissions/get-commissions-count.ts (1)

53-56: LGTM!

Consistent date handling refactor using Date objects directly.

apps/web/lib/api/commissions/get-commissions.ts (1)

50-56: LGTM on date handling; verify the new default status filter is intentional.

The Date object usage is consistent with the rest of the PR. However, lines 50-56 introduce a new default behavior that excludes duplicate, fraud, and canceled statuses when no status filter is provided. This matches get-commissions-count.ts, so it appears intentional—just confirming this behavioral change is expected as part of this fix.

Also applies to: 60-63

apps/web/lib/analytics/utils/get-start-end-dates.ts (1)

23-23: Good fix: Using endOfDay ensures the entire end date is included.

This correctly sets the end boundary to 23:59:59.999 of the specified day, ensuring queries include all records from that day.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx (1)

47-53: Preserving start/end search params (no extra ISO conversion) looks correct

Switching from new Date(start/end).toISOString() to spreading the raw { start, end } keeps the query aligned with whatever canonical date strings getStartEndDates / router logic already produce, and avoids an extra parse/serialize step that could introduce timezone drift. Behavior of only including both start and end when both are present is preserved, and the downstream AnalyticsContext still gets parseable values via new Date(start/end).

This is consistent with the broader PR goal of centralizing Date construction in shared utilities and letting the API handle the date math.

Based on learnings, this also keeps the “browser local timezone + explicit timezone param” pattern intact.

@steven-tey
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

✅ Actions performed

Full review triggered.

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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36107c8 and 3002ed5.

📒 Files selected for processing (4)
  • apps/web/lib/analytics/format-date-tooltip.ts (2 hunks)
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (3 hunks)
  • packages/utils/src/functions/datetime/get-days-difference.ts (0 hunks)
  • packages/utils/src/functions/datetime/index.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/utils/src/functions/datetime/index.ts
  • packages/utils/src/functions/datetime/get-days-difference.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-31T19:45:25.702Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3044
File: apps/web/lib/analytics/utils/get-interval-data.ts:31-0
Timestamp: 2025-10-31T19:45:25.702Z
Learning: In apps/web/lib/analytics/utils/get-interval-data.ts, the `mtd` and `qtd` interval functions are designed to work in both server and client contexts. When no timezone is provided, they intentionally fall back to `new Date()` (local timezone) because front-end code calls these methods without passing a timezone and should use the browser's local timezone for the user's context.

Applied to files:

  • apps/web/lib/analytics/format-date-tooltip.ts
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts
⏰ 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/lib/analytics/utils/get-start-end-dates.ts (1)

1-26: Normalize end to a Date before calling endOfDay to fix a TypeScript type error.

end is typed as string | Date | null, but endOfDay from date-fns has the signature endOfDay(date: Date | number): Date. The expression endOfDay(end ?? Date.now()) has type string | Date | number at compile time, which does not match the expected Date | number parameter type.

You can keep the same semantics and fix the typing by normalizing end before passing it:

   if (start || (interval === "all" && dataAvailableFrom)) {
-    startDate = new Date(start ?? dataAvailableFrom ?? Date.now());
-    endDate = new Date(endOfDay(end ?? Date.now()));
-
-    const daysDifference = differenceInDays(startDate, endDate);
+    const startInput = start ?? dataAvailableFrom ?? Date.now();
+    startDate = new Date(startInput);
+
+    const endInput = end ? new Date(end) : new Date();
+    endDate = endOfDay(endInput);
+
+    const daysDifference = differenceInDays(startDate, endDate);

This preserves the intended behavior (inclusive end-of-day semantics) while satisfying the date-fns type signature.

♻️ Duplicate comments (2)
apps/web/lib/analytics/format-date-tooltip.ts (1)

30-33: differenceInDays arguments are reversed, inverting the day-range logic.

differenceInDays(later, earlier) returns a positive count; passing (start, end) (where start is usually earlier than end) yields negative values and breaks the <= 2 / > 180 checks, causing wrong tooltip formatting for most ranges. Swap the arguments so end (typically “now” or the upper bound) is first.

Suggested fix:

-    const daysDifference = differenceInDays(
-      typeof start === "string" ? new Date(start) : start,
-      typeof end === "string" ? new Date(end) : end,
-    );
+    const daysDifference = differenceInDays(
+      typeof end === "string" ? new Date(end) : end,
+      typeof start === "string" ? new Date(start) : start,
+    );

To double‑check, please verify against the date-fns docs for differenceInDays (that the first argument is treated as the later date) before merging.

apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (1)

35-36: differenceInDays arguments reversed; free/pro limits never trigger.

differenceInDays(start, end || now) yields a negative value for normal ranges where start < end, so the > 31 (free) and > 366 (pro) checks are effectively disabled. You need end || now as the first argument and start as the second.

Apply this diff:

-      (start && differenceInDays(start, end || now) > 31))
+      (start && differenceInDays(end || now, start) > 31))
@@
-  if (plan === "pro" && start && differenceInDays(start, end || now) > 366) {
+  if (plan === "pro" && start && differenceInDays(end || now, start) > 366) {

Also applies to: 46-46

🧹 Nitpick comments (1)
apps/web/lib/api/commissions/get-commissions.ts (1)

4-63: CommissionStatus import + default status filter + Date bounds all look consistent.

  • Importing CommissionStatus from @dub/prisma/client lines up with get-commissions-count.ts and the shared Prisma client.
  • The status: status ?? { notIn: [duplicate, fraud, canceled] } default on the non‑invoice path is a sensible guard so “no filter” excludes obviously invalid states, while still respecting explicit status filters.
  • Moving createdAt to gte: startDate / lte: endDate matches the rest of the PR and Prisma’s natural usage of Date values.

Assuming callers rely on this function for user-facing commission lists (and not for low-level auditing), this behavior shift looks intentional and correct.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3242721 and 1bc3efc.

📒 Files selected for processing (10)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts (1 hunks)
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/route.ts (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx (1 hunks)
  • apps/web/lib/analytics/format-date-tooltip.ts (2 hunks)
  • apps/web/lib/analytics/utils/get-start-end-dates.ts (2 hunks)
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (3 hunks)
  • apps/web/lib/api/commissions/get-commissions-count.ts (1 hunks)
  • apps/web/lib/api/commissions/get-commissions.ts (2 hunks)
  • packages/utils/src/functions/datetime/get-days-difference.ts (0 hunks)
  • packages/utils/src/functions/datetime/index.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/utils/src/functions/datetime/get-days-difference.ts
  • packages/utils/src/functions/datetime/index.ts
🧰 Additional context used
🧠 Learnings (4)
📚 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]/earnings/route.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx
  • apps/web/app/(ee)/api/partner-profile/programs/[programId]/earnings/count/route.ts
📚 Learning: 2025-10-31T19:45:25.702Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3044
File: apps/web/lib/analytics/utils/get-interval-data.ts:31-0
Timestamp: 2025-10-31T19:45:25.702Z
Learning: In apps/web/lib/analytics/utils/get-interval-data.ts, the `mtd` and `qtd` interval functions are designed to work in both server and client contexts. When no timezone is provided, they intentionally fall back to `new Date()` (local timezone) because front-end code calls these methods without passing a timezone and should use the browser's local timezone for the user's context.

Applied to files:

  • apps/web/lib/analytics/format-date-tooltip.ts
  • apps/web/lib/analytics/utils/get-start-end-dates.ts
  • apps/web/lib/analytics/utils/valid-date-range-for-plan.ts
📚 Learning: 2025-07-09T20:52:56.592Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2614
File: apps/web/ui/partners/design/previews/lander-preview.tsx:181-181
Timestamp: 2025-07-09T20:52:56.592Z
Learning: In apps/web/ui/partners/design/previews/lander-preview.tsx, the ellipsis wave animation delay calculation `3 - i * -0.15` is intentionally designed to create negative delays that offset each dot's animation cycle. This pattern works correctly for the intended ellipsis effect and should not be changed to positive incremental delays.

Applied to files:

  • apps/web/lib/analytics/format-date-tooltip.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/lib/api/commissions/get-commissions.ts
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx
🧬 Code graph analysis (1)
apps/web/lib/api/commissions/get-commissions.ts (1)
packages/prisma/client.ts (1)
  • CommissionStatus (10-10)
🔇 Additional comments (5)
apps/web/lib/analytics/utils/valid-date-range-for-plan.ts (1)

1-1: date-fns import usage looks correct.

differenceInDays is imported from the canonical date-fns entrypoint and is used below for plan window checks; no issues here.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/page-client.tsx (1)

47-53: Passing raw start/end query params looks correct, assuming the picker always sets both.

Including start/end directly from searchParamsObj when both exist avoids double serialization and keeps the API aligned with the new getStartEndDates behavior. This does change behavior slightly if only one of start/end could be set, but with SimpleDateRangePicker typically providing both, that seems acceptable.

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

48-63: Using Date objects for createdAt bounds matches Prisma expectations.

Switching createdAt to use gte: startDate / lte: endDate directly is consistent with the updated getStartEndDates behavior and with other endpoints in this PR; Prisma will serialize the Dates correctly.

apps/web/lib/api/commissions/get-commissions-count.ts (1)

35-56: Consistent Date-based createdAt filtering.

Using gte: startDate / lte: endDate in the createdAt filter keeps commissions count aligned with the other updated routes and avoids unnecessary ISO string conversion.

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

41-51: Date object bounds for createdAt are consistent with the rest of the API.

The change to use startDate / endDate directly in the createdAt filter brings this endpoint in line with the other earnings/commissions queries and with the updated getStartEndDates behavior.

@steven-tey steven-tey merged commit 940192a into main Dec 4, 2025
8 of 9 checks passed
@steven-tey steven-tey deleted the fix-start-end branch December 4, 2025 22:11
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.

2 participants