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.
This commit is contained in:
ryan-crabbe-berri 2026-07-28 13:41:23 -07:00
parent a2be3986b2
commit 12dff6b319
4 changed files with 20 additions and 6 deletions

View file

@ -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");
});

View file

@ -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<DefaultTeamSettings>(SETTINGS_QUERY_KEY, (existing) => ({
field_schema: existing?.field_schema ?? {},
values: buildBody(values),
}));
queryClient.invalidateQueries({ queryKey: SETTINGS_QUERY_KEY });
form.reset(values);
onSaved();

View file

@ -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");

View file

@ -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<InternalUserSettings>(SETTINGS_QUERY_KEY, (existing) => ({
field_schema: existing?.field_schema ?? {},
values: buildBody(values),
}));
queryClient.invalidateQueries({ queryKey: SETTINGS_QUERY_KEY });
form.reset(values);
onSaved();