fix(ui): keep alert save disabled while a save is in flight

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Krrish Dholakia 2026-11-04 22:33:40 +00:00
parent 5ae1ee088d
commit f19bd2a6f1
2 changed files with 42 additions and 1 deletions

View file

@ -613,6 +613,43 @@ describe("Settings", () => {
expect(savedSettings).not.toHaveProperty("alert_types");
});
it("should save the reverted alert types when a switch is reverted while a save is in flight", async () => {
mockGetCallbacksCall.mockResolvedValue(teamsOnlyCallbacksResponse);
const finishSaves: Array<() => void> = [];
const pendingSave = () =>
new Promise<void>((resolve) => {
finishSaves.push(resolve);
});
vi.mocked(setCallbacksCall).mockImplementationOnce(pendingSave).mockImplementationOnce(pendingSave);
const user = userEvent.setup();
render(<Settings {...defaultProps} />);
await user.click(await screen.findByRole("tab", { name: "Alerting Types" }));
const deprecationWarningsRow = screen.getByRole("row", { name: "Model Deprecation Warnings" });
const deprecationWarningsSwitch = within(deprecationWarningsRow).getByRole("switch");
const saveButton = screen.getByRole("button", { name: "Save Changes" });
await user.click(deprecationWarningsSwitch);
await user.click(saveButton);
await user.click(deprecationWarningsSwitch);
await user.click(saveButton);
await act(async () => {
finishSaves.forEach((finish) => finish());
});
await user.click(saveButton);
await waitFor(() => {
expect(vi.mocked(setCallbacksCall)).toHaveBeenLastCalledWith(
"token",
expect.objectContaining({
general_settings: expect.objectContaining({
alert_types: ["llm_exceptions", "spend_reports"],
}),
}),
);
});
});
it("should load alert types and webhook values from the Slack callback row", async () => {
mockGetCallbacksCall.mockResolvedValue(slackCallbacksResponseWithoutTopLevelAlertTypes);

View file

@ -324,6 +324,7 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
const [alertToWebhooks, setAlertToWebhooks] = useState<Record<string, string>>({});
const [activeAlerts, setActiveAlerts] = useState<string[]>([]);
const [savedAlerts, setSavedAlerts] = useState<string[]>([]);
const [isSavingAlerts, setIsSavingAlerts] = useState(false);
const [showAddCallbacksModal, setShowAddCallbacksModal] = useState(false);
const [callbackConfigs, setCallbackConfigs] = useState<any[]>([]);
@ -558,11 +559,14 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
},
};
setIsSavingAlerts(true);
try {
await setCallbacksCall(accessToken, payload);
} catch (error) {
toast.fromError(error);
return;
} finally {
setIsSavingAlerts(false);
}
setSavedAlerts(activeAlerts);
toast.success(t("loggingAndAlerts.alertTypes.toasts.saved"));
@ -708,7 +712,7 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
))}
</TableBody>
</Table>
<Button size="xs" className="mt-2" onClick={handleSaveAlerts}>
<Button size="xs" className="mt-2" onClick={handleSaveAlerts} disabled={isSavingAlerts}>
{t("loggingAndAlerts.alertTypes.saveChanges")}
</Button>