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

Skip to content

Conversation

@TWilson023
Copy link
Collaborator

@TWilson023 TWilson023 commented Dec 16, 2025

  • New customer page UIs (both workspace- and partner- sides)
  • Customers index + individual pages under program

Summary by CodeRabbit

  • New Features

    • Dedicated Sales and Partner Earnings pages per customer, new customer layouts, and a CustomerStats panel.
  • Improvements

    • Filter customers by programId; program-aware customer table, tabs, and updated customer links/paths; new program-scoped sidebar "Customers"; refreshed table and card styling; improved referral link behavior.
  • Removals / Migration

    • Legacy customer client page removed and replaced by new layout/page-client components.
  • Bug Fixes / UX

    • Redirects default customer URLs to Sales view; improved loading and empty states.

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

@vercel
Copy link
Contributor

vercel bot commented Dec 16, 2025

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

Project Deployment Review Updated (UTC)
dub Ready Ready Preview Dec 18, 2025 10:19pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

Adds optional programId query handling to customer APIs and schema, remaps many customer links/pages to program-scoped routes, introduces modular customer layouts/pages (sales, earnings) for program and non-program contexts, adds PageNavTabs and related UI, updates redirects to point detail routes to their sales subpage, and applies multiple UI/styling tweaks.

Changes

