Skip to content

onboarding: data-plane picker in the signup form - #2045

Draft
SeanWhelan wants to merge 7 commits into
mainfrom
sean/onboarding-data-plane-picker
Draft

onboarding: data-plane picker in the signup form#2045
SeanWhelan wants to merge 7 commits into
mainfrom
sean/onboarding-data-plane-picker

Conversation

@SeanWhelan

@SeanWhelan SeanWhelan commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Issues

estuary/sre#29 (Phase 2 — tracked in a roadmap doc, not an estuary/ui issue)

Changes

sre#29

  • Adds a DataPlaneSelector to the signup form (BetaOnboard.tsx), between the organization name field and the survey. It's backed by the existing unauthenticated publicDataPlanes query, which this PR consumes for the first time — the authenticated dataPlanes query returns nothing for a brand-new signup, since there are no grants yet.
  • Auto-selects ops/dp/public/aws-us-east-1-c1 when it's available, falling back to the first plane otherwise, so submitting without touching the picker still records an explicit, valid choice.
  • Fails safe: if the plane list can't load, the picker renders nothing and the claim just omits requestedDataPlane rather than sending it as an explicit null, so the backend falls back to its own default instead of the signup form breaking.
  • Extends the betaOnboard directive's generateUserClaim to forward requestedDataPlane only when it's set, so backends that don't know the field yet aren't affected.
  • Adds a usePublicDataPlanes() hook and PublicDataPlaneNode type, following the existing useDataPlanes()/DataPlaneNode pattern already in the codebase.

Options show the region and the full catalog name together, and group by cloud provider. The catalog name is deliberate rather than noise: nothing stops two public planes sharing a region (already true for GCP), so the name is what makes the choice unambiguous.

This depends on estuary/flow#3318 merging and deploying first. The agent rejects unknown claim fields, so if this ships before that one is live, every signup fails with invalidClaims. Merging flow isn't sufficient on its own — an old agent still running will reject the claim.

Tests

Manually tested

I ran a full signup through the real onboarding form against a local flow stack. The picker listed the seeded public planes grouped by cloud provider, auto-selected the platform default, and a completed registration produced the correct storage_mappings row with the picked plane recorded as the tenant's default.

I repeated this against a synthetic AWS-shaped plane with COLOCATED_TRIAL_BUCKETS enabled on the backend. The resulting storage mapping showed the derived S3 bucket, confirming the picker's choice flows through correctly end to end.

I also checked what happens against a real, older backend that predates this feature: the query fails with a clear GraphQL error, the picker just doesn't render, and the rest of the form stays fully usable. No crash.

Finally, with the companion PR's migration applied, a data plane marked closed disappears from the picker's options — the list and the backend agree on what's selectable.

Automated tests

tsc --noEmit and npm run lint both pass clean. I didn't add new unit tests since this is UI wiring around an already-tested backend contract; flagging that for reviewer input rather than assuming it's fine.

Playwright tests ran locally

  • Admin
  • Captures
  • Collections
  • HomePage
  • Login
  • Materialization

Screenshots

…onboarding

The authenticated dataPlanes query returns nothing for a brand-new signup
(no grants yet), so the onboarding plane picker needs the control-plane
API's unauthenticated publicDataPlanes query instead.
…rd claims

Adds a plane picker between the organization name field and the survey,
backed by the unauthenticated publicDataPlanes query (a brand-new signup
has no grants yet, so the authenticated dataPlanes query returns nothing).
Auto-preselects aws-us-east-1-c1 (falling back to the first available
plane) so submitting without touching the picker still records an
explicit, valid choice; fails safe to no picker + an omitted claim if the
plane list can't be loaded, letting the backend apply its own default.

generateUserClaim now forwards requestedDataPlane only when set, never as
an explicit null, so older backends that don't know the field are
unaffected.

Also registers PublicDataPlane in the urql cache's keys config (alongside
the existing DataPlane entry) to silence a normalization warning for the
new unkeyed type.

Verified end-to-end against a real local stack: picker lists the seeded
public plane, auto-selects it, and a completed registration produced a
storage_mappings row with the picked plane in data_planes.
- Move PublicDataPlaneNode + a named toPublicDataPlaneNode transform into
  src/api/gql/dataPlanes.ts, alongside the existing DataPlaneNode /
  toDataPlaneNode pair the sibling useDataPlanes() hook uses. The hook
  previously defined its own type and inlined an identity transform,
  diverging from where this codebase already models data-plane nodes.
