-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Program onboarding domains updates #3143
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 GitHub.
|
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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 (1)
apps/web/ui/modals/register-domain-modal.tsx (1)
46-64: Consider memoizing props if they change frequently.The hook correctly includes
propsin the dependency array (line 59), which will causeRegisterDomainModalto recreate wheneverpropschanges. 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
📒 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 andstopPropagation()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
onSuccesscallback andsetRegisteredParamflag 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 !== falsecheck 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
ProgramLinkConfigurationPropsfrom URL-related props andDomainProps. 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
DomainOnboardingcomponent 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: falseprevents unwanted URL parameter changes during the onboarding flow.
186-280: LGTM: Smooth transitions with proper loading states.The render logic uses
AnimatePresenceandAnimatedSizeContainereffectively 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}/verifyendpoint properly handles both newly registered and existing domains through the route handler atapps/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).
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
🧹 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
📒 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
onSuccesscallbacks (which callsetState("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.
| onSelect: () => { | ||
| if (!domains?.length) setShowAddEditDomainModal(true); | ||
| else setState("select"); | ||
| }, | ||
| loading: isLoadingDomains, |
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.
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.
| 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.
| const { data: verificationData } = useSWRImmutable<{ | ||
| status: DomainVerificationStatusProps; | ||
| response: any; | ||
| }>( | ||
| workspaceId && domain | ||
| ? `/api/domains/${domain}/verify?workspaceId=${workspaceId}` | ||
| : null, | ||
| fetcher, | ||
| ); |
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.
🛠️ 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.
| 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.
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
♻️ Duplicate comments (2)
apps/web/ui/partners/program-link-configuration.tsx (2)
300-303: Replaceresponse: anywith a typed interface for domain verification data.The SWR response shape is partially typed (
status: DomainVerificationStatusProps) but leavesresponseasany, which weakens type‑safety forDomainConfigurationand 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: GuardonSelectonisLoadingDomainsbefore checkingdomainslength.The “Connect a domain you own” option currently branches on
!domains?.lengtheven whileisLoadingDomainscan 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
domainsonce 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 localDomainPropsto avoid confusion with shared domain entity type.
DomainPropsis 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
DomainOnboardingandDomainOnboardingSelectionto use the new name.
96-98: Guard against emptylinkStructureOptionsto avoid a runtime error.
linkStructureOptions?.[0].examplewill still throw ifgetLinkStructureOptionsreturns a non‑null empty array, because the optional chaining only protects the root, not index access.If
getLinkStructureOptionscan ever produce an empty array, consider:- {linkStructureOptions?.[0].example} + {linkStructureOptions?.[0]?.example ?? ""}or provide a more explicit fallback string.
124-126: Consider syncing onboardingstatewithdomainprop changes.
stateis initialized fromdomainonly once. Ifdomainis 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
useEffectfrom React.)
128-135: AlignRegisterDomainModalonSuccesspayload withonDomainChangeexpectations.Here
onSuccesspassesdomaindirectly intoonDomainChange, while theAddEditDomainModalpath below passesdomain.slug. That asymmetry suggests theRegisterDomainModalcallback 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
onSuccessreceives aDomainProps-like object (as in the add/edit modal), this should likely be:onSuccess: (domain) => { onDomainChange(domain.slug); setState("select"); },Please double‑check the
onSuccesstype inuseRegisterDomainModaland make these consistent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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
DomainOnboardingSelectionflow—hookingDomainSelectortoonDomainChange, gating verification onworkspaceId && domain, and only renderingDomainConfigurationwhen 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.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.