Cohort / File(s) Summary
API & Schema
apps/web/app/(ee)/api/customers/route.ts, apps/web/app/(ee)/api/customers/count/route.ts, apps/web/lib/zod/schemas/customers.ts
Add optional programId to query schema and apply link.programId filtering in Prisma where clauses for customer listing/count endpoints.
Routing & Redirects
apps/web/lib/middleware/utils/app-redirect.ts
Add regex redirects that map customer detail routes to their /sales subpages for both program and non-program paths.
Removed legacy customer page
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/page-client.tsx, .../page.tsx
Remove previous monolithic client page and wrapper.
New non-program customer pages
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx, .../sales/page-client.tsx, .../sales/page.tsx, .../earnings/page-client.tsx, .../earnings/page.tsx
Add modular layout and client pages (sales, earnings) with SWR data fetching, memoized tables, stats/details/tabs, and activity sections.
New program-scoped customer pages
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page.tsx, .../page-client.tsx, .../[customerId]/layout.tsx, .../[customerId]/sales/page-client.tsx, .../[customerId]/sales/page.tsx, .../[customerId]/earnings/page-client.tsx, .../[customerId]/earnings/page.tsx
Add program-scoped customers listing, layout, sales, and earnings pages mirroring non-program variants; client components fetch program-aware data.
Customer table & details UI
apps/web/ui/customers/customer-table/customer-table.tsx, apps/web/ui/customers/customer-details-column.tsx, apps/web/ui/customers/customer-tabs.tsx, apps/web/ui/customers/customer-stats.tsx, apps/web/ui/customers/customer-sales-table.tsx, apps/web/ui/customers/customer-partner-earnings-table.tsx
CustomerTable gains query + isProgramPage props and routes links to program paths when applicable; CustomerDetailsColumn now uses CustomerEnriched and accepts isProgramPage/workspaceSlug; add CustomerTabs, CustomerStats; multiple styling/padding adjustments.
Partner / Fraud link updates
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx, apps/web/ui/partners/fraud-risks/*
Update various customer links to program-scoped paths (/{slug}/program/customers/{id}).
Page nav & Sidebar
apps/web/ui/layout/page-nav-tabs.tsx, apps/web/ui/layout/sidebar/app-sidebar-nav.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/partner-nav.tsx
Add PageNavTabs component and types; add "Customers" under program insights; replace inline partner-nav with PageNavTabs usage.
Analytics & small UI tweaks
apps/web/ui/analytics/use-analytics-filters.tsx, apps/web/ui/customers/customer-activity-list.tsx, packages/ui/src/timestamp-tooltip.tsx
Change analytics permalink generation for program pages to /{slug}/program/customers/{id}; add rounded corners to no-activity placeholder; refactor timestamp tooltip table layout.
Other layout/composition changes
apps/web/app/(ee)/partners.dub.co/.../customers/[customerId]/page-client.tsx, .../page.tsx
Rework localized partner customer page layout to use PageContent/PageWidthWrapper and adjust header/content composition.
Types / SWR tweaks
apps/web/lib/swr/use-partner-customer.ts
Broaden SWR return type to include optional `name?: string

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas needing extra attention:
    • Prisma where composition for combined filters including link.programId.
    • Redirect regex ordering and potential unintended matches.
    • Prop-type and call-site updates for CustomerDetailsColumn (CustomerEnriched, new props).
    • SWR cache key consistency and conditional "total" fetch logic in memoized tables.
    • PageNavTabs replacement to ensure accessibility and scroll/animation behavior.

Possibly related PRs

Suggested reviewers

  • steven-tey

Poem

πŸ‡ I hopped through code with nimble paws,

tucked programId into customers' laws.
Tabs now point where program paths prevail,
tiny redirects guide them down the sales trail.
A carrot patch β€” neat commits, no flaws.

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 'Customers pages updates' accurately reflects the main changes: new customer page UI and customers index/individual pages under the program section.
✨ 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 customers-updates

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.

@TWilson023 TWilson023 marked this pull request as ready for review December 18, 2025 17:08
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: 10

Caution

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

⚠️ Outside diff range comments (1)
apps/web/ui/customers/customer-partner-earnings-table.tsx (1)

63-69: Minor inconsistency in border-radius styling.

The loading skeleton (line 63) no longer has rounded-lg, but the "no earnings" empty state (line 65) still uses rounded-lg. Consider adding rounded-lg back to the loading skeleton for visual consistency between states.

πŸ”Ž Apply this diff to fix the inconsistency:
-        <div className="flex h-32 w-full animate-pulse border border-transparent bg-neutral-100" />
+        <div className="flex h-32 w-full animate-pulse rounded-lg border border-transparent bg-neutral-100" />
🧹 Nitpick comments (6)
apps/web/ui/customers/customer-stats.tsx (1)

49-72: LGTM with optional cleanup.

The component handles loading/error states well. Minor note: href and target are passed even when rendering a div (lines 54-55), which is harmless but slightly unnecessary.

πŸ”Ž Optional: Conditionally spread link-specific props
             <As
               key={label}
-              href={href ?? "#"}
-              target="_blank"
+              {...(href && { href, target: "_blank" })}
               className={cn(
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx (1)

34-67: Add displayName for better debugging.

The memoized SalesTable component lacks a displayName, which will show as "Anonymous" in React DevTools, making debugging harder.

πŸ”Ž Apply this diff to add displayName:
 const SalesTable = memo(({ customerId }: { customerId: string }) => {
   // ... component body
 });
+
+SalesTable.displayName = "SalesTable";
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx (1)

70-107: Add displayName to memoized component for better debugging.

The memo() wrapper strips the function name in React DevTools. Consider adding a displayName:

 const PartnerEarningsTable = memo(({ customerId }: { customerId: string }) => {
   // ... component body
 });
+PartnerEarningsTable.displayName = "PartnerEarningsTable";
apps/web/ui/customers/customer-tabs.tsx (1)

1-7: Consider adding "use client" directive.

This component uses React hooks (useParams, useMemo) and the useWorkspace hook. While it may work through client component promotion if only imported by client components, adding the "use client" directive explicitly would make the component's runtime requirements clear and prevent potential issues if imported from a Server Component in the future.

Based on learnings, if the component is only imported by Client Components, this can be safely deferred.

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

1-107: Significant code duplication with non-program earnings page.

This file is identical to apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx. Consider extracting the shared logic into a reusable component or hook to avoid maintaining duplicate code.

For example, create a shared CustomerEarningsContent component in @/ui/customers/ that both page-clients can import:

// apps/web/ui/customers/customer-earnings-content.tsx
export function CustomerEarningsContent({ customerId, slug }: Props) {
  // Shared earnings logic and UI
}

Then both page-clients would simply wrap this component with their respective context.

The same issues noted in the non-program version also apply here:

  • Guard slug before redirect (line 24)
  • Guard workspaceId in SWR key (line 78)
  • Add displayName to memoized component (line 107)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx (1)

34-67: Consider adding displayName for the memoized component.

The memoized SalesTable component lacks a displayName, which can make debugging in React DevTools harder.

πŸ”Ž Suggested fix
 const SalesTable = memo(({ customerId }: { customerId: string }) => {
   // ... component body
 });
+
+SalesTable.displayName = "SalesTable";
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between a8f7288 and fe2e63b.

πŸ“’ Files selected for processing (34)
  • apps/web/app/(ee)/api/customers/count/route.ts (2 hunks)
  • apps/web/app/(ee)/api/customers/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/page-client.tsx (0 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/page.tsx (0 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/partner-nav.tsx (3 hunks)
  • apps/web/lib/middleware/utils/app-redirect.ts (1 hunks)
  • apps/web/lib/zod/schemas/customers.ts (1 hunks)
  • apps/web/ui/analytics/use-analytics-filters.tsx (1 hunks)
  • apps/web/ui/customers/customer-activity-list.tsx (1 hunks)
  • apps/web/ui/customers/customer-details-column.tsx (4 hunks)
  • apps/web/ui/customers/customer-partner-earnings-table.tsx (5 hunks)
  • apps/web/ui/customers/customer-sales-table.tsx (4 hunks)
  • apps/web/ui/customers/customer-stats.tsx (1 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (7 hunks)
  • apps/web/ui/customers/customer-tabs.tsx (1 hunks)
  • apps/web/ui/layout/page-nav-tabs.tsx (1 hunks)
  • apps/web/ui/layout/sidebar/app-sidebar-nav.tsx (1 hunks)
  • apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx (1 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-matching-customer-email-table.tsx (1 hunks)
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx (1 hunks)
  • packages/ui/src/timestamp-tooltip.tsx (1 hunks)
πŸ’€ Files with no reviewable changes (2)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/page-client.tsx
🧰 Additional context used
🧠 Learnings (10)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
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.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
πŸ“š 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/customers/[customerId]/earnings/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page.tsx
  • apps/web/ui/layout/page-nav-tabs.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page.tsx
  • apps/web/ui/customers/customer-tabs.tsx
  • apps/web/ui/customers/customer-details-column.tsx
  • apps/web/ui/layout/sidebar/app-sidebar-nav.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/partner-nav.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx
πŸ“š Learning: 2025-12-08T09:44:28.429Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3200
File: apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts:55-73
Timestamp: 2025-12-08T09:44:28.429Z
Learning: In apps/web/lib/api/fraud/detect-duplicate-payout-method-fraud.ts, the fraud event creation logic intentionally generates self-referential fraud events (where partnerId equals duplicatePartnerId) for partners with duplicate payout methods. This is by design to create raw events for all partners involved in a duplicate payout method scenario, regardless of whether they reference themselves.

Applied to files:

  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx
  • apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-matching-customer-email-table.tsx
πŸ“š Learning: 2025-09-18T16:33:17.719Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2858
File: apps/web/ui/partners/partner-application-tabs.tsx:1-1
Timestamp: 2025-09-18T16:33:17.719Z
Learning: When a React component in Next.js App Router uses non-serializable props (like setState functions), adding "use client" directive can cause serialization warnings. If the component is only imported by Client Components, it's better to omit the "use client" directive to avoid these warnings while still getting client-side execution through promotion.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx
πŸ“š Learning: 2025-08-25T21:03:24.285Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/bounty-card.tsx:1-1
Timestamp: 2025-08-25T21:03:24.285Z
Learning: In Next.js App Router, Server Components that use hooks can work without "use client" directive if they are only imported by Client Components, as they get "promoted" to run on the client side within the Client Component boundary.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx
πŸ“š Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/lib/middleware/utils/app-redirect.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/ui/customers/customer-details-column.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.tsx
πŸ“š Learning: 2025-08-25T20:58:39.467Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx:537-542
Timestamp: 2025-08-25T20:58:39.467Z
Learning: The useTable hook includes an isClickOnInteractiveChild check in the onRowClick handler that prevents row click events from firing when clicking on interactive child elements like buttons, making stopPropagation() calls unnecessary in those cases.

Applied to files:

  • apps/web/ui/customers/customer-table/customer-table.tsx
  • packages/ui/src/timestamp-tooltip.tsx
πŸ“š 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/ui/analytics/use-analytics-filters.tsx
🧬 Code graph analysis (13)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx (1)
  • CustomerEarningsPageClient (15-68)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx (1)
  • CustomerSalesPageClient (13-32)
apps/web/ui/customers/customer-sales-table.tsx (1)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (7-22)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/page-client.tsx (3)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/ui/customers/customer-table/customer-table.tsx (1)
  • CustomerTable (71-440)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/page-client.tsx (1)
  • CustomersPageClient (3-5)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page-client.tsx (5)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/lib/types.ts (2)
  • CustomerEnriched (436-436)
  • CommissionResponse (451-451)
packages/utils/src/constants/misc.ts (1)
  • OG_AVATAR_URL (29-29)
apps/web/lib/constants/misc.ts (1)
  • CUSTOMER_PAGE_EVENTS_LIMIT (2-2)
apps/web/ui/customers/customer-partner-earnings-table.tsx (1)
  • CustomerPartnerEarningsTable (12-125)
apps/web/ui/customers/customer-stats.tsx (3)
apps/web/lib/types.ts (1)
  • CustomerEnriched (436-436)
packages/utils/src/functions/currency-formatter.ts (1)
  • currencyFormatter (7-22)
packages/email/src/react-email.d.ts (1)
  • Link (14-14)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page-client.tsx (1)
  • CustomerEarningsPageClient (15-68)
apps/web/ui/layout/page-nav-tabs.tsx (1)
packages/ui/src/icons/index.tsx (1)
  • Icon (82-82)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (5)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/lib/types.ts (2)
  • CustomerEnriched (436-436)
  • CustomerActivityResponse (529-531)
apps/web/ui/layout/page-content/index.tsx (1)
  • PageContent (10-39)
apps/web/ui/customers/customer-tabs.tsx (1)
  • CustomerTabs (8-48)
apps/web/ui/customers/customer-activity-list.tsx (1)
  • CustomerActivityList (110-154)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx (1)
  • CustomerSalesPageClient (13-32)
apps/web/ui/customers/customer-tabs.tsx (3)
apps/web/lib/types.ts (1)
  • CustomerEnriched (436-436)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/ui/layout/page-nav-tabs.tsx (1)
  • PageNavTabs (30-141)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/partner-nav.tsx (1)
apps/web/ui/layout/page-nav-tabs.tsx (1)
  • PageNavTabs (30-141)
apps/web/ui/customers/customer-partner-earnings-table.tsx (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 (38)
apps/web/ui/layout/sidebar/app-sidebar-nav.tsx (1)

290-294: LGTM!

The new "Customers" navigation item is correctly placed in the program insights section and follows the established patterns. The UserCheck icon is properly imported, and the route /${slug}/program/customers aligns with the PR's objective to introduce program-scoped customer pages.

Based on learnings, this incremental approach to updating routes (prioritizing visible navigation first) is consistent with your preferred workflow.

packages/ui/src/timestamp-tooltip.tsx (1)

151-152: Good semantic HTML structure.

The explicit <tbody> wrapper properly structures the table according to HTML5 standards.

apps/web/ui/customers/customer-activity-list.tsx (1)

120-122: LGTM!

The addition of rounded-lg improves visual consistency with the loading state and other card-based UI components in the customer details flow.

apps/web/lib/zod/schemas/customers.ts (1)

41-41: LGTM!

The programId field follows the established pattern for optional filter parameters and is correctly inherited by getCustomersCountQuerySchema for consistent filtering across endpoints.

apps/web/app/(ee)/api/customers/count/route.ts (1)

36-40: Verify behavior for customers without links.

The nested link: { programId } filter will exclude customers with linkId: null. If this is the intended behavior for program-scoped views (only showing customers acquired through program links), this looks correct.

apps/web/ui/analytics/use-analytics-filters.tsx (1)

766-770: LGTM!

The permalink update correctly aligns with the new program-scoped routing structure (/{slug}/program/customers/...), maintaining consistency with the broader route restructuring in this PR.

apps/web/ui/customers/customer-partner-earnings-table.tsx (2)

36-41: LGTM!

Cell renderers are appropriately simplified to concise single-line arrow functions.


79-79: LGTM!

Padding and layout updates improve visual consistency with the rest of the table UI.

Also applies to: 96-96, 107-119

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page-client.tsx (2)

22-22: Verify redirect behavior during render.

Using redirect() during the render phase is valid in Next.js App Router but throws an exception to interrupt rendering. Ensure this doesn't cause issues with any parent error boundaries or Suspense boundaries.


45-55: LGTM!

Smart optimization to conditionally fetch total sales count only when the initial data reaches the limit, avoiding unnecessary API calls.

apps/web/ui/layout/page-nav-tabs.tsx (2)

72-104: LGTM!

The tab rendering with animated selection indicator using motion is well-implemented. The pathname.endsWith() approach for selection detection is reasonable for the controlled tab IDs used in this context.


108-133: LGTM!

QuickLinks section is cleanly implemented with external link indicators and proper target handling.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/partner-nav.tsx (1)

5-5: LGTM! Clean refactoring to shared PageNavTabs component.

The refactoring successfully consolidates the inline navigation logic into the reusable PageNavTabs component, reducing code duplication and improving maintainability. The quicklinks β†’ quickLinks rename also improves naming consistency.

Also applies to: 15-15, 62-62, 81-86

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page.tsx (1)

1-5: LGTM! Follows the standard page/page-client separation pattern.

This wrapper correctly delegates to the client component for data fetching and rendering, which is the expected Next.js pattern for this type of page structure.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/sales/page.tsx (1)

1-5: LGTM! Consistent with the established page wrapper pattern.

This follows the same clean separation pattern used in the non-program-scoped sales page, maintaining consistency across the routing structure.

apps/web/ui/customers/customer-sales-table.tsx (1)

61-61: LGTM! UI polish improves table spacing and formatting.

The padding updates (p-2 β†’ px-4 py-3) provide better vertical and horizontal spacing, and the simplified amount cell formatting using currencyFormatter directly is cleaner than the previous implementation.

Also applies to: 108-108, 125-125, 136-140

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

1-20: LGTM! Well-structured page with helpful user guidance.

The page composition is clean, using appropriate layout wrappers and providing clear context through the title and help link. The separation of concerns between the page wrapper and client component is well executed.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/earnings/page.tsx (1)

1-5: LGTM! Follows the consistent page wrapper pattern.

Clean implementation that maintains consistency with the other customer detail pages.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/earnings/page.tsx (1)

1-5: LGTM! Consistent wrapper implementation.

This completes the consistent page structure pattern across all customer detail pages (both general and program-scoped).

apps/web/app/(ee)/api/customers/route.ts (1)

48-48: [Rewritten review comment]
[Classification tag]

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

6-14: LGTM!

The component correctly wires up the program-scoped customer table by passing defaultProgramId from the workspace context and setting isProgramPage to enable program-specific behavior. The use of || undefined properly handles falsy values for the query parameter.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx (2)

40-106: Well-structured layout implementation.

The two-column responsive grid with proper ordering, loading states, and activity section is cleanly organized. The conditional rendering for customer name skeleton and the activity list integration follow good UX patterns.


37-38: Consider handling the loading state before redirecting.

The redirect fires immediately when customerError.status === 404, but if workspaceSlug is still loading (undefined), this could redirect to /undefined/customers. Consider adding a guard:

-  if (customerError && customerError.status === 404)
-    redirect(`/${workspaceSlug}/customers`);
+  if (customerError && customerError.status === 404 && workspaceSlug)
+    redirect(`/${workspaceSlug}/customers`);
β›” Skipped due to learnings
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.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.
apps/web/ui/customers/customer-tabs.tsx (1)

8-47: Clean tab navigation implementation.

The conditional tab rendering based on customer's program association and the dynamic basePath for program vs non-program contexts are well-implemented. The useMemo dependencies correctly track the relevant customer properties.

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

73-82: Guard SWR key against undefined workspaceId.

The non-null assertion workspaceId! will cause the API call to be made with workspaceId=undefined as a string if the workspace hasn't loaded yet. Consider guarding the SWR key:

  const { data: commissions, isLoading: isComissionsLoading } = useSWR<
    CommissionResponse[]
  >(
-    `/api/commissions?${new URLSearchParams({
+    workspaceId &&
+      `/api/commissions?${new URLSearchParams({
        customerId,
-       workspaceId: workspaceId!,
+       workspaceId,
        pageSize: CUSTOMER_PAGE_EVENTS_LIMIT.toString(),
      })}`,
    fetcher,
  );
β›” Skipped due to learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.
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.
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/sales/page-client.tsx (1)

13-32: LGTM! Clean component structure with proper data fetching.

The component correctly uses useCustomer hook with enriched fields and handles the loading/not-found states. The redirect pattern for missing customers after loading is appropriate.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (2)

20-38: LGTM! Data fetching and error handling look correct.

The component properly chains data fetching (workspace β†’ customer β†’ activity) and handles the 404 case with a redirect. The conditional SWR key for activity ensures the request only fires when customer data is available.


40-107: Well-structured layout with proper loading states.

The responsive grid layout with loading skeletons and proper composition of child components follows good patterns. The isCustomerActivityLoading prop correctly accounts for both customer loading and activity loading states.

apps/web/ui/customers/customer-table/customer-table.tsx (1)

71-77: LGTM! Clean prop interface for the table component.

The updated function signature properly types the query prop using the Zod schema inference and provides sensible defaults.

apps/web/ui/customers/customer-details-column.tsx (4)

42-65: Clean pattern for rendering basic fields with loading states.

The basicFields array pattern with conditional inclusion based on customer data provides a clean way to handle the email/country display with proper loading skeletons.


198-212: LGTM! Proper program-aware path construction for analytics links.

The isProgramPage conditional correctly adjusts the analytics path to include /program/ when viewing from the program context.


258-301: Well-structured referral section with partner support.

The referral section cleanly handles three states: loading, with partner, and without partner. The conditional rendering and link construction are correct.


306-314: LGTM! Clean helper component for detail headings.

The DetailHeading component provides consistent styling for section headings throughout the details column.

apps/web/ui/partners/fraud-risks/commissions-on-hold-table.tsx (1)

90-90: LGTM: Customer link updated to program scope.

The update to the program-scoped customer path is consistent with the PR's routing restructure.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)

162-162: LGTM: Customer link correctly scoped to program context.

Since this table is already in the program context (/program/commissions/), the program-scoped customer path is the appropriate choice.

apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-matching-customer-email-table.tsx (1)

75-75: LGTM: Fraud event customer link updated to program scope.

The program-scoped path is appropriate for fraud detection features.

apps/web/ui/partners/fraud-risks/fraud-events-tables/fraud-paid-traffic-detected-table.tsx (2)

87-87: LGTM: Customer link updated to program scope.

The program-scoped path aligns with the fraud detection feature context.


87-87: Add redirect rules for old customer paths and deprecate or remove old routes.

The path update is correct, but migration is incomplete. Both old (/${slug}/customers/) and new (/${slug}/program/customers/) routes are currently active with fully functional pages. Configure redirects from old customer paths to new program paths, or remove the old route files. Without redirects, bookmarks and incoming links to the old path will break.

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

πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between fe2e63b and efb8fab.

πŸ“’ Files selected for processing (3)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/lib/middleware/utils/app-redirect.ts (1 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (7 hunks)
🧰 Additional context used
🧠 Learnings (4)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
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.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
πŸ“š 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/ui/customers/customer-table/customer-table.tsx
πŸ“š 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/customers/customer-table/customer-table.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx
πŸ“š Learning: 2025-11-24T09:10:12.536Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3089
File: apps/web/lib/api/fraud/fraud-rules-registry.ts:17-25
Timestamp: 2025-11-24T09:10:12.536Z
Learning: In apps/web/lib/api/fraud/fraud-rules-registry.ts, the fraud rules `partnerCrossProgramBan` and `partnerDuplicatePayoutMethod` intentionally have stub implementations that return `{ triggered: false }` because they are partner-scoped rules handled separately during partner application/onboarding flows (e.g., in detect-record-fraud-application.ts), rather than being evaluated per conversion event like other rules in the registry. The stubs exist only to satisfy the `Record<FraudRuleType, ...>` type constraint.

Applied to files:

  • apps/web/lib/middleware/utils/app-redirect.ts
🧬 Code graph analysis (2)
apps/web/ui/customers/customer-table/customer-table.tsx (2)
apps/web/lib/zod/schemas/customers.ts (1)
  • getCustomersQuerySchema (9-60)
apps/web/ui/partners/partner-row-item.tsx (1)
  • PartnerRowItem (129-178)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (8)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/lib/types.ts (2)
  • CustomerEnriched (436-436)
  • CustomerActivityResponse (529-531)
apps/web/ui/layout/page-content/index.tsx (1)
  • PageContent (10-39)
apps/web/ui/customers/customer-stats.tsx (1)
  • CustomerStats (7-78)
apps/web/ui/customers/customer-details-column.tsx (1)
  • CustomerDetailsColumn (23-304)
apps/web/ui/customers/customer-tabs.tsx (1)
  • CustomerTabs (8-48)
packages/ui/src/button.tsx (1)
  • Button (158-158)
apps/web/ui/customers/customer-activity-list.tsx (1)
  • CustomerActivityList (110-154)
⏰ 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/lib/middleware/utils/app-redirect.ts (1)

84-98: LGTM! Previous issues resolved.

The customer redirect rules are correctly implemented and follow the established pattern from the partner redirect above. The comments accurately describe the behavior (previous typo concerns about "partnerId" have been fixed), and the regex patterns appropriately validate that customer IDs start with "cus_" before redirecting to the sales subpage.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (1)

66-102: Layout structure and props look good.

The isProgramPage prop is now correctly passed to both CustomerDetailsColumn (line 72) and CustomerTabs (line 77), ensuring proper program-page URL construction. Loading states are appropriately propagated to child components.

apps/web/ui/customers/customer-table/customer-table.tsx (6)

7-9: LGTM!

The new imports are necessary for the program-scoped functionality: getCustomersQuerySchema for typing the query prop, PartnerRowItem for the partner column, and z for type inference.


53-61: LGTM!

Adding "partner" to the available columns while keeping it out of defaultVisible is appropriate since the partner column is only conditionally rendered on program pages.


71-77: LGTM!

The props interface uses schema inference for type safety, and isProgramPage defaulting to false maintains backward compatibility with existing usages.


138-150: LGTM!

The conditional routing based on isProgramPage correctly directs users to program-scoped or workspace-scoped customer detail pages.


282-283: LGTM!

The dependency array now correctly includes both isProgramPage and workspaceSlug, addressing the stale closure concern from the previous review.


99-114: LGTM!

The query prop is correctly propagated to both useCustomersCount and the SWR fetch, enabling proper filtering for program-scoped views.

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 (1)
apps/web/ui/customers/customer-table/customer-table.tsx (1)

53-63: Consider default visibility for the partner column on program pages.

The partner column is included in customersColumns.all but not in defaultVisible, meaning it will be hidden by default even when isProgramPage is true. Users would need to manually enable it via the column visibility menu.

If the partner column is a key feature of the program pages, consider conditionally adding it to the visible columns or documenting this UX choice.

πŸ”Ž Optional: conditionally show partner column by default

One approach would be to update the column visibility after initialization:

  const { columnVisibility, setColumnVisibility } = useColumnVisibility(
    "customers-table-columns",
    customersColumns,
  );

  const { pagination, setPagination } = usePagination();

  if (!canManageCustomers) columnVisibility.link = false;
+ if (isProgramPage && columnVisibility.partner === undefined) {
+   columnVisibility.partner = true;
+ }

Alternatively, you could adjust the config based on page context before passing it to the hook.

πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between efb8fab and 9606a45.

πŸ“’ Files selected for processing (2)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx
🧰 Additional context used
🧠 Learnings (4)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
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.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
πŸ“š 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/customers/customer-table/customer-table.tsx
πŸ“š Learning: 2025-08-16T11:14:00.667Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2754
File: apps/web/lib/partnerstack/schemas.ts:47-52
Timestamp: 2025-08-16T11:14:00.667Z
Learning: The PartnerStack API always includes the `group` field in partner responses, so the schema should use `.nullable()` rather than `.nullish()` since the field is never omitted/undefined.

Applied to files:

  • apps/web/ui/customers/customer-table/customer-table.tsx
πŸ“š Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.

Applied to files:

  • apps/web/ui/customers/customer-table/customer-table.tsx
🧬 Code graph analysis (1)
apps/web/ui/customers/customer-table/customer-table.tsx (2)
apps/web/lib/zod/schemas/customers.ts (1)
  • getCustomersQuerySchema (9-60)
apps/web/ui/partners/partner-row-item.tsx (1)
  • PartnerRowItem (129-178)
⏰ 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/ui/customers/customer-table/customer-table.tsx (2)

158-161: All previous review feedback addressed! βœ…

Great work addressing the prior review comments:

  • The debug console.log statement has been removed
  • workspaceSlug is now included in the useMemo dependency array (line 285)
  • The partner column now has a proper null guard (lines 158-161)

Also applies to: 285-285


71-77: LGTM: Clean component API extension.

The new props are well-typed using Zod schema inference, and the isProgramPage default ensures backward compatibility for existing usages.

@TWilson023 TWilson023 changed the title Customers pages updates [DRAFT] Customers pages updates Dec 18, 2025
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

🧹 Nitpick comments (4)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (2)

56-63: Consider removing or completing the commented-out link code.

There's commented-out code for what appears to be a back navigation link (href, aria-label, title, and hover styles). Since this is a draft PR, please either:

  1. Remove the comments if the link functionality is intentionally omitted
  2. Restore the link using <Link> if back navigation is desired

Leaving commented-out code in production can reduce clarity.


147-184: Consider adding displayName to the memoized component.

The EarningsTable component uses memo but lacks a displayName. Adding one improves debugging in React DevTools.

πŸ”Ž Proposed fix
 const EarningsTable = memo(({ customerId }: { customerId: string }) => {
   // ... component body
 });
+
+EarningsTable.displayName = "EarningsTable";
apps/web/ui/customers/customer-stats.tsx (1)

49-60: Consider conditional prop spreading for polymorphic component.

When As is "div", the href and target props are passed but have no effect. While harmless, conditionally spreading these props only when As is Link would be more semantically precise.

πŸ”Ž Proposed refactor
           const As = href ? Link : "div";
           return (
             <As
               key={label}
-              href={href ?? "#"}
-              target="_blank"
+              {...(href ? { href, target: "_blank" } : {})}
               className={cn(
                 "group relative flex flex-col bg-white p-3",
                 href && "transition-colors duration-150 hover:bg-neutral-50",
               )}
             >
apps/web/ui/customers/customer-details-column.tsx (1)

328-331: Heading level changed from h2 to h3.

The DetailHeading component now renders an h3 instead of h2. Ensure this change aligns with the document outline hierarchy in the contexts where this component is used.

πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 20fe549 and 63a4d2a.

πŸ“’ Files selected for processing (6)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (2 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (1 hunks)
  • apps/web/ui/customers/customer-details-column.tsx (4 hunks)
  • apps/web/ui/customers/customer-stats.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/customers/[customerId]/layout.tsx
🧰 Additional context used
🧠 Learnings (4)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
πŸ“š 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/customers/[customerId]/layout.tsx
  • apps/web/ui/customers/customer-details-column.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.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/customers/customer-stats.tsx
πŸ“š 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/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx
🧬 Code graph analysis (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page.tsx (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (1)
  • ProgramCustomerPageClient (21-145)
⏰ 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)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (1)

78-142: Clean responsive layout structure.

The two-column responsive layout using @container queries is well-structured. The order swapping (@3xl/page:order-1 and @3xl/page:order-2) ensures appropriate visual hierarchy across viewport sizes.

apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page.tsx (1)

1-5: LGTM!

Clean simplification. Moving the layout wrappers into page-client.tsx keeps this server component as a thin shell while consolidating rendering logic in the client component.

apps/web/ui/customers/customer-details-column.tsx (2)

246-251: Inconsistent fallback behavior between analytics and UTM links.

The UTM parameter links only render when workspaceSlug is present (line 248), while the analytics links above (line 207) fall back to /programs/${programSlug}/ when workspaceSlug is undefined. Is this intentional? Consider whether UTM links should also support the program-slug fallback for consistency.


300-313: LGTM! Clear fallback logic for referral links.

The conditional href construction appropriately handles both workspace (direct link) and program (analytics query) contexts, with a sensible fallback to undefined when neither is available.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/customers/[customerId]/layout.tsx (2)

37-38: Past review issues have been resolved.

The 404 redirect now correctly targets the program-scoped customers route (line 38), and isProgramPage is properly passed to CustomerTabs (line 78). These changes address the previous review feedback.

Also applies to: 78-78


89-92: This link to the workspace events page is correct. Events are aggregated at the workspace level in the application, not scoped per-program. There is no program-level events page to navigate to, and the same link pattern is used consistently across both program and non-program customer layouts.

Likely an incorrect or invalid review comment.

@TWilson023 TWilson023 changed the title [DRAFT] Customers pages updates Customers pages updates Dec 18, 2025
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 (1)
apps/web/lib/middleware/utils/app-redirect.ts (1)

84-96: Align customer redirect regexes with partner pattern using cus_ prefix validation.

The partner redirect (line 80) correctly validates partner IDs with (pn_[^\/]+) to exclude non-ID paths. The customer redirects use ([^\/]+), which is overly permissive and inconsistent. Since customer IDs are generated with a cus_ prefix throughout the codebase (tolt, rewardful, partnerstack, firstpromoter imports), tightening the regex matches the actual ID format and prevents unintended redirects if future non-ID routes are added under /customers/:

  • Line 85: Update /^\/([^\/]+)\/customers\/([^\/]+)$/ to /^\/([^\/]+)\/customers\/(cus_[^\/]+)$/
  • Lines 90-91: Update /^\/([^\/]+)\/program\/customers\/([^\/]+)$/ to /^\/([^\/]+)\/program\/customers\/(cus_[^\/]+)$/
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 63a4d2a and 6eaeae3.

πŸ“’ Files selected for processing (1)
  • apps/web/lib/middleware/utils/app-redirect.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
⏰ 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/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (2)

56-66: Clean up or complete the commented back-link implementation.

The commented-out href, aria-label, title (lines 57-59), and hover styles (line 62) suggest an incomplete back-link to the customers list. The current div appears interactive due to its styling but has no actual functionality.

Consider either:

  1. Completing the implementation by converting this to a clickable link/button with proper navigation
  2. Removing the commented code if the back-link feature is deferred to a future PR

This will improve code clarity and prevent confusion about the element's intended purpose.

Option 1: Complete the back-link implementation
-        <div className="flex items-center gap-1.5">
-          <div
-            // href={`/programs/${programSlug}/customers`}
-            // aria-label="Back to customers"
-            // title="Back to customers"
+        <div className="flex items-center gap-1.5">
+          <Link
+            href={`/programs/${programSlug}/customers`}
+            aria-label="Back to customers"
+            title="Back to customers"
             className={cn(
-              "bg-bg-subtle flex size-8 shrink-0 items-center justify-center rounded-lg",
-              // "hover:bg-bg-emphasis transition-[transform,background-color] duration-150 active:scale-95",
+              "bg-bg-subtle flex size-8 shrink-0 items-center justify-center rounded-lg",
+              "hover:bg-bg-emphasis transition-[transform,background-color] duration-150 active:scale-95",
             )}
           >
             <UserCheck className="size-4" />
-          </div>
+          </Link>
Option 2: Remove commented code if deferred
         <div className="flex items-center gap-1.5">
           <div
-            // href={`/programs/${programSlug}/customers`}
-            // aria-label="Back to customers"
-            // title="Back to customers"
             className={cn(
               "bg-bg-subtle flex size-8 shrink-0 items-center justify-center rounded-lg",
-              // "hover:bg-bg-emphasis transition-[transform,background-color] duration-150 active:scale-95",
             )}
           >

147-184: Consider adding a display name to the memoized component.

The EarningsTable component is properly memoized with good data-fetching logic (conditional total count fetch, keepPreviousData for smooth UX). However, adding a display name would improve the debugging experience in React DevTools.

Add display name
 const EarningsTable = memo(({ customerId }: { customerId: string }) => {
   // ... component implementation
 });
+
+EarningsTable.displayName = "EarningsTable";
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 6eaeae3 and cbf5fe2.

πŸ“’ Files selected for processing (3)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (2 hunks)
  • apps/web/lib/swr/use-partner-customer.ts (1 hunks)
  • apps/web/ui/customers/customer-details-column.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (9)
πŸ““ Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx:180-189
Timestamp: 2025-09-24T15:50:16.414Z
Learning: TWilson023 prefers to keep security vulnerability fixes separate from refactoring PRs when the vulnerable code is existing and was only moved/relocated rather than newly introduced.
πŸ“š 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/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx
  • apps/web/ui/customers/customer-details-column.tsx
πŸ“š 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/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx
πŸ“š Learning: 2025-08-25T17:42:13.600Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/lib/api/get-workspace-users.ts:76-83
Timestamp: 2025-08-25T17:42:13.600Z
Learning: Business rule confirmed: Each workspace has exactly one program. The code should always return workspace.programs[0] since there's only one program per workspace.

Applied to files:

  • apps/web/ui/customers/customer-details-column.tsx
πŸ“š Learning: 2025-06-10T19:16:23.445Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2510
File: apps/web/lib/actions/partners/onboard-program.ts:16-19
Timestamp: 2025-06-10T19:16:23.445Z
Learning: Business rule: Each workspace may have at most one program; attempting to create more must be blocked in code.

Applied to files:

  • apps/web/ui/customers/customer-details-column.tsx
πŸ“š Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.

Applied to files:

  • apps/web/ui/customers/customer-details-column.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/customers/customer-details-column.tsx
πŸ“š Learning: 2025-11-19T17:26:51.932Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3136
File: apps/web/ui/layout/sidebar/app-sidebar-nav.tsx:377-379
Timestamp: 2025-11-19T17:26:51.932Z
Learning: In the dub repository, TWilson023 prefers an incremental approach to route updates: fixing the most user-visible issues first (like sidebar navigation) while deferring less visible internal references to follow-up PRs, especially when redirects provide backward compatibility.

Applied to files:

  • apps/web/ui/customers/customer-details-column.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/ui/customers/customer-details-column.tsx
🧬 Code graph analysis (1)
apps/web/lib/swr/use-partner-customer.ts (1)
apps/web/lib/types.ts (1)
  • PartnerProfileCustomerProps (465-467)
⏰ 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/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/customers/[customerId]/page-client.tsx (2)

10-14: LGTM! Clean import additions for layout refactoring.

The new imports for PageContent, PageWidthWrapper, icons, and formatDate are all appropriately used in the refactored component structure.


78-142: LGTM! Well-structured responsive layout refactoring.

The two-column grid layout is well-implemented with:

  • Proper responsive breakpoints and column sizing
  • Correct order manipulation for different screen sizes
  • All business logic preserved (reward eligibility, period warnings, loading states)
  • Clean separation of CustomerDetailsColumn, Earnings, and Activity sections

The refactoring successfully maintains functionality while improving the page structure.

apps/web/ui/customers/customer-details-column.tsx (6)

27-42: LGTM: Props correctly support program/workspace routing contexts.

The addition of isProgramPage and workspaceSlug props properly enables the component to generate correct URLs for both workspace-scoped and partner-scoped contexts. The type change to CustomerEnriched aligns with the enriched data structure.


44-67: LGTM: Elegant conditional field collection.

The basicFields array construction intelligently handles different states:

  • Email field included during loading (shows skeleton) or when present
  • Email field excluded when customer exists without email
  • Country field always present with sensible fallback

The pattern cleanly separates field definition from rendering logic.


204-208: LGTM: URL construction correctly implements business rules.

The href logic properly handles both workspace and partner contexts. Based on the established architectural constraint that isProgramPage is only true when workspaceSlug is defined, the URL patterns generated are:

  • Workspace program: /${workspaceSlug}/program/analytics?...
  • Workspace non-program: /${workspaceSlug}/analytics?...
  • Partner-side: /programs/${programSlug}/analytics?...

This aligns with the route structure clarified in the previous review discussion.

Based on learnings: TWilson023 confirmed that all program pages have workspaceSlug available, ensuring the conditional logic here is sound.


238-263: LGTM: UTM parameter handling is clean and consistent.

The UTM section:

  • Only renders when parameters exist (line 238)
  • Correctly limits analytics links to workspace context (line 248)
  • Uses the same sound URL construction pattern as device/browser/OS links

The grid layout efficiently displays parameter labels and values while handling overflow properly.


266-319: LGTM: Partner and referral link handling is well-structured.

The section correctly adapts to different contexts:

  • Partner information (lines 272-291): Renders partner avatar and name with link when in workspace context
  • Referral link routing (lines 302-307): Appropriately distinguishes between:
    • Workspace: Direct link to full link detail page
    • Partner-side: Analytics page with domain/key filters

The different routing approaches make sense given partner users likely have restricted access to link detail pages.


84-332: LGTM: Clean card-based layout with excellent loading state handling.

The refactored component structure provides:

  • Clear visual hierarchy with the new card container
  • Consistent skeleton loading states throughout (lines 96, 118, 130, 149, 218, 296)
  • Responsive grid layouts that adapt to different viewport sizes
  • Improved semantic HTML with DetailHeading using h3 instead of h2 (line 328)

The modular organization with distinct sections (avatar, basic fields, details, UTM, referral) makes the component easy to maintain and extend.

apps/web/lib/swr/use-partner-customer.ts (1)

13-15: The current implementation is correct and should not be changed. The API endpoint conditionally adds the name field to the response based on customerDataSharingEnabledAt, so the field is not always present. Updating PartnerProfileCustomerSchema to unconditionally include name would misrepresent the API contract. The frontend's type intersection PartnerProfileCustomerProps & { name?: string | null } correctly reflects that the field may or may not be present in the response.

Likely an incorrect or invalid review comment.

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