- Destructure betaOnboard's generateUserClaim args by name instead of
  indexing (args[0], args[1], args[2]) — same positional-args signature
  (shared across all directive types), just clearer at the read site.
- Rename DataPlaneSelector's PREFERRED_DEFAULT to
  DEFAULT_PUBLIC_DATA_PLANE, matching the backend's constant of the same
  meaning and making the value's type (a plane name) obvious from the name.

No behavior change: tsc and lint both pass clean.
The Organization Name field overrides the 2px theme default to
borderRadius: 3 (6px). The picker sat right below it inheriting the
theme default, so the two adjacent inputs had visibly different
corners. Apply the same override.
- Sort options by cloudProvider then region: groupBy only groups
  correctly when the list is ordered by group, which previously worked
  only because plane names embed the provider.
- Extract the region/name label used by both getOptionLabel and
  renderOption so the two can't drift.
- Drop isOptionEqualToValue; value is always drawn from options, so
  MUI's default reference comparison already matches.
- Hoist the input sx to a module const, and reuse hasLength for the
  claim's non-empty check.
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

⚪ Code Health

No change to the dead-code surface.

48 Unused files

File imported nowhere — delete (or import) it.

     src/hooks/useDelay.ts
     src/hooks/useDraft.ts
     src/pages/NoGrants.tsx
     src/pages/OAuth.tsx
     src/services/encryption.ts
     src/types/global.ts
     src/types/vitest.ts
     src/components/graphs/TaskHoursByMonthGraph.tsx
     src/components/tables/Link.tsx
     src/context/LoopIndex/index.tsx
…and 38 more

64 Unused exports

Exported symbol with no references outside its own file — un-export it, or delete it if unused entirely

     src/context/Theme.tsx : logoColors
     src/context/Theme.tsx : intensifiedOutlineThick
     src/context/Theme.tsx : tableAlternateRowsSx
     src/context/Theme.tsx : draggableChipIconSx
     src/context/Theme.tsx : hiddenButAccessibleInput
     src/context/Theme.tsx : primaryColoredBackground_hovered
     src/context/Theme.tsx : detailsPanelBgColor
     src/context/Theme.tsx : menuBackgroundColor
     src/context/Theme.tsx : flexGrowToSiblingsSx
     src/context/Theme.tsx : shardTableRow
…and 54 more

26 Unused exported types

Exported type with no references outside its own file — un-export it, or delete it if unused entirely

     src/utils/billing-utils.ts : FREE_GB_BY_TIER
     src/types/index.ts : InferredSchemas
     src/types/index.ts : Shard
     src/components/shared/WizardDialog/index.ts : WizardStep
     src/api/dataPlanes.ts : AwsDnsEntry
     src/stores/ShardDetail/types.ts : TaskShardDetailsWithShard
     src/stores/ShardDetail/types.ts : ShardDetails
     src/components/tables/Logs/types.ts : RefreshLogsFunction
     src/types/schemaModels.ts : CollectionSchema
     src/components/editor/Bindings/types.ts : SelectedCollectionChangeData
…and 16 more

14 Unused exported enum members

An enum member referenced nowhere

     src/services/supabase.ts : CONNECTOR_TAGS
     src/services/supabase.ts : DRAFTS_EXT
     src/services/supabase.ts : TASKS_BY_DAY
     src/stores/Tables/hooks.ts : accessGrants
     src/stores/Tables/hooks.ts : accessLinks
     src/stores/Tables/hooks.ts : billing
     src/stores/Tables/hooks.ts : connectors
     src/stores/Tables/hooks.ts : entitySelector
     src/stores/Tables/hooks.ts : prefixes
     src/stores/Tables/hooks.ts : prefixAlerts
…and 4 more

5 Unused dependencies

In package.json but never imported

     package.json : @mui/lab
     package.json : @testing-library/jest-dom
     package.json : @urql/exchange-retry
     package.json : logrocket-react
     package.json : stripe

3 Unused devDependencies

In package.json devDependencies but never used

     package.json : @types/logrocket-react
     package.json : @types/react-inspector
     package.json : sharp

CI's Check Quality runs prettier, which I hadn't run locally. Formatting
only, no functional change.
@SeanWhelan
SeanWhelan requested a review from GregorShear August 7, 2026 10:25
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.

1 participant