Skip to content

Commissions on hold filter#3409

Merged
steven-tey merged 5 commits intomainfrom
commission-on-hold
Feb 4, 2026
Merged

Commissions on hold filter#3409
steven-tey merged 5 commits intomainfrom
commission-on-hold

Conversation

@marcusljf
Copy link
Collaborator

@marcusljf marcusljf commented Feb 3, 2026

Being able to filter the commissions table by "On hold" status from fraud.

CleanShot 2026-02-03 at 10 25 01@2x

Summary by CodeRabbit

  • New Features

    • Added a "hold" status across commissions and payouts with UI badges, tooltips, and filter support.
    • Export queries now include timezone when exporting commissions.
  • Data & Metrics

    • Counts now include a "hold" category and track earnings alongside amounts.
  • Chores

    • Added a database index to support hold-related queries.

Being able to filter the commissions table by "On hold" status from fraud.
@vercel
Copy link
Contributor

vercel bot commented Feb 3, 2026

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

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Feb 4, 2026 1:18am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds explicit "hold" status handling across commissions and payouts: routes detect and strip status=hold, set isHoldStatus, and pass it to helpers which apply fraud-event-based filters and status mappings; types, counts, badges, and queries are extended to expose and handle a derived "hold" category.

Changes

Cohort / File(s) Summary
Commission API Routes
apps/web/app/(ee)/api/commissions/route.ts, apps/web/app/(ee)/api/commissions/count/route.ts
Detect status=hold, remove status from parsed params, set isHoldStatus and pass it to helper functions so queries apply fraud-event based filters instead of raw status.
Payout API Routes
apps/web/app/(ee)/api/programs/[programId]/payouts/route.ts, apps/web/app/(ee)/api/programs/[programId]/payouts/count/route.ts
Detect status=hold, strip for parsing, map to PayoutStatus.pending, and when hold is active include programEnrollment.fraudEventGroups.some({ status: FraudEventStatus.pending }); adjust includes and tenantId sourcing.
Commission Helpers
apps/web/lib/api/commissions/get-commissions.ts, apps/web/lib/api/commissions/get-commissions-count.ts
Add isHoldStatus to filter types; introduce statusFilter and programEnrollmentFilter that include fraud-event constraints when hold is true; apply earnings: { not: 0 }; expose "hold" in counts and results.
Types
apps/web/lib/types.ts
Extend CommissionsCount keys to include "hold" and add earnings to the count value payload.
UI Filter Components
apps/web/app/app.dub.co/.../program/commissions/use-commission-filters.tsx, apps/web/app/app.dub.co/.../program/payouts/use-payout-filters.tsx
Reintroduce "hold" into status options; payout filters conditionally hide the right-side count for the "hold" option.
Status Badge Components
apps/web/ui/partners/commission-status-badges.tsx, apps/web/ui/partners/payout-status-badges.tsx
Add a new "hold" badge entry for commissions and payouts (label, variant, icon, tooltip/text) to represent fraud-pending hold state.
DB Schema
packages/prisma/schema/payout.prisma
Add composite index @@index([programId, partnerId]) on Payout to support programEnrollment-related queries.
Export Modal
apps/web/ui/modals/export-commissions-modal.tsx
Query string construction now always includes timezone, altering the exported API query shape.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant APIRoute as API Route
    participant Helper as Helper (getCommissions / getCommissionsCount)
    participant DB as Database (Prisma)

    Client->>APIRoute: GET /commissions?status=hold
    APIRoute->>APIRoute: detect status === "hold"\nstrip status from params\nset isHoldStatus = true
    APIRoute->>Helper: call helper with parsedParams + isHoldStatus
    Helper->>Helper: build statusFilter (pending|processed)\nbuild programEnrollmentFilter (fraudEventGroups.status = pending)
    Helper->>DB: execute Prisma query with statusFilter + programEnrollmentFilter + earnings != 0
    DB-->>Helper: return filtered results
    Helper-->>APIRoute: aggregated counts / items (includes "hold")
    APIRoute-->>Client: JSON response (hold presented as derived category)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related PRs

Suggested reviewers

  • devkiran

Poem

🐰 A rabbit nudges a flagged file bright,
"Hold" peeks out in morning light,
Fraud whispers paused, filters hum,
Counts and badges hop along, drum-drum,
A gentle guard for payouts tonight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'Commissions on hold filter' directly and clearly summarizes the main objective of the changeset: adding functionality to filter commissions by 'on hold' status.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 commission-on-hold

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

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/api/commissions/get-commissions-count.ts (1)

85-122: ⚠️ Potential issue | 🟠 Major

