feat(ui): add text input for String general settings so retention periods can be set from the Admin UI
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Waiting to run
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-25 01:00:39 +00:00
parent b91831270d
commit 24e49d71d7
2 changed files with 67 additions and 1 deletions

View file

@ -184,6 +184,63 @@ it("persists a List setting typed as comma-separated text as a trimmed string ar
]);
});
it("persists a String setting typed into its text input", async () => {
vi.mocked(getGeneralSettingsCall).mockResolvedValue([
{
field_name: "maximum_daily_tag_spend_retention_period",
field_type: "String",
field_value: null,
field_description: "retention",
stored_in_db: null,
},
]);
vi.mocked(updateConfigFieldSetting).mockClear();
const user = userEvent.setup();
renderWithProviders(<GeneralSettings accessToken="token" userRole="Admin" userID="user" />);
await user.click(screen.getByRole("tab", { name: "General" }));
const input = await screen.findByRole("textbox", { name: "maximum_daily_tag_spend_retention_period" });
expect(input).toHaveValue("");
fireEvent.change(input, { target: { value: "30d" } });
await user.click(
within(screen.getByRole("row", { name: /maximum_daily_tag_spend_retention_period/ })).getByRole("button", {
name: "Update",
}),
);
expect(vi.mocked(updateConfigFieldSetting).mock.calls).toEqual([
["token", "maximum_daily_tag_spend_retention_period", "30d"],
]);
expect(screen.getByText("In DB")).toBeInTheDocument();
});
it("clears a stored String setting when Update is clicked on an emptied input", async () => {
vi.mocked(getGeneralSettingsCall).mockResolvedValue([
{
field_name: "maximum_daily_tag_spend_retention_period",
field_type: "String",
field_value: "30d",
field_description: "retention",
stored_in_db: true,
},
]);
vi.mocked(updateConfigFieldSetting).mockClear();
vi.mocked(deleteConfigFieldSetting).mockClear();
const user = userEvent.setup();
renderWithProviders(<GeneralSettings accessToken="token" userRole="Admin" userID="user" />);
await user.click(screen.getByRole("tab", { name: "General" }));
const input = await screen.findByRole("textbox", { name: "maximum_daily_tag_spend_retention_period" });
expect(input).toHaveValue("30d");
fireEvent.change(input, { target: { value: "" } });
await user.click(
within(screen.getByRole("row", { name: /maximum_daily_tag_spend_retention_period/ })).getByRole("button", {
name: "Update",
}),
);
expect(vi.mocked(deleteConfigFieldSetting).mock.calls).toEqual([
["token", "maximum_daily_tag_spend_retention_period"],
]);
expect(updateConfigFieldSetting).not.toHaveBeenCalled();
});
it("clears a stored List setting when Update is clicked on an emptied input", async () => {
vi.mocked(getGeneralSettingsCall).mockResolvedValue([
{

View file

@ -114,6 +114,15 @@ const SettingValueEditor: React.FC<{
/>
);
}
if (setting.field_type === "String") {
return (
<Input
aria-label={setting.field_name}
value={setting.field_value ?? ""}
onChange={(event) => onChange(setting.field_name, event.target.value === "" ? null : event.target.value)}
/>
);
}
if (setting.field_type === "Select") {
return (
<Select value={setting.field_value ?? null} onValueChange={(newValue) => onChange(setting.field_name, newValue)}>
@ -249,7 +258,7 @@ const GeneralSettings: React.FC<GeneralSettingsPageProps> = ({ accessToken, user
const fieldValue = setting?.field_value;
if (fieldValue == null) {
if (setting?.field_type === "Select" || setting?.field_type === "List") handleResetField(fieldName);
if (["Select", "List", "String"].includes(setting?.field_type ?? "")) handleResetField(fieldName);
return;
}
try {