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

Skip to content

Conversation

@marcusljf
Copy link
Collaborator

@marcusljf marcusljf commented Dec 15, 2025

On the single partner page view for invited partners only, updated the actions to match the table dropdown, minus the change group action, as that already shows on the page.

Only invite delete, redirects the user back to the all partners table and displays toast message showing the partner invite has been deleted.

CleanShot 2025-12-15 at 08 52 31@2x CleanShot 2025-12-15 at 08 52 16@2x

Summary by CodeRabbit

  • New Features

    • Resend partner invitations
    • Delete partner invitations with confirmation
  • Improvements

    • Context-aware action menu and primary actions that change based on invitation status (message, create commission/clawback, advanced settings, archive/reactivate/ban)
    • Loading indicators and toast notifications for invite actions
    • Improved post-action navigation and mobile/compact menu behavior

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

Introduces 'Resend invite' and 'Delete invite' actions for partners with 'invited' status in the partner dashboard. Updates the UI to conditionally show these actions and integrates loading states and toast notifications for user feedback.
@vercel
Copy link
Contributor

vercel bot commented Dec 15, 2025

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

Project Deployment Review Updated (UTC)
dub Ready Ready Preview Dec 15, 2025 5:20pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds resend and delete invite server-action handlers to the partner layout, conditionally renders invite-specific UI for partners with status "invited", refreshes partner cache via mutatePrefix, navigates after delete, and shows success/error toasts.

Changes

Cohort / File(s) Summary
Partner invite UI & actions
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
Added imports for resendProgramInviteAction, deleteProgramInviteAction, mutatePrefix, useAction, toasts, and icons. Implemented useAction handlers for resend and delete with loading states, success/error toasts, cache refresh (mutatePrefix('/api/partners')), popover-close and router navigation to ?status=invited on delete. Conditionally render “Resend” and “Delete invite” controls when partner.status === "invited", and adjust primary actions and mobile/compact menus for non-invited states.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant UI as Frontend (layout.tsx)
    participant Action as Server Action
    participant Cache as mutatePrefix Cache
    participant Router

    User->>UI: Click "Resend invite"
    UI->>Action: call resendProgramInviteAction(workspaceId, partnerId)
    Action-->>UI: success / error
    alt success
        UI->>Cache: mutatePrefix('/api/partners')
        UI-->>User: show success toast
    else error
        UI-->>User: show error toast
    end

    Note over UI,User: Delete flow includes confirmation

    User->>UI: Confirm "Delete invite"
    UI->>Action: call deleteProgramInviteAction(workspaceId, partnerId)
    Action-->>UI: success / error
    alt success
        UI->>Cache: mutatePrefix('/api/partners')
        UI->>Router: navigate to ?status=invited
        UI-->>User: show success toast
    else error
        UI-->>User: show error toast
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Review server-action invocation and error handling in useAction handlers.
  • Verify mutatePrefix('/api/partners') scope and timing covers all updated partner data.
  • Check router navigation and popover-close sequencing after delete.
  • Validate conditional UI for partner.status === "invited" and mobile/compact menu parity.

Possibly related PRs

Poem

🐰 I tap the resend, I nudge the beam,

Invites take flight on a carrot dream.
A toast, a spin, a cached refresh hop,
Delete confirmed — then back to the top.
Hoppity code, the partners pop! 🥕

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 'Invited partners action update' accurately summarizes the main change: updating actions for invited partners on the single partner page view to match table dropdown options.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e283def and 3ef629e.