Missing population of counts.hold when isHoldStatus is true.

The type annotation declares "hold" as a valid key in the return type (types.ts:448-449), but getCommissionsCount never populates counts.hold. When isHoldStatus is true (when callers request ?status=hold), the API endpoint returns the entire counts object without the hold key, violating the type contract.

When filtering by hold status, set counts.hold to the aggregated totals:

🐛 Proposed fix
  counts.all = commissionsCount.reduce(
    (acc, p) => ({
      count: acc.count + p._count,
      amount: acc.amount + (p._sum.amount ?? 0),
      earnings: acc.earnings + (p._sum.earnings ?? 0),
    }),
    { count: 0, amount: 0, earnings: 0 },
  );

+  if (isHoldStatus) {
+    counts.hold = counts.all;
+  }
+
  return counts;
🧹 Nitpick comments (1)
apps/web/ui/partners/payout-status-badges.tsx (1)

53-59: Consider using ShieldAlert icon for consistency with commission hold status.

The hold status in CommissionStatusBadges (commission-status-badges.tsx:136) uses ShieldAlert to convey the fraud-related nature of the hold. Using CircleXmark here creates a visual inconsistency between the two views for what represents the same underlying concept.

🔧 Suggested change for icon consistency
+import {
+  CircleCheck,
+  CircleHalfDottedCheck,
+  CircleHalfDottedClock,
+  CircleWarning,
+  CircleXmark,
+  PaperPlane,
+  ShieldAlert,
+} from "@dub/ui/icons";
...
   // extra status for hold (not in OpenAPI spec)
   hold: {
     label: "On Hold",
     variant: "error",
-    icon: CircleXmark,
+    icon: ShieldAlert,
     className: "text-red-600 bg-red-100",
   },

@steven-tey
Copy link
Collaborator

@coderabbitai full review, any breaking changes?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

@steven-tey, I'll conduct a full review. Here are the breaking changes I've identified:

🚨 Breaking Changes

1. CommissionsCount Type Shape Changed (apps/web/lib/types.ts)

The value shape in the CommissionsCount type changed from:

{ count: number; amount: number; }

to:

{ count: number; amount: number; earnings: number }

Impact: Any TypeScript consumers expecting the old shape will have type errors. Runtime consumers expecting only count and amount will need to handle the new earnings field.

2. "hold" Status Not in OpenAPI Spec

Multiple files note that "hold" is an extra status not in the OpenAPI spec:

  • apps/web/ui/partners/commission-status-badges.tsx
  • apps/web/ui/partners/payout-status-badges.tsx

Impact:

  • API clients generated from OpenAPI spec won't recognize this status
  • Documentation may be out of sync
  • Could cause validation errors in strict API clients

3. New Key "hold" in CommissionsCount Record

The type now accepts "hold" as a key: Record<CommissionStatus | "all" | "hold", {...}>

Impact: Consumers iterating over expected keys or using exhaustive type checking will need updates.

📋 Recommendations

  1. Update OpenAPI spec to include the "hold" status
  2. Consider versioning the API if the type change affects external consumers
  3. Add migration guide if this is a public API
  4. Verify database performance with the new [programId, partnerId] index under production load
✅ 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: 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/api/commissions/get-commissions-count.ts (1)

85-122: ⚠️ Potential issue | 🟠 Major

Set counts.hold explicitly to match the new public type.

The return type now includes "hold", but this function never assigns it, so consumers will see undefined. Consider populating it (at least when isHoldStatus is true) to keep runtime behavior in sync with the type.

✅ Suggested fix
   counts.all = commissionsCount.reduce(
     (acc, p) => ({
       count: acc.count + p._count,
       amount: acc.amount + (p._sum.amount ?? 0),
       earnings: acc.earnings + (p._sum.earnings ?? 0),
     }),
     { count: 0, amount: 0, earnings: 0 },
   );
+
+  counts.hold = isHoldStatus
+    ? { ...counts.all }
+    : { count: 0, amount: 0, earnings: 0 };
🧹 Nitpick comments (1)
apps/web/ui/partners/payout-status-badges.tsx (1)

53-59: Consider using ShieldAlert icon for consistency with commission status badges.

The commission status badges (in commission-status-badges.tsx) use ShieldAlert for the hold status, but here CircleXmark is used. For visual consistency across commissions and payouts when filtering by "On Hold", consider using the same icon.

This is a minor UI polish suggestion and can be deferred if the distinction is intentional.

@steven-tey steven-tey merged commit 229846b into main Feb 4, 2026
9 of 11 checks passed
@steven-tey steven-tey deleted the commission-on-hold branch February 4, 2026 02:06
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