From 12dff6b319c1a9316df11172b38bd33db04acbd6 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Tue, 28 Jul 2026 13:41:23 -0700 Subject: [PATCH] fix(ui): seed the settings query cache on save so the read-only view never shows stale values Greptile P2 on the new form: onSaved switched to the read-only view while invalidateQueries was still refetching, so the view rendered the pre-save cached settings until the GET resolved. The endpoint replaces the whole settings object with the submitted body, so that body is written into the query cache before leaving edit mode; the invalidate stays as background reconciliation. Applied to the default-user-settings form too, which had the same flash. Regression tests pin the behavior by never resolving the refetch. --- .../DefaultTeamSettingsForm.test.tsx | 9 ++++++--- .../default-team-settings/DefaultTeamSettingsForm.tsx | 4 ++++ .../DefaultUserSettingsForm.test.tsx | 9 ++++++--- .../default-user-settings/DefaultUserSettingsForm.tsx | 4 ++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx index d3b102c2a76..a80eb4fbe1e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.test.tsx @@ -185,11 +185,13 @@ describe("DefaultTeamSettingsForm", () => { expect(updateSettings).toHaveBeenCalledWith({ ...SAVED_BODY, team_member_permissions: null }); }); - it("returns to the read-only view showing the new values after a successful save", async () => { + it("shows the saved values in the read-only view immediately, without waiting for the refetch", async () => { const user = userEvent.setup(); - const updated = { ...SETTINGS, values: { ...SETTINGS.values, max_budget: 250 } }; const { updateSettings } = renderForm({ - fetchSettings: vi.fn().mockResolvedValueOnce(SETTINGS).mockResolvedValue(updated), + fetchSettings: vi + .fn() + .mockResolvedValueOnce(SETTINGS) + .mockImplementation(() => new Promise(() => {})), }); await enterEditMode(user); @@ -200,6 +202,7 @@ describe("DefaultTeamSettingsForm", () => { await waitFor(() => expect(updateSettings).toHaveBeenCalledTimes(1)); expect(await screen.findByRole("button", { name: "Edit Settings" })).toBeInTheDocument(); expect(await screen.findByText("250")).toBeInTheDocument(); + expect(screen.queryByText("100")).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Save Changes" })).not.toBeInTheDocument(); expect(NotificationsManager.success).toHaveBeenCalledWith("Default team settings updated successfully"); }); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx index b993606ff5f..56a7cc13844 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/_components/default-team-settings/DefaultTeamSettingsForm.tsx @@ -150,6 +150,10 @@ const SettingsForm = ({ initialValues, updateSettings, onCancel, onSaved }: Sett mutationFn: (values: DefaultTeamSettingsFormValues) => updateSettings(buildBody(values)), onSuccess: (_result, values) => { NotificationsManager.success("Default team settings updated successfully"); + queryClient.setQueryData(SETTINGS_QUERY_KEY, (existing) => ({ + field_schema: existing?.field_schema ?? {}, + values: buildBody(values), + })); queryClient.invalidateQueries({ queryKey: SETTINGS_QUERY_KEY }); form.reset(values); onSaved(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.test.tsx index bfdcc70fb0c..4212e7ddab2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.test.tsx @@ -234,11 +234,13 @@ describe("DefaultUserSettingsForm", () => { expect(updateSettings).toHaveBeenCalledWith({ ...SAVED_BODY, teams: null }); }); - it("returns to the read-only view showing the new values after a successful save", async () => { + it("shows the saved values in the read-only view immediately, without waiting for the refetch", async () => { const user = userEvent.setup(); - const updated = { ...SETTINGS, values: { ...SETTINGS.values, max_budget: 250 } }; const { updateSettings } = renderForm({ - fetchSettings: vi.fn().mockResolvedValueOnce(SETTINGS).mockResolvedValue(updated), + fetchSettings: vi + .fn() + .mockResolvedValueOnce(SETTINGS) + .mockImplementation(() => new Promise(() => {})), }); await enterEditMode(user); @@ -249,6 +251,7 @@ describe("DefaultUserSettingsForm", () => { await waitFor(() => expect(updateSettings).toHaveBeenCalledTimes(1)); expect(await screen.findByRole("button", { name: "Edit Settings" })).toBeInTheDocument(); expect(await screen.findByText("250")).toBeInTheDocument(); + expect(screen.queryByText("100")).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Save Changes" })).not.toBeInTheDocument(); expect(NotificationsManager.success).toHaveBeenCalledWith("Default user settings updated successfully"); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx index bb6cb2b9410..14520b71b31 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/default-user-settings/DefaultUserSettingsForm.tsx @@ -227,6 +227,10 @@ const SettingsForm = ({ initialValues, roleOptions, updateSettings, onCancel, on mutationFn: (values: DefaultUserSettingsFormValues) => updateSettings(buildBody(values)), onSuccess: (_result, values) => { NotificationsManager.success("Default user settings updated successfully"); + queryClient.setQueryData(SETTINGS_QUERY_KEY, (existing) => ({ + field_schema: existing?.field_schema ?? {}, + values: buildBody(values), + })); queryClient.invalidateQueries({ queryKey: SETTINGS_QUERY_KEY }); form.reset(values); onSaved();