-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Allow updating partner email #2602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
@CodeRabbit final review pls |
WalkthroughThe changes add support for editing a partner's email address in the profile form, with conditional disabling and tooltips if payouts have been completed. The backend schema and update logic are updated to require and validate the email field, and to restrict email changes if payouts exist, aligning with existing restrictions on other sensitive fields. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ProfileForm
participant ConfirmationDialog
participant BackendAction
User->>ProfileForm: Edit email/country/profile type
ProfileForm->>User: Disable fields or show tooltip if payouts exist
User->>ProfileForm: Click "Save changes"
alt Stripe Connect ID present
ProfileForm->>ConfirmationDialog: Show warning about resetting Stripe
ConfirmationDialog->>User: Prompt for confirmation
User->>ConfirmationDialog: Confirm/Cancel
alt User cancels
ConfirmationDialog->>ProfileForm: Abort submission
else User confirms
ProfileForm->>BackendAction: Submit updated profile data
end
else
ProfileForm->>BackendAction: Submit updated profile data
end
BackendAction->>ProfileForm: Success/Error response
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 15350 🏁 Script executed: Length of output: 4139 🏁 Script executed: Length of output: 182 🏁 Script executed: Length of output: 3209 🏁 Script executed: Length of output: 78 🏁 Script executed: Length of output: 104574 🏁 Script executed: Length of output: 3253 🏁 Script executed: Length of output: 2309 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 9128 @steven-tey Looking at your partner profile update implementation, this is a well-structured feature with solid business logic and security considerations. Here's my final review: ✅ StrengthsSecurity & Validation:
Business Logic:
User Experience:
Code Quality:
🔧 Minor SuggestionsComponent Organization: // Extract field components
<EmailField completedPayoutsCount={completedPayoutsCount} />
<CountryField completedPayoutsCount={completedPayoutsCount} />
<ProfileTypeField completedPayoutsCount={completedPayoutsCount} />Code Duplication: const useFieldRestrictions = (completedPayoutsCount: number) => ({
isRestricted: completedPayoutsCount > 0,
getTooltip: (fieldName: string) => `Since you've already received payouts...`,
getFieldProps: (fieldName: string) => ({ disabled: completedPayoutsCount > 0 })
});📋 Technical NotesImage Handling: The conditional image upload logic is clean: image: imageChanged ? data.image : nullForm Validation: The Zod schema properly handles the company name requirement: .refine((data) => data.profileType === "company" ? !!data.companyName : true)Cache Invalidation: Good use of 🎯 Overall AssessmentThis is production-ready code that demonstrates solid understanding of:
The implementation properly balances user flexibility with business requirements around payout restrictions. The code is maintainable and follows React/Next.js best practices. Approved ✅ - This feature is ready for deployment with excellent attention to security, UX, and business logic requirements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/web/lib/actions/partners/update-partner-profile.ts (1)
81-82: Update the comment to include email.The comment should reflect that email is now also restricted along with country, profile type, and company name.
- // Partner is not able to update their country, profile type, or company name + // Partner is not able to update their email, country, profile type, or company nameapps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (2)
143-155: Enhance confirmation dialog to be more specific about changed fields.The current confirmation dialog shows a generic message regardless of which specific fields have changed. Consider making it more precise to improve user experience.
- onClick={() => { - if (partner?.stripeConnectId) { - const confirmed = window.confirm( - "Updating your email, country, or profile type will reset your Stripe account, which will require you to restart the payouts connection process. Are you sure you want to continue?", - ); - - if (!confirmed) { - return; - } - } - - formRef.current?.requestSubmit(); - }} + onClick={() => { + if (partner?.stripeConnectId) { + const form = formRef.current; + if (form) { + const formData = new FormData(form); + const changedFields = []; + + if (formData.get('email') !== partner.email) changedFields.push('email'); + if (formData.get('country') !== partner.country) changedFields.push('country'); + if (formData.get('profileType') !== partner.profileType) changedFields.push('profile type'); + + if (changedFields.length > 0) { + const confirmed = window.confirm( + `Updating your ${changedFields.join(', ')} will reset your Stripe account, which will require you to restart the payouts connection process. Are you sure you want to continue?`, + ); + + if (!confirmed) { + return; + } + } + } + } + + formRef.current?.requestSubmit(); + }}
315-315: Simplify boolean expression.The ternary operator is unnecessary when the result is already a boolean.
- error={errors.country ? true : false} + error={!!errors.country}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx(9 hunks)apps/web/lib/actions/partners/update-partner-profile.ts(5 hunks)
🧰 Additional context used
🧠 Learnings (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (2)
Learnt from: devkiran
PR: dubinc/dub#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.
Learnt from: TWilson023
PR: dubinc/dub#2471
File: apps/web/ui/auth/reset-password-form.tsx:52-65
Timestamp: 2025-06-04T15:09:51.562Z
Learning: In the Dub codebase, server-side validation errors for password fields are handled via toast notifications rather than using react-hook-form's setError method to display field-specific errors. This architectural pattern separates client-side validation feedback (inline) from server-side error handling (toast).
🧬 Code Graph Analysis (1)
apps/web/lib/actions/partners/update-partner-profile.ts (1)
apps/web/lib/stripe/index.ts (1)
stripe(3-9)
🪛 Biome (1.9.4)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx
[error] 315-315: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx
Outdated
Show resolved
Hide resolved
|
@CodeRabbit final review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 20203 @steven-tey Looking at your partner profile update implementation, this is a well-structured feature with solid business logic and security considerations. Here's my final review: ✅ StrengthsSecurity & Validation:
Business Logic:
User Experience:
Code Quality:
🔧 Minor SuggestionsComponent Organization: // Extract field components
<EmailField completedPayoutsCount={completedPayoutsCount} />
<CountryField completedPayoutsCount={completedPayoutsCount} />
<ProfileTypeField completedPayoutsCount={completedPayoutsCount} />Code Duplication: const useFieldRestrictions = (completedPayoutsCount: number) => ({
isRestricted: completedPayoutsCount > 0,
getTooltip: (fieldName: string) => `Since you've already received payouts...`,
getFieldProps: (fieldName: string) => ({ disabled: completedPayoutsCount > 0 })
});📋 Technical NotesImage Handling: The conditional image upload logic is clean: image: imageChanged ? data.image : nullForm Validation: The Zod schema properly handles the company name requirement: .refine((data) => data.profileType === "company" ? !!data.companyName : true)Cache Invalidation: Good use of 🎯 Overall AssessmentThis is production-ready code that demonstrates solid understanding of:
The implementation properly balances user flexibility with business requirements around payout restrictions. The code is maintainable and follows React/Next.js best practices. Approved ✅ - This feature is ready for deployment with excellent attention to security, UX, and business logic requirements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (2)
143-155: Consider including company name in the confirmation dialog.The confirmation message mentions "email, country, or profile type" but based on the backend restrictions, company name changes also reset the Stripe account. Consider updating the message for completeness.
- "Updating your email, country, or profile type will reset your Stripe account, which will require you to restart the payouts connection process. Are you sure you want to continue?", + "Updating your email, country, profile type, or company name will reset your Stripe account, which will require you to restart the payouts connection process. Are you sure you want to continue?",
314-314: Simplify boolean expression.The ternary operator with boolean literals is unnecessary and can be simplified for better readability.
- error={errors.country ? true : false} + error={!!errors.country}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (2)
Learnt from: devkiran
PR: dubinc/dub#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.
Learnt from: TWilson023
PR: dubinc/dub#2471
File: apps/web/ui/auth/reset-password-form.tsx:52-65
Timestamp: 2025-06-04T15:09:51.562Z
Learning: In the Dub codebase, server-side validation errors for password fields are handled via toast notifications rather than using react-hook-form's setError method to display field-specific errors. This architectural pattern separates client-side validation feedback (inline) from server-side error handling (toast).
🪛 Biome (1.9.4)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx
[error] 314-314: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (4)
apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/page-client.tsx (4)
93-93: LGTM: Email field properly added to form data type.The email field addition aligns with the PR objective to enable partner email updates.
108-108: LGTM: Proper email initialization with fallback.The form correctly initializes with the partner's current email and provides an empty string fallback for null values.
272-301: LGTM: Well-implemented email field with proper restrictions.The email input field correctly:
- Uses proper validation with
errors.emailreference- Implements conditional disabling based on completed payouts
- Provides informative tooltip explaining restrictions
- Applies appropriate styling for disabled state
Great implementation that follows the established patterns in the codebase.
350-392: LGTM: Consistent tooltip implementation for profile type field.The profile type field correctly implements the same conditional disabling pattern as other restricted fields, with appropriate tooltip messaging and visual feedback for disabled state.
Summary by CodeRabbit
New Features
Bug Fixes