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

Skip to content

Conversation

@TWilson023
Copy link
Collaborator

@TWilson023 TWilson023 commented Nov 21, 2025

Screenshot 2025-11-21 at 4 04 59 PM

Summary by CodeRabbit

  • New Features

    • Two-step domain onboarding flow with animated transitions and selection UI.
    • Program link configuration now accepts and updates a program URL and notifies on URL changes.
    • Domain registration supports an optional post-success callback and optional registered-parameter behavior.
  • Refactor

    • Modularized domain verification and onboarding into distinct selection and configuration flows.
    • Improved form submission handling to guard default events and propagation for more robust behavior.

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

@vercel
Copy link
Contributor

vercel bot commented Nov 21, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 22, 2025 1:09am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 21, 2025

Walkthrough

Adds explicit form event guarding in the domain form, extends the register-domain modal API with onSuccess and setRegisteredParam, and refactors ProgramLinkConfiguration into a DomainOnboarding flow with selection and verification and new url/onUrlChange props.

Changes

Cohort / File(s) Change Summary
Form event handling
apps/web/ui/domains/add-edit-domain-form.tsx
Import FormEvent and replace onSubmit={handleSubmit(onSubmit)} with an inline async onSubmit using FormEvent<HTMLFormElement> that calls preventDefault()/stopPropagation() then delegates to handleSubmit(onSubmit).
Register domain modal API
apps/web/ui/modals/register-domain-modal.tsx
RegisterDomainProps extended with onSuccess?: (domain: string) => void and setRegisteredParam?: boolean; RegisterDomain and useRegisterDomainModal updated to accept/forward these props; on success calls onSuccess?.(domain) and conditionally sets the registered query param only when setRegisteredParam !== false.
Program link configuration → DomainOnboarding
apps/web/ui/partners/program-link-configuration.tsx
Replaced inline domain verification UI with a DomainOnboarding flow (idle/select/verify), moved verification logic into DomainOnboardingSelection, added `url: string

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PLC as ProgramLinkConfiguration
    participant Onboard as DomainOnboarding
    participant Select as DomainOnboardingSelection
    participant RegModal as RegisterDomainModal

    User->>PLC: open Program link settings
    PLC->>Onboard: render onboarding (idle)
    User->>Onboard: choose "register" or "connect"
    Onboard->>RegModal: open register/connect modal
    User->>RegModal: complete flow
    RegModal->>PLC: onSuccess(domain)
    PLC->>Select: switch to selection state
    Select->>Select: fetch/verify domain status
    Select-->>User: show domain + verification state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • apps/web/ui/partners/program-link-configuration.tsx: onboarding state machine, prop additions (url/onUrlChange), and interactions with selection/verification.
    • apps/web/ui/modals/register-domain-modal.tsx: correct forwarding of new props, onSuccess invocation, and conditional query-parameter behavior.
    • apps/web/ui/domains/add-edit-domain-form.tsx: ensure the inline form event handler preserves original form validation/submission behavior.

Possibly related PRs

Poem

🐰 A rabbit taps the keyboard light,
Domains align and modals bright,
Callbacks sing and forms stand guard,
Selection hops, verification starred,
Hooray — new paths take flight!

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 'Program onboarding domains updates' accurately reflects the main changes across all three modified files, which collectively implement domain onboarding features and refactor domain handling in the program link configuration flow.
✨ 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 program-onboarding-domains

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: 0

🧹 Nitpick comments (1)
apps/web/ui/modals/register-domain-modal.tsx (1)

46-64: Consider memoizing props if they change frequently.

The hook correctly includes props in the dependency array (line 59), which will cause RegisterDomainModal to recreate whenever props changes. If the caller passes a new object reference on each render, this could cause unnecessary re-renders of components using this modal.

If you notice performance issues, consider having callers memoize their props object:

const registerModalProps = useMemo(
  () => ({ onSuccess, setRegisteredParam }),
  [onSuccess, setRegisteredParam]
);
const { RegisterDomainModal, setShowRegisterDomainModal } = 
  useRegisterDomainModal(registerModalProps);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd3e8e2 and 49a2d30.

📒 Files selected for processing (3)
  • apps/web/ui/domains/add-edit-domain-form.tsx (2 hunks)
  • apps/web/ui/modals/register-domain-modal.tsx (3 hunks)
  • apps/web/ui/partners/program-link-configuration.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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: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/ui/modals/register-domain-modal.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/program-link-configuration.tsx (5)
apps/web/lib/types.ts (2)
  • DomainProps (258-274)
  • DomainVerificationStatusProps (250-256)
apps/web/lib/swr/use-domains.ts (1)
  • useDomains (15-107)
apps/web/ui/modals/register-domain-modal.tsx (1)
  • useRegisterDomainModal (46-65)
apps/web/ui/modals/add-edit-domain-modal.tsx (1)
  • useAddEditDomainModal (85-128)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
⏰ 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/ui/domains/add-edit-domain-form.tsx (1)

34-34: LGTM: Explicit event typing and propagation control.

The explicit FormEvent<HTMLFormElement> typing and stopPropagation() call are appropriate for preventing submission events from bubbling to parent forms (as noted in the comment). This is a defensive pattern that improves form handling when nested in complex UIs like the link builder.

Also applies to: 279-287

apps/web/ui/modals/register-domain-modal.tsx (2)

5-10: LGTM: Interface extension is backwards compatible.

The optional onSuccess callback and setRegisteredParam flag are well-named and maintain backwards compatibility by being optional.


12-44: LGTM: Success callback and conditional parameter handling.

The success callback invocation and conditional parameter setting are implemented correctly. The setRegisteredParam !== false check maintains backwards compatibility by defaulting to setting the parameter when the prop is undefined.

apps/web/ui/partners/program-link-configuration.tsx (5)

1-25: LGTM: Clean type definitions and imports.

The type definitions properly use intersection types to compose ProgramLinkConfigurationProps from URL-related props and DomainProps. The imports are well-organized and include the necessary UI components and hooks for the new onboarding flow.


27-127: LGTM: Clean delegation to DomainOnboarding component.

The main component properly delegates domain selection to the new DomainOnboarding component while maintaining the URL input and link preview functionality. The label change to "Program domain" better reflects the context.


129-184: LGTM: Well-structured onboarding flow with appropriate modal integration.

The component properly integrates the register and add/edit domain modals with success callbacks that update the domain and transition to the selection state. The setRegisteredParam: false prevents unwanted URL parameter changes during the onboarding flow.


186-280: LGTM: Smooth transitions with proper loading states.

The render logic uses AnimatePresence and AnimatedSizeContainer effectively for smooth transitions between idle and select states. Loading states are properly handled with disabled buttons and loading spinners.


282-324: No changes required—domain verification API is correctly implemented.

The /api/domains/${domain}/verify endpoint properly handles both newly registered and existing domains through the route handler at apps/web/app/api/domains/[domain]/verify/route.ts. The handler validates workspace authorization, fetches domain status from Vercel, and returns a response structure that matches the component's TypeScript types exactly. The component correctly displays configuration UI whenever the domain status is not "Valid Configuration", covering all verification states (pending, not found, conflicts, misconfigured).

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

🧹 Nitpick comments (1)
apps/web/ui/partners/program-link-configuration.tsx (1)

311-349: Consider adding loading and error states for domain verification.

The component fetches verification data but doesn't display a loading indicator or handle potential errors. While the verification UI only appears when data is available, users might not understand why the verification section doesn't appear immediately after selecting a domain.

Consider adding a loading skeleton or message:

{domain && (
  <>
    {!verificationData && (
      <div className="mt-6 animate-pulse rounded-md border border-neutral-200 bg-neutral-50 p-5">
        <div className="text-xs text-neutral-500">
          Checking domain configuration...
        </div>
      </div>
    )}
    {verificationData?.status !== "Valid Configuration" && (
      <motion.div ...>
        <DomainConfiguration data={verificationData} />
      </motion.div>
    )}
  </>
)}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49a2d30 and 12b6243.

📒 Files selected for processing (1)
  • apps/web/ui/partners/program-link-configuration.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 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.912Z
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: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/ui/partners/program-link-configuration.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/program-link-configuration.tsx (5)
apps/web/lib/types.ts (2)
  • DomainProps (258-274)
  • DomainVerificationStatusProps (250-256)
apps/web/lib/swr/use-domains.ts (1)
  • useDomains (15-107)
apps/web/ui/modals/register-domain-modal.tsx (1)
  • useRegisterDomainModal (46-65)
apps/web/ui/modals/add-edit-domain-modal.tsx (1)
  • useAddEditDomainModal (85-128)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
⏰ 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/ui/partners/program-link-configuration.tsx (4)

1-25: LGTM!

The imports are well-organized and all necessary dependencies are properly included for the new domain onboarding flow.


27-36: LGTM!

The type definitions are well-structured with clear prop composition using intersection types.


38-138: LGTM!

The component is well-structured with proper separation of concerns. The new DomainOnboarding abstraction improves maintainability, and the animated link preview provides good UX feedback.


148-163: Verified: Modal cancellation behavior is working correctly.

The modals properly handle cancellation without changing component state. When users dismiss either modal, the component state remains at "idle" since the onSuccess callbacks (which call setState("select")) are never invoked on cancellation. This is the correct behavior—state only changes when the form is successfully submitted, not when the modal is opened or dismissed.

No issues found with the modal cancellation flow or state management.

Comment on lines +182 to +186
onSelect: () => {
if (!domains?.length) setShowAddEditDomainModal(true);
else setState("select");
},
loading: isLoadingDomains,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential race condition between loading state and domain check.

The logic checks !domains?.length to decide whether to open the add domain modal or transition to "select" state. However, this check executes even when isLoadingDomains is true (which only disables the button). If the button's disabled state doesn't fully prevent onClick execution in edge cases, the check might evaluate incorrectly against stale/empty domain data.

Consider moving the domain length check inside the loading condition:

 onSelect: () => {
-  if (!domains?.length) setShowAddEditDomainModal(true);
-  else setState("select");
+  if (isLoadingDomains) return;
+  if (!domains?.length) {
+    setShowAddEditDomainModal(true);
+  } else {
+    setState("select");
+  }
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onSelect: () => {
if (!domains?.length) setShowAddEditDomainModal(true);
else setState("select");
},
loading: isLoadingDomains,
onSelect: () => {
if (isLoadingDomains) return;
if (!domains?.length) {
setShowAddEditDomainModal(true);
} else {
setState("select");
}
},
loading: isLoadingDomains,
🤖 Prompt for AI Agents
In apps/web/ui/partners/program-link-configuration.tsx around lines 182 to 186,
the onSelect callback checks !domains?.length even when isLoadingDomains is true
which can cause a race/stale-state branch; update the handler to early-return or
guard on isLoadingDomains first (e.g., if (isLoadingDomains) return), then
perform the domains length check and call setShowAddEditDomainModal(true) or
setState("select") accordingly so the loading state is respected before deciding
which action to take.

Comment on lines +301 to +309
const { data: verificationData } = useSWRImmutable<{
status: DomainVerificationStatusProps;
response: any;
}>(
workspaceId && domain
? `/api/domains/${domain}/verify?workspaceId=${workspaceId}`
: null,
fetcher,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace any type with a proper type definition.

The verification response is typed as any, which bypasses TypeScript's type safety. Consider defining a proper interface for the response structure.

Define and use a proper type:

+interface DomainVerificationResponse {
+  // Add actual response structure fields based on API contract
+  domainJson?: Record<string, unknown>;
+  configJson?: Record<string, unknown>;
+}
+
 const { data: verificationData } = useSWRImmutable<{
   status: DomainVerificationStatusProps;
-  response: any;
+  response: DomainVerificationResponse;
 }>(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { data: verificationData } = useSWRImmutable<{
status: DomainVerificationStatusProps;
response: any;
}>(
workspaceId && domain
? `/api/domains/${domain}/verify?workspaceId=${workspaceId}`
: null,
fetcher,
);
interface DomainVerificationResponse {
// Add actual response structure fields based on API contract
domainJson?: Record<string, unknown>;
configJson?: Record<string, unknown>;
}
const { data: verificationData } = useSWRImmutable<{
status: DomainVerificationStatusProps;
response: DomainVerificationResponse;
}>(
workspaceId && domain
? `/api/domains/${domain}/verify?workspaceId=${workspaceId}`
: null,
fetcher,
);
🤖 Prompt for AI Agents
In apps/web/ui/partners/program-link-configuration.tsx around lines 301 to 309,
the SWR generic currently uses `any` for the verification response which defeats
TypeScript safety; define a concrete interface (e.g.,
DomainVerificationResponse) describing the shape of the response object (fields
returned by /api/domains/:domain/verify such as verified:boolean,
message?:string, errors?:Record<string,string>, etc.), replace `any` with that
interface in the useSWRImmutable generic, and update the fetcher (or its typing)
to return that typed shape so downstream code uses the new type instead of
`any`. Ensure to export/import the interface or declare it in this file and
adjust any code that accesses response properties to match the defined types.

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

♻️ Duplicate comments (2)
apps/web/ui/partners/program-link-configuration.tsx (2)

300-303: Replace response: any with a typed interface for domain verification data.

The SWR response shape is partially typed (status: DomainVerificationStatusProps) but leaves response as any, which weakens type‑safety for DomainConfiguration and other consumers:

const { data: verificationData } = useSWRImmutable<{
  status: DomainVerificationStatusProps;
  response: any;
}>(/* ... */);

Define an explicit interface for the verification payload and use it here, e.g.:

interface DomainVerificationResponse {
  // Shape based on `/api/domains/:domain/verify`
  verified?: boolean;
  message?: string;
  errors?: Record<string, string>;
  // add other known fields as needed
}

const { data: verificationData } = useSWRImmutable<{
  status: DomainVerificationStatusProps;
  response: DomainVerificationResponse;
}>(/* ... */);

Then update DomainConfiguration’s props (if necessary) to use the same type.


145-167: Guard onSelect on isLoadingDomains before checking domains length.

The “Connect a domain you own” option currently branches on !domains?.length even while isLoadingDomains can be true:

onSelect: () => {
  if (!domains?.length) setShowAddEditDomainModal(true);
  else setState("select");
},
loading: isLoadingDomains,

Even though the button is disabled while loading, it’s safer and clearer to ensure the handler itself is a no‑op during loading and only inspects domains once loaded:

         onSelect: () => {
-          if (!domains?.length) setShowAddEditDomainModal(true);
-          else setState("select");
+          if (isLoadingDomains) return;
+          if (!domains?.length) {
+            setShowAddEditDomainModal(true);
+          } else {
+            setState("select");
+          }
         },
         loading: isLoadingDomains,

This avoids any accidental decision based on an incomplete domains list.

🧹 Nitpick comments (4)
apps/web/ui/partners/program-link-configuration.tsx (4)

16-25: Rename local DomainProps to avoid confusion with shared domain entity type.

DomainProps is already a widely used type for full domain objects (from "@/lib/types"). Using the same name here for { domain: string | null; onDomainChange: (domain: string) => void } is likely to confuse future readers.

Consider renaming this alias to something more specific (e.g. ProgramDomainSelectionProps) and updating the intersection and component signatures accordingly:

-type DomainProps = {
+type ProgramDomainSelectionProps = {
   domain: string | null;
   onDomainChange: (domain: string) => void;
 };
 
-type ProgramLinkConfigurationProps = {
+type ProgramLinkConfigurationProps = {
   url: string | null;
   onUrlChange: (url: string) => void;
   hideLinkPreview?: boolean;
-} & DomainProps;
+} & ProgramDomainSelectionProps;

Then update DomainOnboarding and DomainOnboardingSelection to use the new name.


96-98: Guard against empty linkStructureOptions to avoid a runtime error.

linkStructureOptions?.[0].example will still throw if getLinkStructureOptions returns a non‑null empty array, because the optional chaining only protects the root, not index access.

If getLinkStructureOptions can ever produce an empty array, consider:

-                    {linkStructureOptions?.[0].example}
+                    {linkStructureOptions?.[0]?.example ?? ""}

or provide a more explicit fallback string.


124-126: Consider syncing onboarding state with domain prop changes.

state is initialized from domain only once. If domain is populated asynchronously after mount (e.g. editing an existing program where the domain is loaded later), the UI could remain stuck in "idle" even though a domain is set.

If that scenario is possible, consider syncing via an effect:

const [state, setState] = useState<"idle" | "select">(domain ? "select" : "idle");

useEffect(() => {
  if (domain) {
    setState("select");
  }
}, [domain]);

(You’d also need to import useEffect from React.)


128-135: Align RegisterDomainModal onSuccess payload with onDomainChange expectations.

Here onSuccess passes domain directly into onDomainChange, while the AddEditDomainModal path below passes domain.slug. That asymmetry suggests the RegisterDomainModal callback may be returning a full domain object but you’re treating it as a slug:

useRegisterDomainModal({
  onSuccess: (domain) => {
    onDomainChange(domain); // vs. onDomainChange(domain.slug) below
    setState("select");
  },
  setRegisteredParam: false,
});

If onSuccess receives a DomainProps-like object (as in the add/edit modal), this should likely be:

onSuccess: (domain) => {
  onDomainChange(domain.slug);
  setState("select");
},

Please double‑check the onSuccess type in useRegisterDomainModal and make these consistent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12b6243 and 8d2797d.

📒 Files selected for processing (1)
  • apps/web/ui/partners/program-link-configuration.tsx (3 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.912Z
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: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/ui/partners/program-link-configuration.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/ui/partners/program-link-configuration.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/partners/program-link-configuration.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/program-link-configuration.tsx (5)
apps/web/lib/types.ts (2)
  • DomainProps (258-274)
  • DomainVerificationStatusProps (250-256)
apps/web/lib/swr/use-domains.ts (1)
  • useDomains (15-107)
apps/web/ui/modals/register-domain-modal.tsx (1)
  • useRegisterDomainModal (46-65)
apps/web/ui/modals/add-edit-domain-modal.tsx (1)
  • useAddEditDomainModal (85-128)
apps/web/lib/swr/use-workspace.ts (1)
  • useWorkspace (7-48)
⏰ 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 (1)
apps/web/ui/partners/program-link-configuration.tsx (1)

293-337: Domain selection and verification flow looks coherent.

The DomainOnboardingSelection flow—hooking DomainSelector to onDomainChange, gating verification on workspaceId && domain, and only rendering DomainConfiguration when the status is not "Valid Configuration"—is a clean separation of concerns and keeps verification UI out of the main form.

No changes needed here from my side.

@steven-tey steven-tey merged commit 2e52141 into main Nov 22, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the program-onboarding-domains branch November 22, 2025 01:28
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