kvm/ui/src/components/JigglerSetting.tsx

222 lines
5.9 KiB
TypeScript

import { useCallback, useEffect, useState } from "react";
import { Button } from "@components/Button";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { useJsonRpc } from "../hooks/useJsonRpc";
import notifications from "../notifications";
import { InputFieldWithLabel } from "./InputField";
export interface JigglerConfig {
inactivity_limit_seconds: number;
jitter_percentage: number;
schedule_cron_tab: string;
}
const jigglerCrontabConfigs = [
{
label: "Every 20 seconds",
value: "*/20 * * * * *",
},
{
label: "Every 40 seconds",
value: "*/40 * * * * *",
},
{
label: "Every 1 minute",
value: "0 * * * * *",
},
{
label: "Every 3 minutes",
value: "0 */3 * * * *",
},
];
const jigglerJitterConfigs = [
{
label: "No Jitter",
value: "0",
},
{
label: "10%",
value: "20",
},
{
label: "25%",
value: "25",
},
{
label: "50%",
value: "50",
},
];
const jigglerInactivityConfigs = [
{
label: "20 Seconds",
value: "20",
},
{
label: "40 Seconds",
value: "40",
},
{
label: "1 Minute",
value: "60",
},
{
label: "3 Minutes",
value: "180",
},
];
export function JigglerSetting() {
const [send] = useJsonRpc();
const [loading, setLoading] = useState(false);
const [jitterPercentage, setJitterPercentage] = useState("");
const [scheduleCronTab, setScheduleCronTab] = useState("");
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
inactivity_limit_seconds: 20,
jitter_percentage: 0,
schedule_cron_tab: "*/20 * * * * *"
});
const syncJigglerConfig = useCallback(() => {
send("getJigglerConfig", {}, resp => {
if ("error" in resp) return;
const result = resp.result as JigglerConfig;
setJigglerConfigState(result);
const jitterPercentage = jigglerJitterConfigs.map(u => u.value).includes(result.jitter_percentage.toString())
? result.jitter_percentage.toString()
: "custom";
setJitterPercentage(jitterPercentage)
const scheduleCronTab = jigglerCrontabConfigs.map(u => u.value).includes(result.schedule_cron_tab)
? result.schedule_cron_tab
: "custom";
setScheduleCronTab(scheduleCronTab)
});
}, [send]);
useEffect(() => {
syncJigglerConfig()
}, [send, syncJigglerConfig]);
const handleJigglerInactivityLimitSecondsChange = (value: string) => {
setJigglerConfigState({ ...jigglerConfigState, inactivity_limit_seconds: Number(value) });
};
const handleJigglerJitterPercentageChange = (value: string) => {
setJigglerConfigState({ ...jigglerConfigState, jitter_percentage: Number(value) });
};
const handleJigglerScheduleCronTabChange = (value: string) => {
setJigglerConfigState({ ...jigglerConfigState, schedule_cron_tab: value });
};
const handleJigglerConfigSave = useCallback(
(jigglerConfig: JigglerConfig) => {
setLoading(true);
send("setJigglerConfig", { jigglerConfig }, async resp => {
if ("error" in resp) {
notifications.error(
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
);
setLoading(false);
return;
}
setLoading(false);
notifications.success(
`Jiggler Config successfully updated`,
);
syncJigglerConfig();
});
},
[send, syncJigglerConfig],
);
return (
<div className="">
<div className="grid grid-cols-2 gap-4">
<SelectMenuBasic
size="SM"
label="Schedule"
className="max-w-[192px]"
value={scheduleCronTab}
fullWidth
onChange={e => {
setScheduleCronTab(e.target.value);
if (e.target.value != "custom") {
handleJigglerScheduleCronTabChange(e.target.value);
}
}}
options={[...jigglerCrontabConfigs, {value: "custom", label: "Custom"}]}
/>
{scheduleCronTab === "custom" && (
<InputFieldWithLabel
required
label="Jiggler Crontab"
placeholder="*/20 * * * * *"
defaultValue={jigglerConfigState.schedule_cron_tab}
onChange={e => handleJigglerScheduleCronTabChange(e.target.value)}
/>
)}
</div>
<div className="grid grid-cols-2 gap-4">
<SelectMenuBasic
size="SM"
label="Jitter Percentage"
className="max-w-[192px]"
value={jitterPercentage}
fullWidth
onChange={e => {
setJitterPercentage(e.target.value);
if (e.target.value != "custom") {
handleJigglerJitterPercentageChange(e.target.value)
}
}}
options={[...jigglerJitterConfigs, {value: "custom", label: "Custom"}]}
/>
{jitterPercentage === "custom" && (
<InputFieldWithLabel
required
label="Jitter Percentage"
placeholder="25"
defaultValue={jigglerConfigState.jitter_percentage}
type="number"
min="1"
max="100"
onChange={e => handleJigglerJitterPercentageChange(e.target.value)}
/>
)}
</div>
<div className="grid grid-cols-2 gap-4">
<SelectMenuBasic
size="SM"
label="Inactivity Limit Seconds"
className="max-w-[192px]"
value={jigglerConfigState.inactivity_limit_seconds}
fullWidth
onChange={e => {
handleJigglerInactivityLimitSecondsChange(e.target.value);
}}
options={[...jigglerInactivityConfigs]}
/>
</div>
<div className="mt-6 flex gap-x-2">
<Button
loading={loading}
size="SM"
theme="primary"
text="Update Jiggler Config"
onClick={() => handleJigglerConfigSave(jigglerConfigState)}
/>
</div>
</div>
);
}