diff --git a/packages/dashboard/app/components/__tests__/AuthenticationSection.instances.test.tsx b/packages/dashboard/app/components/__tests__/AuthenticationSection.instances.test.tsx index dcaa9cc000..d9b39355bf 100644 --- a/packages/dashboard/app/components/__tests__/AuthenticationSection.instances.test.tsx +++ b/packages/dashboard/app/components/__tests__/AuthenticationSection.instances.test.tsx @@ -39,10 +39,12 @@ function renderSection(providers: AuthProvider[]) { } describe("AuthenticationSection credential instances", () => { - it("keeps a single account card free of instance chrome", () => { + it("keeps a single account card free of pending account field chrome", () => { renderSection([{ id: "brave", name: "Brave", authenticated: true, type: "api_key", instances: [{ instanceId: "default", authenticated: true, isDefault: true, type: "api_key" }] }]); expect(screen.queryByTestId("auth-instances-brave")).not.toBeInTheDocument(); expect(screen.getByText("Add another account")).toBeInTheDocument(); + expect(screen.queryByLabelText("Account name")).not.toBeInTheDocument(); + expect(screen.queryByTestId("auth-pending-instance-brave")).not.toBeInTheDocument(); }); it("scopes every multi-instance API-key save to its account and pending row", () => { @@ -58,12 +60,18 @@ describe("AuthenticationSection credential instances", () => { fireEvent.click(screen.getByText("Add another account")); const pending = screen.getByTestId("auth-pending-instance-brave"); + const accountName = within(pending).getByLabelText("Account name"); + expect(accountName).toHaveAttribute("id", "auth-pending-instance-brave-label"); + expect(accountName.closest(".auth-pending-instance-field")).toBeInTheDocument(); + fireEvent.change(accountName, { target: { value: "Work" } }); + expect(accountName).toHaveValue("Work"); fireEvent.change(within(pending).getByPlaceholderText("Enter API key"), { target: { value: "pending-key" } }); fireEvent.click(within(pending).getByText("Save")); - expect(handlers.handleSaveApiKey).toHaveBeenLastCalledWith("brave", "acct-new", undefined); + expect(handlers.handleSaveApiKey).toHaveBeenLastCalledWith("brave", "acct-new", "Work"); + expect(within(pending).getByText("Cancel")).toBeInTheDocument(); }); - it("binds OAuth instance actions to the selected instance", () => { + it("binds OAuth instance actions to the selected instance and labels pending accounts", () => { const handlers = renderSection([{ id: "github-copilot", name: "GitHub", authenticated: true, type: "oauth", instances: [ { instanceId: "default", authenticated: true, isDefault: true, type: "oauth" }, { instanceId: "acct-two", authenticated: true, isDefault: false, type: "oauth" }, @@ -71,5 +79,15 @@ describe("AuthenticationSection credential instances", () => { const row = within(screen.getByTestId("auth-instances-github-copilot")).getByText("acct-two").closest(".auth-instance-row") as HTMLElement; fireEvent.click(within(row).getByText("Logout")); expect(handlers.handleLogout).toHaveBeenCalledWith("github-copilot", "acct-two"); + + fireEvent.click(screen.getByText("Add another account")); + const pending = screen.getByTestId("auth-pending-instance-github-copilot"); + const accountName = within(pending).getByLabelText("Account name"); + expect(accountName).toHaveAttribute("id", "auth-pending-instance-github-copilot-label"); + fireEvent.change(accountName, { target: { value: "Personal" } }); + expect(accountName).toHaveValue("Personal"); + expect(within(pending).getByText("Cancel")).toBeInTheDocument(); + fireEvent.click(within(pending).getByText("Login")); + expect(handlers.handleLogin).toHaveBeenCalledWith("github-copilot", "acct-new", "Personal"); }); }); diff --git a/packages/dashboard/app/components/settings/sections/AuthenticationSection.css b/packages/dashboard/app/components/settings/sections/AuthenticationSection.css index e8cd0cc9f1..2d4c0f995d 100644 --- a/packages/dashboard/app/components/settings/sections/AuthenticationSection.css +++ b/packages/dashboard/app/components/settings/sections/AuthenticationSection.css @@ -6,8 +6,12 @@ Settings modal so account controls stay reachable without introducing a second c .auth-instance-controls { display: flex; flex-direction: column; gap: var(--space-sm); margin-top: var(--space-sm); } .auth-instance-list { display: flex; flex-direction: column; gap: var(--space-xs); } .auth-instance-row, .auth-instance-pending { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-xs); } +.auth-instance-pending { gap: var(--space-sm); padding: var(--space-sm); } +.auth-pending-instance-field { display: flex; flex: 1 1 var(--space-2xl); flex-direction: column; gap: var(--space-xs); } +.auth-pending-instance-label { color: var(--text-muted); font-size: var(--font-size-xs); font-weight: 600; } .auth-instance-row > span { flex: 1; min-width: 0; } @media (max-width: 768px) { .auth-instance-row, .auth-instance-pending { align-items: stretch; } .auth-instance-row > .btn, .auth-instance-pending > .btn { flex: 1; } + .auth-pending-instance-field { flex-basis: 100%; } } diff --git a/packages/dashboard/app/components/settings/sections/AuthenticationSection.tsx b/packages/dashboard/app/components/settings/sections/AuthenticationSection.tsx index d569204f1b..13e49ffdf9 100644 --- a/packages/dashboard/app/components/settings/sections/AuthenticationSection.tsx +++ b/packages/dashboard/app/components/settings/sections/AuthenticationSection.tsx @@ -254,7 +254,16 @@ export function AuthenticationSection({ auth, form, setForm }: AuthenticationSec })} } {pending &&
- setPendingInstances((current) => ({ ...current, [provider.id]: { ...pending, label: event.target.value } }))} /> + {/* + FNXC:ProviderAuth 2026-08-02-05:27: + Each pending credential needs a visible Account name label associated with its provider-specific input. The padded field keeps its purpose separate from credential actions at desktop and mobile widths without changing instance identity or save/login payloads. + */} +
+ + setPendingInstances((current) => ({ ...current, [provider.id]: { ...pending, label: event.target.value } }))} /> +
{providerSupportsApiKey(provider) ? renderApiKeySection(provider, pending.instanceId, pending.label, true) : renderAvailableOAuthActions(provider, pending.instanceId, pending.label || undefined)}