-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add PartnerStack import logic to program onboarding #2804
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
WalkthroughAdds PartnerStack support to the new program flow: schema gains an optional partnerstack object; action now returns masked credentials; import form accepts watch/setValue to persist returned keys into the parent form; overview validation now treats partnerstack as a valid import source alongside rewardful and tolt. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant RewardsForm as Rewards Form
participant PSForm as ImportPartnerStackForm
participant Action as setPartnerStackTokenAction
User->>PSForm: Enter Public/Secret Keys
PSForm->>Action: setCredentials(publicKey, secretKey)
Action-->>PSForm: { publicKey, maskedSecretKey }
PSForm->>RewardsForm: setValue("partnerstack", { ... })
PSForm->>RewardsForm: onSuccess()
note over RewardsForm: Parent form now holds partnerstack data via watch/setValue
rect rgba(230,245,255,0.6)
note over RewardsForm: Overview validates import when any of rewardful/tolt/partnerstack present
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
| placeholder="Secret key" | ||
| className="mt-2 max-w-full" | ||
| value={secretKey} | ||
| value={secretKey || partnerStack?.maskedSecretKey || ""} |
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.
The secret key input field will display a masked value (like "sk_***") when credentials are already saved, making it confusing for users who expect to see their actual input or a blank field.
View Details
📝 Patch Details
diff --git a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx
index b38199581..7f534644c 100644
--- a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx
+++ b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx
@@ -67,7 +67,7 @@ export const ImportPartnerStackForm = ({
type="password"
placeholder="Public key"
className="mt-2 max-w-full"
- value={publicKey || partnerStack?.publicKey || ""}
+ value={publicKey || ""}
onChange={(e) => setPublicKey(e.target.value)}
/>
<div className="mt-2 text-xs font-normal leading-[1.1] text-neutral-600">
@@ -91,7 +91,7 @@ export const ImportPartnerStackForm = ({
type="password"
placeholder="Secret key"
className="mt-2 max-w-full"
- value={secretKey || partnerStack?.maskedSecretKey || ""}
+ value={secretKey || ""}
onChange={(e) => setSecretKey(e.target.value)}
/>
</div>
Analysis
The secret key input field on line 94 uses value={secretKey || partnerStack?.maskedSecretKey || ""}. This means that when PartnerStack credentials have been previously saved, the input will display the masked secret key (e.g., "sk_***") instead of being empty or showing the user's current input.
This creates a poor user experience because:
- Users will see asterisks in the input field, which looks like they're seeing the actual secret but is actually just a masked representation
- If users try to edit the field, they'll be typing over the masked value rather than entering a fresh secret
- The masked value is not useful for editing - users need to enter their actual secret key
The input should either be empty when showing previously saved credentials, or should not show the masked value at all. A better pattern would be:
value={secretKey || ""}to only show the current user input- Or add conditional logic to show a different UI state when credentials are already saved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx (1)
88-97: Clear PartnerStack state when switching to “New program”.
Avoid stale PartnerStack credentials lingering when toggling programType.useEffect(() => { if (programType === "new") { setValue("rewardful", null); setValue("tolt", null); + setValue("partnerstack", null); } else if (programType === "import") { setValue("type", null); setValue("amount", null); setValue("maxDuration", null); } }, [programType]);
🧹 Nitpick comments (3)
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx (1)
226-238: Prevent conflicting state when changing import source.
When the user selects a different import source, clear other provider objects to avoid ambiguity downstream.setSelectedItem={(item: ImportSource) => { if (item) { setValue("importSource", item.id, { shouldDirty: true, }); + // Clear previously entered credentials from other sources + setValue("rewardful", null); + setValue("tolt", null); + setValue("partnerstack", null); } }}apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx (2)
47-58: Allow proceeding with already-saved credentials (better UX).
If partnerstack is already saved in form state, let users continue without re-entering keys.@@ - const onSubmit = async () => { - if (!workspaceId || !publicKey || !secretKey) { - toast.error("Please fill in all required fields."); - return; - } - - await executeAsync({ - workspaceId, - publicKey, - secretKey, - }); - }; + const hasSavedCredentials = Boolean( + partnerStack?.publicKey && partnerStack?.maskedSecretKey, + ); + + const onSubmit = async () => { + if (!workspaceId) { + toast.error("Workspace not found."); + return; + } + // If nothing entered but creds already saved, proceed. + if (!publicKey && !secretKey && hasSavedCredentials) { + onSuccess(); + return; + } + if (!publicKey || !secretKey) { + toast.error("Please fill in all required fields."); + return; + } + await executeAsync({ workspaceId, publicKey, secretKey }); + }; @@ - <Button + <Button text="Continue" className="w-full" - disabled={!publicKey || !secretKey} + disabled={(!publicKey || !secretKey) && !hasSavedCredentials} loading={isSettingPartnerStackToken || isPending} onClick={onSubmit} />Also applies to: 99-105
70-71: Avoid pre-filling password fields with masked values.
Pre-populating password inputs with masked secrets is confusing and may cause accidental submission without edits. Prefer placeholders or a “Saved” badge.Example:
- value={publicKey || partnerStack?.publicKey || ""} + value={publicKey} @@ - value={secretKey || partnerStack?.maskedSecretKey || ""} + value={secretKey}Then show a small “Credentials saved” hint when partnerStack exists.
Also applies to: 94-95
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/overview/page-client.tsx(2 hunks)apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx(1 hunks)apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx(3 hunks)apps/web/lib/actions/partners/set-partnerstack-token.ts(1 hunks)apps/web/lib/zod/schemas/program-onboarding.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-30T15:25:13.936Z
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx:56-66
Timestamp: 2025-07-30T15:25:13.936Z
Learning: In apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx, the form schema uses partial condition objects to allow users to add empty/unconfigured condition fields without type errors, while submission validation uses strict schemas to ensure data integrity. This two-stage validation pattern improves UX by allowing progressive completion of complex forms.
Applied to files:
apps/web/lib/zod/schemas/program-onboarding.tsapps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/overview/page-client.tsx
📚 Learning: 2025-07-30T15:29:54.131Z
Learnt from: TWilson023
PR: dubinc/dub#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)/app.dub.co/(new-program)/[slug]/program/new/overview/page-client.tsx
🧬 Code graph analysis (1)
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx (2)
apps/web/lib/types.ts (1)
ProgramData(495-495)apps/web/lib/actions/partners/set-partnerstack-token.ts (1)
setPartnerStackTokenAction(13-35)
⏰ 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). (2)
- GitHub Check: Vade Review
- GitHub Check: build
🔇 Additional comments (4)
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/overview/page-client.tsx (1)
52-55: PartnerStack included in destructuring and validity scope — good addition.
No issues with adding partnerstack to the form data read.apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx (1)
155-157: Prop plumbing to ImportPartnerStackForm looks correct.
Passing watch and setValue aligns with the new form-state behavior.apps/web/lib/zod/schemas/program-onboarding.ts (1)
38-43: Schema extension for partnerstack is consistent with progressive form UX.
Optional fields align with the two-stage validation pattern you use elsewhere.apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx (1)
14-21: watch/setValue props integration — solid.
Enables the parent form to own state while this child reflects and updates it.
apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/overview/page-client.tsx
Show resolved
Hide resolved
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
/vercel run |
Summary by CodeRabbit