refactor(web): split provider-editor-modal into focused modules (LUM-2224)#33256
Conversation
…2224) Extract from the 910-line ProviderEditorContent component: - provider-editor-constants.ts: types, constants, pure helpers - use-provider-credentials-list.ts: TanStack Query hook for fetching available credentials with org-readiness gating and error reporting - provider-editor-api-key-section.tsx: API Key field + Advanced credential-reference disclosure sub-component Main file: 910 → 679 lines. Sub-component owns its own disclosure state (isAdvancedExpanded, isCreatingNewCredential, newCredentialName); parent retains apiKeyValue and credential because the save handler needs them. Closes LUM-2224 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…normalize comments Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Nice work! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
There was a problem hiding this comment.
✦ APPROVE
Value: Phase 2 of the settings/AI decomposition arc (sibling to #33246 which landed ~50 min ago). provider-editor-modal.tsx goes 910 → 662 (−248) by extracting three single-responsibility modules. Every extracted module is testable in isolation, has zero unintended coupling back to the modal, and useStoredCredentialPresence from #33246 is correctly retained at the call site.
What this does:
provider-editor-constants.ts(55 lines, zero React) — types (AuthType), constants (CONNECTION_PROVIDERS,AUTH_TYPE_DISPLAY_NAMES), pure helpers (parseCredentialRef,connectionSaveErrorMessage). Importable by tests without dragging React in.use-provider-credentials-list.ts(77 lines) — TanStack hook wrappingsecretsGet. Honors the full daemon-config-hooks contract:useIsOrgReadygate,shouldRetryDaemonError,captureError(bestEffort),useMemo'd tuple queryKey[QK, assistantId],staleTime: 30_000. Query key is module-private (correctly — it's an implementation detail, not a shared constant).provider-editor-api-key-section.tsx(201 lines) — sub-component owning ONLY the disclosure UI state (isAdvancedExpanded,isCreatingNewCredential,newCredentialName). Parent retainsapiKeyValue/credential/isSavingKeybecause those flow into the save handler. Clean ownership boundary.provider-editor-modal.tsx— replaces inline definitions with imports; second commit (a554624) self-addresses Devin's stale-comment finding and DRYs the save handler's manual credential-ref parsing ontoparseCredentialReffrom the new constants module.
Anti-pattern cross-check
- No
asruntime-boundary casts in any of the 4 files. ✓ useStoredCredentialPresenceretained at the modal call site (2 occurrences) — #33246's hook is NOT dropped. ✓- daemon-config-hooks.md compliance on
useProviderCredentialsList:useIsOrgReady✓,shouldRetryDaemonError✓,captureError(bestEffort: true)✓,assertHasResponse+response.okgate beforedata!✓,useMemo'd query-key tuple ✓. data!.secrets ?? data!.accountsnon-null assertions are post-assertHasResponse+!response.okthrows — safe.- Semantic tokens used throughout (
var(--content-tertiary),var(--content-secondary),var(--system-negative-strong)is unchanged from main);text-body-small-defaulttypography token. design-system-review.md compliant. ✓ - State ownership correctly partitioned: pure UI disclosure state in sub-component, save-handler-bound state in parent. No prop drilling of state that doesn't belong to the parent.
- No SSE/seq territory. No vembda territory. (R11e clean — Vargas + Mahmoud untouched.)
Devin's first-commit finding (auto-fixed at HEAD)
At 03fc2c7b Devin flagged: "Stale comment describes removed isCreatingNewCredential condition in shouldShowAdvancedSection" — the comment at lines 400–402 still referenced a condition that had been moved into the sub-component.
Devin self-fixed in a554624: updated the parent comment to describe only the two remaining conditions, and the sub-component's comment now delegates to the parent's showAdvancedSection prop. Verified at HEAD. The second commit also handled 4 other first-principles audit items (DRY save handler onto parseCredentialRef, dedupe import type lines, normalize /// Swift-style comments to //, drop cross-file refactoring-lineage comments).
Self-improvement worth calling out: Devin's own "audit fixes" second commit catching /// triple-slash Swift comments (25 total, all in this one file — clearly drifted in from someone's Swift muscle memory) is exactly the kind of first-principles housekeeping that should ride along with extraction PRs. The lineage-comment cleanup ("Mirrors X in profile-editor-modal.tsx") is also right — those describe how the code got here, not what it does.
Merge gate at HEAD a554624: Vex ✓ · Codex 👍 ("Didn't find any major issues. Nice work!") + 👍 reaction on PR description ✓ · Devin: 1 issue found at first commit, self-fixed at HEAD with inline clearance ✓ · CI 7/7 green ✓. Will squash-merge.
Vellum Constitution — Distinct: a clean decomposition where the parent's line count drops sharply but the new files each have a single, easy-to-name reason to exist — and the second commit's self-audit (Swift comment style drift, lineage refs, save-handler DRY) shows the kind of taste that compounds when the next person reads the diff.
Prompt / plan
LUM-2224 — Phase 2 of the settings/AI domain decomposition. Split
provider-editor-modal.tsx(910 lines) into focused, single-responsibility modules, then audit every extraction from first principles.What changed
New files:
provider-editor-constants.ts(55 lines) — Types (AuthType), constants (CONNECTION_PROVIDERS,AUTH_TYPE_DISPLAY_NAMES), and pure helpers (parseCredentialRef,connectionSaveErrorMessage). Zero React dependencies.use-provider-credentials-list.ts(77 lines) — TanStack Query hook wrappingsecretsGet()with org-readiness gating (useIsOrgReady()), retry logic (shouldRetryDaemonError), and Sentry error reporting. Query key is module-private (implementation detail of the hook, not a shared constant).provider-editor-api-key-section.tsx(201 lines) — Sub-component encapsulating the API Key input field + Advanced credential-reference disclosure. Owns its own UI state (isAdvancedExpanded,isCreatingNewCredential,newCredentialName). Parent passes values and callbacks via props.Modified:
provider-editor-modal.tsx(910 → 662 lines, −248) — Imports from the three new modules. Inline constants, credential query, and API key JSX replaced with extracted abstractions. Parent retainsapiKeyValueandcredentialstate (needed by save handler).Design decisions
apiKeyValue,credential,isSavingKey(all flow into the save handler). Sub-component ownsisAdvancedExpanded,isCreatingNewCredential,newCredentialName(pure UI disclosure state with no save-handler dependency).PROVIDER_CREDENTIALS_LIST_QKis an implementation detail ofuseProviderCredentialsList, not a shared constant. It lives in the hook file, not the constants file.First-principles audit fixes (second commit)
parseCredentialRef'scredential/service/fieldsplitting logic. Replaced with a call to the extracted helper, eliminating the duplication.provider-editor-modal.tsxandprovider-editor-api-key-section.tsxthat still referencedisCreatingNewCredentialas a condition forshouldShowAdvancedSectionafter it was moved to the sub-component.import typelines from the same module inprovider-editor-api-key-section.tsx.///comment style normalization — All 25 triple-slash comments inapps/web/src/were in this one file (a Swift convention). Normalized to//for consistency with the rest of the codebase.Mirrors X in profile-editor-modal.tsxreferences that describe refactoring lineage rather than what the code does.Test plan
bun test src/domains/settings/ai/)oauth-apps.ts/oauth-providers.tsconfirmed on main — unrelated)Closes LUM-2224
Link to Devin session: https://app.devin.ai/sessions/4b3b130bbf274e5487da3b4992883093
Requested by: @ashleeradka