-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Improve payouts UX with clear timestamps for program holding period #3146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 9 minutes and 26 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
WalkthroughRemoved includeProgramEnrollment usage from commission queries and exports; switched groupId filtering to use programEnrollment; surfaced partner.groupId in commission payloads and UI; swapped partner schema to include groupId; removed PartnerStatus and several ProgramSchema fields; adjusted payouts sorting defaults and links. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as Commissions API
participant DB as Database
participant UI as Frontend
Note over API,DB: groupId filtering now uses programEnrollment
Client->>API: GET /api/commissions?groupId=...
API->>DB: getCommissions(filters with programEnrollment.groupId)
DB-->>API: Commissions[] with programEnrollment
API->>API: For each commission, set partner.groupId = programEnrollment.groupId
API-->>Client: Commissions[] (partner enriched with groupId)
Client->>UI: render table (useGroups)
UI->>Client: groups[]
Client->>Client: resolve group by partner.groupId
Client->>Client: compute payout date (commission.createdAt + holdingPeriodDays)
Client-->>Client: render StatusBadge tooltip with group + commission data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this 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 (1)
apps/web/ui/partners/commission-status-badges.tsx (1)
1-25: New holding‑period tooltips are logically correct; tighten type imports for PrismaThe updated
CommissionTooltipDataPropsandpendingtooltip logic correctly:
- Require a
commissionobject so you can compute the payout date fromcreatedAt.- Use
group.holdingPeriodDaysto show an exact “eligible for payout on `` …” message when configured, and fall back to “shortly” otherwise.- Provide a more specific workspace link to the partner group’s settings while keeping the partner variant on the generic help article.
Two follow-ups to consider:
- Use type-only imports for Prisma models
Since
Commission,PartnerGroup, andProgramare used purely for typing in a UI module that’s consumed by client components, it’s safer to make this a type-only import to avoid accidentally bundling Prisma client code into the browser:-import { Commission, PartnerGroup, Program } from "@dub/prisma/client"; +import type { Commission, PartnerGroup, Program } from "@dub/prisma/client";
- Assumptions about data presence
The pending tooltip assumes:
commission.createdAtis a valid Date/ISO string (soaddDaysandformatDateTimeSmartbehave sensibly), and- when you expect a concrete payout date,
group.holdingPeriodDaysis configured.Those assumptions look consistent with how the call sites were wired in this PR, but are worth keeping in mind if any new callers are added later.
Also applies to: 29-37
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
apps/web/app/(ee)/api/commissions/export/route.ts(0 hunks)apps/web/app/(ee)/api/commissions/route.ts(1 hunks)apps/web/app/(ee)/api/cron/commissions/export/fetch-commissions-batch.ts(0 hunks)apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx(3 hunks)apps/web/lib/api/commissions/get-commissions-count.ts(1 hunks)apps/web/lib/api/commissions/get-commissions.ts(1 hunks)apps/web/lib/partners/get-discoverability-requirements.ts(1 hunks)apps/web/lib/zod/schemas/commissions.ts(2 hunks)apps/web/lib/zod/schemas/programs.ts(0 hunks)apps/web/ui/partners/commission-status-badges.tsx(2 hunks)packages/prisma/schema/partner.prisma(0 hunks)
💤 Files with no reviewable changes (4)
- apps/web/app/(ee)/api/cron/commissions/export/fetch-commissions-batch.ts
- apps/web/app/(ee)/api/commissions/export/route.ts
- apps/web/lib/zod/schemas/programs.ts
- packages/prisma/schema/partner.prisma
🧰 Additional context used
🧠 Learnings (3)
📚 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/zod/schemas/commissions.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/commissions/commission-table.tsx
📚 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/ui/partners/commission-status-badges.tsxapps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsxapps/web/lib/partners/get-discoverability-requirements.ts
🧬 Code graph analysis (2)
apps/web/lib/zod/schemas/commissions.ts (1)
apps/web/lib/zod/schemas/partners.ts (1)
EnrolledPartnerSchema(344-444)
apps/web/lib/partners/get-discoverability-requirements.ts (1)
packages/utils/src/functions/currency-formatter.ts (1)
currencyFormatter(7-22)
⏰ 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 (7)
apps/web/lib/partners/get-discoverability-requirements.ts (1)
95-95: LGTM! Correct usage ofcurrencyFormatter.The change correctly passes the value in cents directly to
currencyFormatter, which handles the division by 100 internally for USD. This will properly display "$10" as the commission threshold (withstripIfIntegerremoving the decimal zeros), and is consistent with the validation logic at line 22 that compares cents to cents.apps/web/lib/api/commissions/get-commissions-count.ts (1)
57-60: Group-based filtering viaprogramEnrollmentlooks correctUsing
...(groupId && { programEnrollment: { groupId } })is a clean way to scope counts to a specific group and avoids touching unaffected queries whengroupIdis undefined. No issues from a Prisma or logic perspective.apps/web/lib/api/commissions/get-commissions.ts (1)
57-61: Alignment of commissions query withprogramEnrollment/group usageRouting the
groupIdfilter throughprogramEnrollment: { groupId }and always including bothpartnerandprogramEnrollmentmatches the new data model and downstream tooltip/schema expectations. This keeps the group-aware behavior localized to the enrollment relation while keeping the invoice branch untouched.Also applies to: 64-67
apps/web/lib/zod/schemas/commissions.ts (1)
6-7: UsingEnrolledPartnerSchemafor commission partner shape is consistentSwitching
CommissionEnrichedSchema.partnertoEnrolledPartnerSchema.pick(...)and explicitly includinggroupIdaligns the commissions API with the enrolled-partner model and the new group-aware UI. This avoids schema drift and makes it clear which partner fields are guaranteed on commission responses.Also applies to: 32-40
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (1)
239-252: Passingcommissioninto status tooltip matches new tooltip contractAdding
commission: row.originalto the tooltip payload correctly satisfies the newCommissionStatusBadgessignature and enables date-based messaging while reusing existing row data.apps/web/app/(ee)/api/commissions/route.ts (1)
52-59: ConfirmprogramEnrollmentis always present for returned commissionsYou’re now deriving
partner.groupIdfromc.programEnrollment.groupId, which is what the enriched schema and tooltips expect. This is fine as long as every commission returned by this route has a non-nullprogramEnrollmentwith agroupId; otherwise,c.programEnrollment.groupIdwill throw.If there are any legacy/manual commissions without an enrollment, consider either:
- enforcing the invariant at the Prisma schema/migration level, or
- guarding here (e.g. using optional chaining and handling the no-group case explicitly).
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)
4-5: Group-aware status tooltip wiring is soundLooking up the group via
groups?.find((g) => g.id === row.original.partner.groupId)and passing it, along withcommission: row.original, cleanly connects the new group/holding‑period context into the status tooltip. The conditionalgroupIdcheck and optional chaining ensure it degrades gracefully when group data is absent.Also applies to: 54-55, 192-208
There was a problem hiding this 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 (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)
4-4: Group resolution for the new column and status tooltip is correct; consider de‑duplicating the lookupThe
useGroupshook and the newgroupcolumn correctly guard for missinggroupsand missing matches, returning"-"safely. The status cell also passes the resolvedgroup(orundefined) pluscommission,program, andworkspaceintoCommissionStatusBadges[...] .tooltip, which lines up with the new pending‑state copy that references the group’s holding period and settings URL.You’re doing
groups.find((g) => g.id === row.original.partner.groupId)in both thegroupcolumn and the status tooltip; if the groups list grows, it’s worth centralizing to a memoized lookup map keyed byidto avoid repeated linear scans and to keep the column definitions a bit leaner.@@ - const { program } = useProgram(); - const { groups } = useGroups(); + const { program } = useProgram(); + const { groups } = useGroups(); + + const groupsById = useMemo( + () => + new Map( + (groups ?? []).map((group) => [group.id, group] as const), + ), + [groups], + ); @@ - { - id: "group", - header: "Group", - cell: ({ row }) => { - if (!groups) return "-"; - - const group = groups.find( - (g) => g.id === row.original.partner.groupId, - ); - - if (!group) return "-"; - - return ( - <div className="flex items-center gap-2"> - <GroupColorCircle group={group} /> - <span className="truncate text-sm font-medium"> - {group.name} - </span> - </div> - ); - }, - }, + { + id: "group", + header: "Group", + cell: ({ row }) => { + if (!groups) return "-"; + + const group = groupsById.get(row.original.partner.groupId); + if (!group) return "-"; + + return ( + <div className="flex items-center gap-2"> + <GroupColorCircle group={group} /> + <span className="truncate text-sm font-medium"> + {group.name} + </span> + </div> + ); + }, + }, @@ - tooltip={badge.tooltip({ + tooltip={badge.tooltip({ variant: "workspace", program, workspace, - group: row.original.partner.groupId - ? groups?.find((g) => g.id === row.original.partner.groupId) - : undefined, + group: + row.original.partner.groupId && groups + ? groupsById.get(row.original.partner.groupId) + : undefined, commission: row.original, })}Also applies to: 80-81, 181-201, 266-280
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
apps/web/app/(ee)/api/partner-profile/payouts/route.ts(1 hunks)apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx(1 hunks)apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx(2 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx(6 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/overview-tasks.tsx(1 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsx(3 hunks)apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/success/page-client.tsx(2 hunks)apps/web/lib/zod/schemas/partner-profile.ts(2 hunks)apps/web/lib/zod/schemas/partners.ts(0 hunks)apps/web/lib/zod/schemas/payouts.ts(1 hunks)apps/web/ui/layout/sidebar/app-sidebar-nav.tsx(1 hunks)apps/web/ui/partners/commission-status-badges.tsx(3 hunks)apps/web/ui/partners/groups/design/branding-form.tsx(2 hunks)packages/email/src/templates/partner-payout-failed.tsx(2 hunks)packages/email/src/templates/program-payout-reminder.tsx(1 hunks)packages/prisma/client.ts(0 hunks)
💤 Files with no reviewable changes (2)
- packages/prisma/client.ts
- apps/web/lib/zod/schemas/partners.ts
🧰 Additional context used
🧠 Learnings (4)
📚 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/ui/layout/sidebar/app-sidebar-nav.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/success/page-client.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsxapps/web/ui/partners/commission-status-badges.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/overview-tasks.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx
📚 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/ui/layout/sidebar/app-sidebar-nav.tsxpackages/email/src/templates/partner-payout-failed.tsxapps/web/ui/partners/commission-status-badges.tsx
📚 Learning: 2025-06-18T20:26:25.177Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2538
File: apps/web/ui/partners/overview/blocks/commissions-block.tsx:16-27
Timestamp: 2025-06-18T20:26:25.177Z
Learning: In the Dub codebase, components that use workspace data (workspaceId, defaultProgramId) are wrapped in `WorkspaceAuth` which ensures these values are always available, making non-null assertions safe. This is acknowledged as a common pattern in their codebase, though not ideal.
Applied to files:
apps/web/ui/partners/groups/design/branding-form.tsx
📚 Learning: 2025-07-11T16:28:55.693Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2635
File: packages/prisma/schema/payout.prisma:24-25
Timestamp: 2025-07-11T16:28:55.693Z
Learning: In the Dub codebase, multiple payout records can now share the same stripeTransferId because payouts are grouped by partner and processed as single Stripe transfers. This is why the unique constraint was removed from the stripeTransferId field in the Payout model - a single transfer can include multiple payouts for the same partner.
Applied to files:
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx
🧬 Code graph analysis (4)
apps/web/ui/partners/groups/design/branding-form.tsx (1)
apps/web/lib/types.ts (1)
PartnerGroupProps(581-583)
apps/web/app/(ee)/api/partner-profile/payouts/route.ts (2)
apps/web/lib/auth/partner.ts (1)
withPartnerProfile(47-175)apps/web/lib/zod/schemas/partner-profile.ts (1)
partnerProfilePayoutsQuerySchema(198-200)
apps/web/lib/zod/schemas/partner-profile.ts (1)
apps/web/lib/zod/schemas/payouts.ts (1)
payoutsQuerySchema(26-36)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (5)
packages/ui/src/timestamp-tooltip.tsx (1)
TimestampTooltip(28-50)apps/web/ui/partners/partner-row-item.tsx (1)
PartnerRowItem(127-172)packages/ui/src/tooltip.tsx (1)
Tooltip(66-117)apps/web/ui/partners/commission-status-badges.tsx (1)
CommissionStatusBadges(27-129)packages/ui/src/table/table.tsx (1)
useTable(51-243)
⏰ 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 (23)
apps/web/ui/partners/groups/design/branding-form.tsx (2)
9-9: LGTM!The import addition is necessary for the type change in
BrandingFormDataand is correctly formatted.
40-43: LGTM! Type definition now aligns with actual usage.This change correctly updates
BrandingFormDatato source branding fields fromPartnerGroupPropsinstead ofProgramProps, which aligns with how the form actually accesses these fields from thegroupobject (lines 177-179).apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx (2)
36-36: LGTM: Default sort improves UX.Changing the default sort from "periodEnd" to "initiatedAt" aligns with the PR's goal of improving payout UX with clear timestamps. The "Initiated" column includes helpful tooltips explaining the payout timeline.
67-71: Verify removal of Period column sortability.The "Period" column (periodEnd) is still displayed in the table but has been removed from the sortableColumns array. Users may expect to sort by this column since it's prominently displayed. Please confirm this is intentional.
If removing sortability is intentional, consider whether users will find this confusing, especially since Period is the first column shown.
Also applies to: 181-181
packages/email/src/templates/program-payout-reminder.tsx (1)
92-92: LGTM: Consistent URL simplification.Removing the explicit
sortBy=amountparameter is consistent with the PR's approach of relying on default sorting behavior at the destination.apps/web/lib/zod/schemas/partner-profile.ts (1)
197-200: LGTM: Partner-specific sort default.The new
partnerProfilePayoutsQuerySchemaappropriately overrides the default sortBy to "initiatedAt" for partner-facing payout views, aligning with the UX improvements in the partner payout table.apps/web/ui/layout/sidebar/app-sidebar-nav.tsx (1)
220-220: LGTM: Consistent navigation URL.Removing the
sortBy=amountparameter from the Payouts navigation link is consistent with the PR's approach of relying on default sorting at the destination page.apps/web/app/(ee)/api/partner-profile/payouts/route.ts (1)
3-4: LGTM: Correct schema for partner context.Switching to
partnerProfilePayoutsQuerySchemaensures the partner-facing API route uses the appropriate default sortBy value ("initiatedAt"), consistent with the partner UX improvements.Also applies to: 12-12
apps/web/app/(ee)/app.dub.co/invoices/[invoiceId]/partner-payout-invoice.tsx (1)
401-401: LGTM: Consistent URL in PDF invoice.Removing the
sortBy=amountparameter from the payouts link in the PDF invoice aligns with the PR's URL simplification pattern.apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsx (3)
59-59: LGTM: Appropriate default for program view.Defaulting to "amount" sorting for the program owner's payout table makes sense, as program owners likely want to prioritize payouts by size. This differs appropriately from the partner view which defaults to "initiatedAt".
108-111: Verify removal of Period column sortability.Similar to the partner payout table, the "Period" column (periodEnd) is displayed but removed from sortableColumns. This change appears in multiple payout tables, suggesting it may be intentional, but please confirm this design decision is consistent with user expectations.
Also applies to: 233-233
343-343: LGTM: Consistent tooltip link.Removing the
sortBy=amountparameter from the tooltip link is consistent with the PR's URL simplification approach.packages/email/src/templates/partner-payout-failed.tsx (1)
119-119: LGTM: Consistent email links.Removing the
sortBy=amountparameter from both payout links in the email template is consistent with the PR's URL simplification pattern.Also applies to: 130-130
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/overview-tasks.tsx (1)
39-39: LGTM! Removal of redundant sortBy parameter.The removal of
sortBy=amountis appropriate since the schema now defaults to sorting by amount (as updated inapps/web/lib/zod/schemas/payouts.tsline 33).apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/success/page-client.tsx (1)
79-79: LGTM! Consistent removal of redundant sortBy parameter.Both navigation links now correctly omit
sortBy=amountsince it matches the schema default.Also applies to: 98-98
apps/web/ui/partners/commission-status-badges.tsx (4)
1-15: LGTM! Proper imports for date calculations.The new imports support computing exact payout dates based on holding periods. The types and utilities are appropriate for the functionality being added.
19-24: LGTM! Extended type definitions for payout date calculations.The addition of
slugto the group type andcreatedAtfrom Commission enables the tooltip to display exact payout dates and link to group settings.
50-50: LGTM! Consistent removal of redundant sortBy parameter.The removal of
&sortBy=amountaligns with the schema changes and is consistent with other files in this PR.
35-36: Date calculation logic is correct and handles timezone and boundary cases appropriately.Verification confirms:
- The
addDaysfunction from date-fns properly handles date arithmetic- The conditional check
data.group?.holdingPeriodDaysensures the calculation only executes when the value exists- The
formatDateTimeSmartfunction usestoLocaleDateStringand properly handles year display based on the current date- Edge cases are properly handled: when
holdingPeriodDaysis 0, the date shows as today (correct); when undefined/null, it displays "shortly"- Timezone handling is appropriate: JavaScript's Date object automatically manages UTC-to-local conversion for display purposes
The code follows best practices with standard libraries and doesn't require fixes. The suggestion to add unit tests (if desired) would be optional for improving test coverage, but the implementation itself is sound.
apps/web/lib/zod/schemas/payouts.ts (1)
33-33: Schema change verified—no breaking references remain in the codebase.The removal of
"createdAt"and"periodEnd"from the sortBy enum is properly implemented:
- Both UI components (
payout-table.tsxin partner and program dashboards) use only the new enum values:["amount", "initiatedAt", "paidAt"]- No code references the removed sortBy options
- The
initiatedAtfield is correctly wired into sortable columns and display logic in both dashboards- Data model fields (
createdAt,periodEnd) remain unchanged—only the sortBy enum was updated- A backfill migration (
backfill-payout-initiated-at.ts) exists to populate the new fieldapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (3)
43-63: Column visibility + dynamic defaults are wired cleanly; just ensureuseColumnVisibilityreacts as expectedThe
commissionsColumnsconfig,defaultVisibleColumnsderived fromprogram.primaryRewardEvent, and theuseColumnVisibility("commissions-table-columns", …)integration withuseTableall look consistent. Themenucolumn is correctly forced to stay visible viaenableHiding: falseand the final.filteragainstcommissionsColumns.allkeeps things sane.One thing to double‑check: when
programtransitions fromundefinedto a loaded"sale"program,defaultVisibleColumnschanges to include"amount". This relies onuseColumnVisibilityhonoring runtime changes to itsdefaultVisibleoption; otherwise, the"amount"column may remain hidden by default even for sale programs on first load. If the hook does not already handle this, consider an internaluseEffectthere keyed ondefaultVisibleto resync initial visibility when no user overrides are stored.Also applies to: 107-127, 289-297, 301-322
129-144:TimestampTooltipusage on the Date column aligns well with the UX goalWrapping
createdAtinTimestampTooltipwithrows={["local", "utc", "unix"]}, a small delay, and keeping the human‑readableformatDateTimeSmartas the visible text is a good balance of clarity and detail. This should make the holding‑period timing much clearer without cluttering the table.
224-260: Commission cell tooltip and styling logic look solidThe commission cell cleanly derives
earnings, usesCLAWBACK_REASONS_MAPto surface a human‑readable clawback reason whendescriptionis present, and falls back to the raw description string otherwise. The Tooltip integration and conditional red styling for negative earnings are applied consistently in both the tooltip and non‑tooltip branches, and thecursor-help+ dotted underline give a clear affordance without impacting non‑clawback rows.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this 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 (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx (1)
144-157: Extract duplicateaddBusinessDayscalculation to variable; clarify holiday handling limitation.The expression
addBusinessDays(row.original.initiatedAt, 5)is computed twice (lines 146 and 150). Extract it to a variable for readability and efficiency:) : row.original.initiatedAt ? ( + (() => { + const estimatedDate = addBusinessDays(row.original.initiatedAt, 5); + return ( <Tooltip - content={`This payout is estimated to be processed on \`${formatDateTimeSmart(addBusinessDays(row.original.initiatedAt, 5), { month: "short" })}\` (after 5 business days)`} + content={`This payout is estimated to be processed on \`${formatDateTimeSmart(estimatedDate, { month: "short" })}\` (after 5 business days)`} > <span className="hover:text-content-emphasis text-content-muted flex items-center gap-1 underline decoration-dotted underline-offset-2"> <CircleHalfDottedClock className="size-3.5 shrink-0" />{" "} - {formatDateSmart(addBusinessDays(row.original.initiatedAt, 5), { + {formatDateSmart(estimatedDate, { month: "short", })} </span> </Tooltip> + ); + })() ) : (Note: The
addBusinessDaysfunction from date-fns skips weekends (Mon–Fri) but does not handle public holidays—there's no built-in holiday list. The email template claims payouts are "excluding weekends and public holidays," but the code only excludes weekends. Consider adding holiday logic or updating the messaging to match the actual behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
apps/web/app/(ee)/api/cron/payouts/charge-succeeded/queue-external-payouts.ts(1 hunks)apps/web/app/(ee)/api/cron/payouts/process/updates/route.ts(1 hunks)apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx(4 hunks)packages/email/package.json(1 hunks)packages/email/src/templates/partner-payout-confirmed.tsx(6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.
Applied to files:
packages/email/src/templates/partner-payout-confirmed.tsxapps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx
📚 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/cron/payouts/process/updates/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx (1)
packages/ui/src/tooltip.tsx (1)
Tooltip(66-117)
⏰ 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 (8)
apps/web/app/(ee)/api/cron/payouts/process/updates/route.ts (1)
111-111: LGTM!The addition of
initiatedAtto the email payload is consistent with the template changes and properly passes the timestamp from the payout record.apps/web/app/(ee)/api/cron/payouts/charge-succeeded/queue-external-payouts.ts (1)
136-136: LGTM!Consistent addition of
initiatedAtto the external payout email payload.packages/email/src/templates/partner-payout-confirmed.tsx (3)
1-6: LGTM!The new imports (
formatDateTimeSmartandaddBusinessDays) are properly utilized in the estimated arrival date feature.Also applies to: 20-20
50-50: LGTM!The nullable
initiatedAtfield is appropriate and aligns with the conditional rendering logic.
123-130: LGTM!The wording changes improve clarity by using "transferred" and explicitly referencing "Stripe Express account."
apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsx (3)
21-34: LGTM! Imports are correctly added for the new feature.The new imports (
CircleHalfDottedClock,formatDateTimeSmart, andaddBusinessDays) are all utilized in the estimated payout date functionality.
43-43: Inconsistency between code and AI-generated summary.The default
sortByis set to"initiatedAt", but the AI summary states it changed to"amount". The code takes precedence, so this appears to be an inaccuracy in the summary.
199-199: LGTM! sortableColumns correctly updated.The change from
["periodEnd", "amount", "paidAt"]to["amount", "initiatedAt", "paidAt"]aligns well with the new focus on initiated timestamps and improves the payout UX as intended.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Summary by CodeRabbit
New Features
Refactor
UI/Behavior
Chore
✏️ Tip: You can customize this high-level summary in your review settings.