refactor(frontend): decompose DBProvidersPage into components hooks and helpers#14049
refactor(frontend): decompose DBProvidersPage into components hooks and helpers#14049tarciorodrigues wants to merge 8 commits into
Conversation
Golden-master coverage for the untouched 866-line page ahead of the component extraction: provider list order and badges, active-provider resolution from LANGFLOW_KNOWLEDGE_BACKEND, chroma panel action state, required-field gating of Save and Test connection, masked secret hydration, POST vs PATCH persistence incl. Credential typing and provider activation, and the coming-soon stub panel. Behavior-only assertions so each upcoming move can run against this suite unchanged.
Verbatim move of the inline list-item into components/ProviderListItem with its explicit prop types. Characterization suite green.
Verbatim moves of TextFieldRow and BooleanFieldRow into components/, carrying the MASKED_VALUE constant with its only consumer. Characterization suite green.
Verbatim move of the configuration panel into components/, importing the already-extracted field rows; orphan imports pruned from index. Characterization suite green.
…lper Verbatim move of the pure buildBackendConfigPayload function into helpers/build-backend-config-payload.ts. Characterization suite green.
Verbatim move of the global-variable query, the create-or-update setVariable primitive and activateProvider into hooks/useDBProviderVariables. Characterization suite green.
Verbatim move of getFieldValue, hasConfiguredValue, isHydrated and canSave into hooks/useDBProviderFields, parameterized on the selected provider, stored globals and session edits. Characterization suite green.
Verbatim move of handleSave, handleTestConnection and handleUseChroma into hooks/useDBProviderActions together with the ApiError helper; the hook owns the test-connection mutation and alert wiring. index.tsx is now a 153-line composition of the extracted hooks and components. Characterization suite green.
WalkthroughChangesDatabase provider configuration
Estimated code review effort: 4 (Complex) | ~45 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 8 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (8 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts (1)
241-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winChroma lookup relies on array position.
DB_PROVIDER_OPTIONS[0]assumes Chroma is always first; reordering the list would silently activate the wrong provider with no compiler warning.♻️ Proposed fix
- const chromaProvider = DB_PROVIDER_OPTIONS[0]; + const chromaProvider = DB_PROVIDER_OPTIONS.find((p) => p.id === "chroma"); + if (!chromaProvider) return;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts` around lines 241 - 245, Update handleUseChroma to retrieve the Chroma option by its stable provider identifier or explicit property instead of relying on DB_PROVIDER_OPTIONS[0]. Preserve the existing activateProvider and setSelectedProviderId flow once the Chroma option is found.src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/helpers/build-backend-config-payload.ts (1)
24-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded fallback defaults duplicate field-definition defaults.
These literal fallbacks (
"us-east-1","vector_field","text",true) mirror values that likely already live asdefaultValueon the corresponding fields indbProviderConstants. Two sources of truth for the same default risks silent drift between what the UI shows/pre-fills and what's actually sent to the backend if one is updated without the other.Also applies to: 34-39
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/helpers/build-backend-config-payload.ts` at line 24, Update buildBackendConfigPayload to use the corresponding field definitions’ defaultValue from dbProviderConstants for cloud_region and the other affected payload fields, instead of duplicating "us-east-1", "vector_field", "text", and true. Preserve explicitly provided literalFields values while falling back to the centralized field defaults.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/__tests__/DBProvidersPage.test.tsx`:
- Around line 25-30: Add error-path coverage to the DBProvidersPage test suite:
configure mockTestMutateAsync and the save mutation to reject, and cover missing
configuration causing setErrorData to be used. Assert mockSetErrorData is called
with the surfaced error and preserve existing success and disabled-button tests.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/BooleanFieldRow.tsx`:
- Around line 20-24: Update BooleanFieldRow’s switch aria-label to reuse the
same translated label value produced by the visible field label’s t(...) call,
including its defaultValue fallback, instead of using raw field.label. Keep the
visible label rendering unchanged and ensure both accessible and visual labels
stay consistent across locales.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/ProviderConfigurationPanel.tsx`:
- Around line 155-160: Update the focus handling around isConfigured in
ProviderConfigurationPanel so it only calls onSecretEditingChange for a
configured secret and does not initialize variableValues through
onVariableChange with an empty string. Leave draft creation to the existing
onChange path, preserving masking and unedited state after blur.
- Line 50: Update hasUnsavedChanges in ProviderConfigurationPanel to evaluate
only draft values belonging to the currently selected provider, rather than any
entries persisted in variableValues. Reuse the selected-provider identity and
existing variableValues structure from the panel/index flow, preserving “Use”
when the selected provider has no draft and “Save and use” only when it does.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts`:
- Around line 24-26: Update getErrorDetail to accept or access the existing
translation function t and translate the fallback message when
ApiError.response.data.detail is absent, while preserving API-provided detail
values unchanged.
- Around line 79-95: Add a shared secret-aware getMissingRequiredFields helper
in useDBProviderFields, reusing the same variableValues, hasConfiguredValue, and
getFieldValue rules as canSave. Update both handleSave and handleTestConnection
in useDBProviderActions to use this helper, and localize the missing-fields
sentence through t() with interpolated field labels instead of raw string
concatenation.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderVariables.ts`:
- Around line 38-40: Update the existing-variable branch in the variable
persistence logic around findVariable and updateGlobalVariable so secret values
preserve credential classification. When isSecret is true, include type:
"Credential" in the PATCH payload, or reject and recreate a conflicting Generic
variable; retain existing behavior for non-secret updates.
---
Nitpick comments:
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/helpers/build-backend-config-payload.ts`:
- Line 24: Update buildBackendConfigPayload to use the corresponding field
definitions’ defaultValue from dbProviderConstants for cloud_region and the
other affected payload fields, instead of duplicating "us-east-1",
"vector_field", "text", and true. Preserve explicitly provided literalFields
values while falling back to the centralized field defaults.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts`:
- Around line 241-245: Update handleUseChroma to retrieve the Chroma option by
its stable provider identifier or explicit property instead of relying on
DB_PROVIDER_OPTIONS[0]. Preserve the existing activateProvider and
setSelectedProviderId flow once the Chroma option is found.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a68a7855-6620-4590-b916-8491297e392a
📒 Files selected for processing (10)
src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/__tests__/DBProvidersPage.test.tsxsrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/BooleanFieldRow.tsxsrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/ProviderConfigurationPanel.tsxsrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/ProviderListItem.tsxsrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/TextFieldRow.tsxsrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/helpers/build-backend-config-payload.tssrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.tssrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderFields.tssrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderVariables.tssrc/frontend/src/pages/SettingsPage/pages/DBProvidersPage/index.tsx
| const mockTestMutateAsync = jest.fn(() => | ||
| Promise.resolve({ ok: true, message: "cluster green" }), | ||
| ); | ||
|
|
||
| const mockSetSuccessData = jest.fn(); | ||
| const mockSetErrorData = jest.fn(); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
No test exercises the error path despite the scaffolding being present.
mockTestMutateAsync always resolves successfully and mockSetErrorData is never asserted as called anywhere in the suite. The coding guideline requires coverage for "positive, negative, edge, and error cases," but this suite only covers happy-path save/activation and disabled-button gating — there's no test for a rejected useTestDBProviderConnection call, a rejected save, or missing-config error surfacing via setErrorData.
🧪 Example error-path test
+ it("surfaces a connection-test failure via setErrorData", async () => {
+ mockTestMutateAsync.mockRejectedValueOnce(new Error("cluster red"));
+ const user = userEvent.setup();
+ render(<DBProvidersPage />);
+ await user.click(screen.getByTestId("db-provider-item-opensearch"));
+ // ...fill required fields...
+ await user.click(screen.getByTestId("db-provider-test-connection"));
+ await waitFor(() => expect(mockSetErrorData).toHaveBeenCalled());
+ });As per coding guidelines, "follow frontend test naming conventions... and coverage for positive, negative, edge, and error cases" for frontend test files.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/__tests__/DBProvidersPage.test.tsx`
around lines 25 - 30, Add error-path coverage to the DBProvidersPage test suite:
configure mockTestMutateAsync and the save mutation to reject, and cover missing
configuration causing setErrorData to be used. Assert mockSetErrorData is called
with the surfaced error and preserve existing success and disabled-button tests.
Source: Coding guidelines
| <span className="text-[12px] font-medium"> | ||
| {t(`settings.dbProviders.fields.${field.variableKey}.label`, { | ||
| defaultValue: field.label, | ||
| })} | ||
| </span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
aria-label bypasses i18n while the visible label is translated.
Line 41 uses the raw field.label for the switch's accessible name, while the visible label (Lines 20-24) goes through t(). Screen-reader users on non-English locales will hear the English label while sighted users see the translated one.
🌐 Proposed fix
<Switch
checked={value}
onCheckedChange={onChange}
disabled={disabled}
- aria-label={field.label}
+ aria-label={t(`settings.dbProviders.fields.${field.variableKey}.label`, {
+ defaultValue: field.label,
+ })}
data-testid={`db-provider-toggle-${field.variableKey}`}
/>Also applies to: 41-41
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/BooleanFieldRow.tsx`
around lines 20 - 24, Update BooleanFieldRow’s switch aria-label to reuse the
same translated label value produced by the visible field label’s t(...) call,
including its defaultValue fallback, instead of using raw field.label. Keep the
visible label rendering unchanged and ensure both accessible and visual labels
stay consistent across locales.
| const isComingSoon = provider.status === "coming_soon"; | ||
| const isActive = activeProviderId === provider.id; | ||
| // True when the user has interacted with any field this session. | ||
| const hasUnsavedChanges = Object.keys(variableValues).length > 0; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Scope unsaved changes to the selected provider.
variableValues persists across provider selection in index.tsx Lines 108-111, so drafts for another provider can incorrectly change this panel’s action from “Use” to “Save and use.”
Proposed fix
- const hasUnsavedChanges = Object.keys(variableValues).length > 0;
+ const hasUnsavedChanges = provider.configFields.some(
+ (field) => field.variableKey in variableValues,
+ );📝 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 hasUnsavedChanges = Object.keys(variableValues).length > 0; | |
| const hasUnsavedChanges = provider.configFields.some( | |
| (field) => field.variableKey in variableValues, | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/ProviderConfigurationPanel.tsx`
at line 50, Update hasUnsavedChanges in ProviderConfigurationPanel to evaluate
only draft values belonging to the currently selected provider, rather than any
entries persisted in variableValues. Reuse the selected-provider identity and
existing variableValues structure from the panel/index flow, preserving “Use”
when the selected provider has no draft and “Save and use” only when it does.
| const isConfigured = | ||
| field.isSecret && | ||
| globalVariables.some((v) => v.name === field.variableKey); | ||
| if (isConfigured && !(field.variableKey in variableValues)) { | ||
| onSecretEditingChange(field.variableKey, true); | ||
| onVariableChange(field.variableKey, ""); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not create an empty secret draft on focus.
Adding the empty key makes TextFieldRow.tsx Lines 36-40 treat the field as having a new value. After blur, the configured secret remains blank instead of becoming masked again and is falsely counted as edited.
Typing already creates the draft through onChange, so only toggle editing here.
Proposed fix
if (isConfigured && !(field.variableKey in variableValues)) {
onSecretEditingChange(field.variableKey, true);
- onVariableChange(field.variableKey, "");
}📝 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 isConfigured = | |
| field.isSecret && | |
| globalVariables.some((v) => v.name === field.variableKey); | |
| if (isConfigured && !(field.variableKey in variableValues)) { | |
| onSecretEditingChange(field.variableKey, true); | |
| onVariableChange(field.variableKey, ""); | |
| const isConfigured = | |
| field.isSecret && | |
| globalVariables.some((v) => v.name === field.variableKey); | |
| if (isConfigured && !(field.variableKey in variableValues)) { | |
| onSecretEditingChange(field.variableKey, true); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/components/ProviderConfigurationPanel.tsx`
around lines 155 - 160, Update the focus handling around isConfigured in
ProviderConfigurationPanel so it only calls onSecretEditingChange for a
configured secret and does not initialize variableValues through
onVariableChange with an empty string. Leave draft creation to the existing
onChange path, preserving masking and unedited state after blur.
| const getErrorDetail = (error: unknown) => | ||
| (error as ApiError)?.response?.data?.detail || | ||
| "An unexpected error occurred. Please try again."; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fallback error text isn't localized.
Every other user-facing string in this file goes through t(); this fallback message is the one exception, so an API error without a detail field always renders in English.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts`
around lines 24 - 26, Update getErrorDetail to accept or access the existing
translation function t and translate the fallback message when
ApiError.response.data.detail is absent, while preserving API-provided detail
values unchanged.
| if (!canSave) { | ||
| setErrorData({ | ||
| title: t("settings.dbProviders.errorMissingConfig"), | ||
| list: [ | ||
| `${selectedProvider.label} requires ${selectedProvider.configFields | ||
| .filter( | ||
| (field): field is DBProviderTextField => | ||
| field.kind !== "boolean" && | ||
| field.required && | ||
| !getFieldValue(field).trim(), | ||
| ) | ||
| .map((field) => field.label) | ||
| .join(", ")}.`, | ||
| ], | ||
| }); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Missing-field error list mismatches canSave's own secret-handling rule, and bypasses i18n entirely.
Two issues in this duplicated block:
- The filter
!getFieldValue(field).trim()doesn't match the secret-aware ruleuseDBProviderFieldsuses forcanSave(variableValues[...]?.trim() || hasConfiguredValue(...)). A required secret field that's already saved (masked, sogetFieldValuereturns"") will still be listed as "required" here even though it isn't the reasoncanSaveis false — confusing users into re-entering credentials that are already configured. - The
listsentence is built via raw string concatenation and never passed throught()(unliketitleon the same call), so this user-facing message is never localized.
Both instances (handleSave and handleTestConnection) duplicate the same logic, so fixing once via a shared helper resolves both.
🔧 Suggested direction
Expose a secret-aware helper from useDBProviderFields (mirroring canSave's per-field predicate) and consume it here instead of re-deriving from getFieldValue:
// in useDBProviderFields.ts
const getMissingRequiredFields = () =>
selectedProvider.configFields.filter(
(field): field is DBProviderTextField =>
field.kind !== "boolean" &&
field.required &&
!(field.isSecret
? Boolean(variableValues[field.variableKey]?.trim()) || hasConfiguredValue(field.variableKey)
: Boolean(getFieldValue(field).trim())),
);Then in useDBProviderActions.ts, build the list from getMissingRequiredFields().map(f => f.label) and wrap the sentence in t(...) with interpolated values instead of a raw template literal.
Also applies to: 158-174
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderActions.ts`
around lines 79 - 95, Add a shared secret-aware getMissingRequiredFields helper
in useDBProviderFields, reusing the same variableValues, hasConfiguredValue, and
getFieldValue rules as canSave. Update both handleSave and handleTestConnection
in useDBProviderActions to use this helper, and localize the missing-fields
sentence through t() with interpolated field labels instead of raw string
concatenation.
| const existingVariable = findVariable(name); | ||
| if (existingVariable) { | ||
| await updateGlobalVariable({ id: existingVariable.id, value }); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether the PATCH payload supports changing variable type.
rg -n -C5 '\bPatchGlobalVariablesParams\b' src/frontend/src
rg -n -C5 '"Credential"|"Generic"' src/frontend/src/controllers src/frontend/src/typesRepository: langflow-ai/langflow
Length of output: 4875
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the hook and related API/model definitions around variable updates.
FILE1='src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderVariables.ts'
FILE2='src/frontend/src/controllers/API/queries/variables/use-patch-global-variables.ts'
FILE3='src/frontend/src/types/global_variables/index.ts'
for f in "$FILE1" "$FILE2" "$FILE3"; do
echo "===== $f ====="
nl -ba "$f" | sed -n '1,220p'
echo
done
# Search for other update paths that might include `type`.
rg -n -C3 '\btype\b|isSecret|Credential|Generic|usePatchGlobalVariables|updateGlobalVariable' src/frontend/src/pages src/frontend/src/controllers src/frontend/src/typesRepository: langflow-ai/langflow
Length of output: 301
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE1='src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderVariables.ts'
FILE2='src/frontend/src/controllers/API/queries/variables/use-patch-global-variables.ts'
FILE3='src/frontend/src/types/global_variables/index.ts'
for f in "$FILE1" "$FILE2" "$FILE3"; do
echo "===== $f ====="
sed -n '1,220p' "$f" | cat -n
echo
done
echo "===== search ====="
rg -n -C4 'updateGlobalVariable|usePatchGlobalVariables|PatchGlobalVariablesParams|type:\s*["'\'']Credential["'\'']|type:\s*["'\'']Generic["'\'']|isSecret' src/frontend/srcRepository: langflow-ai/langflow
Length of output: 50376
Preserve credential classification on update. This path updates only value, so a secret written over an existing Generic variable keeps the wrong type and can bypass the masking contract. Add type: "Credential" to the PATCH contract or reject/recreate the conflicting variable when isSecret is true.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/frontend/src/pages/SettingsPage/pages/DBProvidersPage/hooks/useDBProviderVariables.ts`
around lines 38 - 40, Update the existing-variable branch in the variable
persistence logic around findVariable and updateGlobalVariable so secret values
preserve credential classification. When isSecret is true, include type:
"Credential" in the PATCH payload, or reject and recreate a conflicting Generic
variable; retain existing behavior for non-secret updates.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release-1.11.0 #14049 +/- ##
==================================================
+ Coverage 60.16% 60.73% +0.56%
==================================================
Files 2333 2397 +64
Lines 228353 231475 +3122
Branches 32038 33901 +1863
==================================================
+ Hits 137386 140579 +3193
+ Misses 89351 89280 -71
Partials 1616 1616
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Why
LE-1735 Tier A componentization — work package 2.
DBProvidersPage/index.tsxwas an 866-line monolith mixing the provider list, the configuration panel, field rows, a pure payload builder, variable persistence, field-value resolution and the save/test/activate flows — with zero test coverage. This PR decomposes it into named files, behavior-preserving, with a characterization suite pinned before any move.What
LANGFLOW_KNOWLEDGE_BACKEND, chroma panel state, required-field gating of Save and Test connection, masked-secret hydration, POST vs PATCH persistence including Credential typing and provider activation, and the coming-soon stub.components/ProviderListItem.tsx,components/TextFieldRow.tsx+components/BooleanFieldRow.tsx(carryingMASKED_VALUEwith its only consumer),components/ProviderConfigurationPanel.tsx,helpers/build-backend-config-payload.ts,hooks/useDBProviderVariables.ts,hooks/useDBProviderFields.tsandhooks/useDBProviderActions.ts.index.tsxis now a 153-line composition of the extracted pieces. No exported symbol changed; everything new lives under the page's own directory.Tests
make test_frontend: 445 of 446 suites green — the one red suite (AuthModal) is a userEvent 5s timeout under a loaded parallel run, passes 8/8 in isolation and does not touch this diff.make format_frontendno drift; biome check clean on the page directory.ProviderListItem/MASKED_VALUEmatches in modelProviderModal are same-named locals of that modal).Note
The mask edit/cancel focus flow could not be driven by browser automation on this machine — the password-manager extension detaches the debugger when a password field gains focus; that flow is covered by the characterization suite. A valid Test Connection needs a real cluster and was not exercised.
Refs LE-1735
Screenshots
DB Providers page — OpenSearch panel
Light
Before
After
Dark
Before
After
Summary by CodeRabbit
New Features
Tests