📒 Files selected for processing (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (6 hunks)

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (2)

231-239: Remove unnecessary setIsOpen(false) call.

This Message button is rendered outside the Popover component (in the desktop view controls), so setIsOpen(false) has no visible effect here—the Popover isn't open when clicking this button.

 <Link href={`/${workspaceSlug}/program/messages/${partner.id}`}>
   <Button
     variant="secondary"
     text="Message"
     icon={<Msgs className="size-4 shrink-0" />}
-    onClick={() => setIsOpen(false)}
     className="hidden h-8 w-fit px-3 sm:h-9 md:flex"
   />
 </Link>

255-261: Consider using a confirmation modal instead of window.confirm.

The rest of this component uses modals for destructive actions (ban, archive, deactivate, etc.). Using window.confirm here is inconsistent with that pattern and provides a less polished UX. However, if the table dropdown uses the same confirm dialog approach for consistency (as mentioned in PR objectives), this may be acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b016815 and cb90fbb.

📒 Files selected for processing (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.
📚 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/partners/[partnerId]/layout.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/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.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/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
📚 Learning: 2025-10-06T15:48:14.205Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.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/partners/[partnerId]/layout.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/partners/[partnerId]/layout.tsx
📚 Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (2)

139-148: LGTM on resend invite implementation.

The resend invite action is well-implemented with proper loading state handling, toast notifications, and defensive checks. Since resending an invite doesn't change data (only sends an email), not invalidating the cache is reasonable.

Also applies to: 198-218


248-271: Conditional menu rendering looks correct.

The branching logic for invited vs non-invited partner states is well-structured. The status checks for various actions (archive, deactivate, ban, etc.) correctly handle the different partner states.

Also applies to: 272-367

Adds a redirect to the partners list with status=invited after successfully deleting a partner invite. This improves user experience by guiding users back to the relevant page.
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 (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (2)

200-243: Conditional UI rendering looks good.

The logic correctly differentiates between invited and non-invited partners, showing appropriate actions for each state.

The status check in line 213 (if (partner.status !== "invited" || !workspaceId)) is redundant since the button only renders when partner.status === "invited". While this defensive check doesn't cause issues, it could be simplified to just check !workspaceId.


250-273: Consider using a custom modal for consistency.

The delete confirmation uses window.confirm (line 258), while other dangerous actions in this file use custom modals (e.g., BanPartnerModal, DeactivatePartnerModal). This creates a minor UX inconsistency.

For consistency with the rest of the codebase, consider creating a DeleteInviteModal similar to other confirmation modals. However, if simplicity is preferred for this less critical action, the current implementation is functional.

The redundant status check in line 254 (if (partner.status !== "invited" || !workspaceId)) could also be simplified to just check !workspaceId since this MenuItem only renders when partner.status === "invited".

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb90fbb and e283def.

📒 Files selected for processing (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (9)
📚 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/partners/[partnerId]/layout.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/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.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/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
📚 Learning: 2025-10-06T15:48:14.205Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2935
File: apps/web/lib/actions/partners/invite-partner-from-network.ts:21-28
Timestamp: 2025-10-06T15:48:14.205Z
Learning: For the network invites limit check in apps/web/lib/actions/partners/invite-partner-from-network.ts, the team accepts that concurrent invites may bypass the limit due to race conditions. Perfect atomicity is not required for this feature.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
📚 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:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.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/partners/[partnerId]/layout.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/partners/[partnerId]/layout.tsx
📚 Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.

Applied to files:

  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx
🧬 Code graph analysis (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (3)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
apps/web/lib/actions/partners/resend-program-invite.ts (1)
  • resendProgramInviteAction (17-100)
packages/ui/src/menu-item.tsx (1)
  • MenuItem (43-86)
⏰ 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 (4)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (4)

3-5: LGTM! All imports are properly used.

The new imports for invite actions, icons, and utilities are correctly integrated throughout the component.

Also applies to: 25-25, 27-27, 31-31, 37-37, 47-47


125-126: LGTM! Correctly extracts required dependencies.

The workspaceId and router are properly extracted for use in the invite action handlers.


151-164: LGTM! Delete action properly implemented.

The delete invite handler correctly refreshes data, shows feedback, and navigates to the filtered partners list. The navigation issue from the previous review has been resolved.


274-369: LGTM! Non-invited partner actions properly preserved.

All existing actions for non-invited partners are correctly maintained, with appropriate mobile/desktop visibility controls and modal integrations.

The call to setIsOpen(false) was removed from the resendProgramInviteAction success handler, so the modal will no longer close automatically when the invite is resent.
@steven-tey
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@steven-tey steven-tey merged commit 8a43799 into main Dec 16, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the partner-invite-dropdown branch December 16, 2025 05:17
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.

3 participants