Spaces:
Running
Running
import React, { useState, useEffect } from "react"; | |
import { | |
Card, | |
Subtitle, | |
Table, | |
TableHead, | |
TableRow, | |
Badge, | |
TableHeaderCell, | |
TableCell, | |
TableBody, | |
Metric, | |
Text, | |
Grid, | |
Button, | |
TextInput, | |
Switch, | |
Col, | |
TabPanel, | |
TabPanels, | |
TabGroup, | |
TabList, | |
Tab, | |
Callout, | |
SelectItem, | |
Icon, | |
} from "@tremor/react"; | |
import { | |
PencilAltIcon | |
} from "@heroicons/react/outline"; | |
import { Modal, Typography, Form, Input, Select, Button as Button2, message } from "antd"; | |
const { Title, Paragraph } = Typography; | |
const isLocal = process.env.NODE_ENV === "development"; | |
const proxyBaseUrl = isLocal ? "http://localhost:4000" : null; | |
if (isLocal != true) { | |
console.log = function() {}; | |
} | |
import { | |
getCallbacksCall, | |
setCallbacksCall, | |
serviceHealthCheck, | |
} from "./networking"; | |
import AlertingSettings from "./alerting/alerting_settings"; | |
import FormItem from "antd/es/form/FormItem"; | |
interface SettingsPageProps { | |
accessToken: string | null; | |
userRole: string | null; | |
userID: string | null; | |
premiumUser: boolean; | |
} | |
interface genericCallbackParams { | |
litellm_callback_name: string // what to send in request | |
ui_callback_name: string // what to show on UI | |
litellm_callback_params: string[] | null // known required params for this callback | |
} | |
interface AlertingVariables { | |
SLACK_WEBHOOK_URL: string | null; | |
LANGFUSE_PUBLIC_KEY: string | null; | |
LANGFUSE_SECRET_KEY: string | null; | |
LANGFUSE_HOST: string | null; | |
OPENMETER_API_KEY: string | null; | |
} | |
interface AlertingObject { | |
name: string; | |
variables: AlertingVariables; | |
} | |
const defaultLoggingObject: AlertingObject[] = [ | |
{ | |
name: "slack", | |
variables: { | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: null, | |
LANGFUSE_SECRET_KEY: null, | |
OPENMETER_API_KEY: null, | |
SLACK_WEBHOOK_URL: null, | |
}, | |
}, | |
{ | |
name: "langfuse", | |
variables: { | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: null, | |
LANGFUSE_SECRET_KEY: null, | |
OPENMETER_API_KEY: null, | |
SLACK_WEBHOOK_URL: null, | |
}, | |
}, | |
{ | |
name: "openmeter", | |
variables: { | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: null, | |
LANGFUSE_SECRET_KEY: null, | |
OPENMETER_API_KEY: null, | |
SLACK_WEBHOOK_URL: null, | |
}, | |
}, | |
]; | |
const Settings: React.FC<SettingsPageProps> = ({ | |
accessToken, | |
userRole, | |
userID, | |
premiumUser, | |
}) => { | |
const [callbacks, setCallbacks] = | |
useState<AlertingObject[]>([]); | |
const [alerts, setAlerts] = useState<any[]>([]); | |
const [isModalVisible, setIsModalVisible] = useState(false); | |
const [form] = Form.useForm(); | |
const [selectedCallback, setSelectedCallback] = useState<string | null>(null); | |
const [selectedAlertValues, setSelectedAlertValues] = useState([]); | |
const [catchAllWebhookURL, setCatchAllWebhookURL] = useState<string>(""); | |
const [alertToWebhooks, setAlertToWebhooks] = useState< | |
Record<string, string> | |
>({}); | |
const [activeAlerts, setActiveAlerts] = useState<string[]>([]); | |
const [showAddCallbacksModal, setShowAddCallbacksModal] = useState(false); | |
const [allCallbacks, setAllCallbacks] = useState<genericCallbackParams[]>([]); | |
const [selectedCallbacktoAdd, setSelectedCallbacktoAdd] = useState<string | null>(null); | |
const [selectedCallbackParams, setSelectedCallbackParams] = useState<string[]>([]); | |
const [showEditCallback, setShowEditCallback] = useState(false); | |
const [selectedEditCallback, setSelectedEditCallback] = useState<any | null>(null); | |
const handleSwitchChange = (alertName: string) => { | |
if (activeAlerts.includes(alertName)) { | |
setActiveAlerts(activeAlerts.filter((alert) => alert !== alertName)); | |
} else { | |
setActiveAlerts([...activeAlerts, alertName]); | |
} | |
}; | |
const alerts_to_UI_NAME: Record<string, string> = { | |
llm_exceptions: "LLM Exceptions", | |
llm_too_slow: "LLM Responses Too Slow", | |
llm_requests_hanging: "LLM Requests Hanging", | |
budget_alerts: "Budget Alerts (API Keys, Users)", | |
db_exceptions: "Database Exceptions (Read/Write)", | |
daily_reports: "Weekly/Monthly Spend Reports", | |
outage_alerts: "Outage Alerts", | |
region_outage_alerts: "Region Outage Alerts", | |
}; | |
useEffect(() => { | |
if (!accessToken || !userRole || !userID) { | |
return; | |
} | |
getCallbacksCall(accessToken, userID, userRole).then((data) => { | |
console.log("callbacks", data); | |
setCallbacks(data.callbacks); | |
setAllCallbacks(data.available_callbacks); | |
// setCallbacks(callbacks_data); | |
let alerts_data = data.alerts; | |
console.log("alerts_data", alerts_data); | |
if (alerts_data) { | |
if (alerts_data.length > 0) { | |
let _alert_info = alerts_data[0]; | |
console.log("_alert_info", _alert_info); | |
let catch_all_webhook = _alert_info.variables.SLACK_WEBHOOK_URL; | |
console.log("catch_all_webhook", catch_all_webhook); | |
let active_alerts = _alert_info.active_alerts; | |
setActiveAlerts(active_alerts); | |
setCatchAllWebhookURL(catch_all_webhook); | |
setAlertToWebhooks(_alert_info.alerts_to_webhook); | |
} | |
} | |
setAlerts(alerts_data); | |
}); | |
}, [accessToken, userRole, userID]); | |
const isAlertOn = (alertName: string) => { | |
return activeAlerts && activeAlerts.includes(alertName); | |
}; | |
const handleAddCallback = () => { | |
console.log("Add callback clicked"); | |
setIsModalVisible(true); | |
}; | |
const handleCancel = () => { | |
setIsModalVisible(false); | |
form.resetFields(); | |
setSelectedCallback(null); | |
}; | |
const handleChange = (values: any) => { | |
setSelectedAlertValues(values); | |
// Here, you can perform any additional logic with the selected values | |
console.log("Selected values:", values); | |
}; | |
const handleSaveEmailSettings = () => { | |
if (!accessToken) { | |
return; | |
} | |
let updatedVariables: Record<string, string> = {}; | |
alerts | |
.filter((alert) => alert.name === "email") | |
.forEach((alert) => { | |
Object.entries(alert.variables ?? {}).forEach(([key, value]) => { | |
const inputElement = document.querySelector(`input[name="${key}"]`) as HTMLInputElement; | |
if (inputElement && inputElement.value) { | |
updatedVariables[key] = inputElement?.value; | |
} | |
}); | |
}); | |
console.log("updatedVariables", updatedVariables); | |
//filter out null / undefined values for updatedVariables | |
const payload = { | |
general_settings: { | |
alerting: ["email"], | |
}, | |
environment_variables: updatedVariables, | |
}; | |
try { | |
setCallbacksCall(accessToken, payload); | |
} catch (error) { | |
message.error("Failed to update alerts: " + error, 20); | |
} | |
message.success("Email settings updated successfully"); | |
} | |
const updateCallbackCall = async (formValues: Record<string, any>) => { | |
if (!accessToken) { | |
return; | |
} | |
let env_vars: Record<string, string> = {}; | |
// add all other variables | |
Object.entries(formValues).forEach(([key, value]) => { | |
if (key !== "callback") { | |
env_vars[key] = value | |
} | |
}); | |
let payload = { | |
environment_variables: env_vars, | |
} | |
try { | |
let newCallback = await setCallbacksCall(accessToken, payload); | |
message.success(`Callback added successfully`); | |
setIsModalVisible(false); | |
form.resetFields(); | |
setSelectedCallback(null); | |
} catch (error) { | |
message.error("Failed to add callback: " + error, 20); | |
} | |
} | |
const addNewCallbackCall = async (formValues: Record<string, any>) => { | |
if (!accessToken) { | |
return; | |
} | |
let new_callback = formValues?.callback | |
let env_vars: Record<string, string> = {}; | |
// add all other variables | |
Object.entries(formValues).forEach(([key, value]) => { | |
if (key !== "callback") { | |
env_vars[key] = value | |
} | |
}); | |
let payload = { | |
environment_variables: env_vars, | |
litellm_settings: { | |
success_callback: [new_callback], | |
}, | |
} | |
try { | |
let newCallback = await setCallbacksCall(accessToken, payload); | |
message.success(`Callback ${new_callback} added successfully`); | |
setIsModalVisible(false); | |
form.resetFields(); | |
setSelectedCallback(null); | |
} catch (error) { | |
message.error("Failed to add callback: " + error, 20); | |
} | |
} | |
const handleSelectedCallbackChange = (callbackObject: genericCallbackParams) => { | |
console.log("inside handleSelectedCallbackChange", callbackObject); | |
setSelectedCallback(callbackObject.litellm_callback_name); | |
console.log("all callbacks", allCallbacks); | |
if (callbackObject && callbackObject.litellm_callback_params) { | |
setSelectedCallbackParams(callbackObject.litellm_callback_params); | |
console.log("selectedCallbackParams", selectedCallbackParams); | |
} else { | |
setSelectedCallbackParams([]); | |
} | |
}; | |
const handleSaveAlerts = () => { | |
if (!accessToken) { | |
return; | |
} | |
const updatedAlertToWebhooks: Record<string, string> = {}; | |
Object.entries(alerts_to_UI_NAME).forEach(([key, value]) => { | |
const webhookInput = document.querySelector( | |
`input[name="${key}"]` | |
) as HTMLInputElement; | |
console.log("key", key); | |
console.log("webhookInput", webhookInput); | |
const newWebhookValue = webhookInput?.value || ""; | |
console.log("newWebhookValue", newWebhookValue); | |
updatedAlertToWebhooks[key] = newWebhookValue; | |
}); | |
console.log("updatedAlertToWebhooks", updatedAlertToWebhooks); | |
const payload = { | |
general_settings: { | |
alert_to_webhook_url: updatedAlertToWebhooks, | |
alert_types: activeAlerts, | |
}, | |
}; | |
console.log("payload", payload); | |
try { | |
setCallbacksCall(accessToken, payload); | |
} catch (error) { | |
message.error("Failed to update alerts: " + error, 20); | |
} | |
message.success("Alerts updated successfully"); | |
}; | |
const handleSaveChanges = (callback: any) => { | |
if (!accessToken) { | |
return; | |
} | |
const updatedVariables = Object.fromEntries( | |
Object.entries(callback.variables).map(([key, value]) => [ | |
key, | |
(document.querySelector(`input[name="${key}"]`) as HTMLInputElement) | |
?.value || value, | |
]) | |
); | |
console.log("updatedVariables", updatedVariables); | |
console.log("updateAlertTypes", selectedAlertValues); | |
const payload = { | |
environment_variables: updatedVariables, | |
litellm_settings: { | |
success_callback: [callback.name], | |
}, | |
}; | |
try { | |
setCallbacksCall(accessToken, payload); | |
} catch (error) { | |
message.error("Failed to update callback: " + error, 20); | |
} | |
message.success("Callback updated successfully"); | |
}; | |
const handleOk = () => { | |
if (!accessToken) { | |
return; | |
} | |
// Handle form submission | |
form.validateFields().then((values) => { | |
// Call API to add the callback | |
console.log("Form values:", values); | |
let payload; | |
if (values.callback === "langfuse") { | |
payload = { | |
environment_variables: { | |
LANGFUSE_PUBLIC_KEY: values.langfusePublicKey, | |
LANGFUSE_SECRET_KEY: values.langfusePrivateKey, | |
}, | |
litellm_settings: { | |
success_callback: [values.callback], | |
}, | |
}; | |
setCallbacksCall(accessToken, payload); | |
let newCallback: AlertingObject = { | |
name: values.callback, | |
variables: { | |
SLACK_WEBHOOK_URL: null, | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: values.langfusePublicKey, | |
LANGFUSE_SECRET_KEY: values.langfusePrivateKey, | |
OPENMETER_API_KEY: null, | |
}, | |
}; | |
// add langfuse to callbacks | |
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]); | |
} else if (values.callback === "slack") { | |
console.log(`values.slackWebhookUrl: ${values.slackWebhookUrl}`); | |
payload = { | |
general_settings: { | |
alerting: ["slack"], | |
alerting_threshold: 300, | |
}, | |
environment_variables: { | |
SLACK_WEBHOOK_URL: values.slackWebhookUrl, | |
}, | |
}; | |
setCallbacksCall(accessToken, payload); | |
// add slack to callbacks | |
console.log(`values.callback: ${values.callback}`); | |
let newCallback: AlertingObject = { | |
name: values.callback, | |
variables: { | |
SLACK_WEBHOOK_URL: values.slackWebhookUrl, | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: null, | |
LANGFUSE_SECRET_KEY: null, | |
OPENMETER_API_KEY: null, | |
}, | |
}; | |
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]); | |
} else if (values.callback == "openmeter") { | |
console.log(`values.openMeterApiKey: ${values.openMeterApiKey}`); | |
payload = { | |
environment_variables: { | |
OPENMETER_API_KEY: values.openMeterApiKey, | |
}, | |
litellm_settings: { | |
success_callback: [values.callback], | |
}, | |
}; | |
setCallbacksCall(accessToken, payload); | |
let newCallback: AlertingObject = { | |
name: values.callback, | |
variables: { | |
SLACK_WEBHOOK_URL: null, | |
LANGFUSE_HOST: null, | |
LANGFUSE_PUBLIC_KEY: null, | |
LANGFUSE_SECRET_KEY: null, | |
OPENMETER_API_KEY: values.openMeterAPIKey, | |
}, | |
}; | |
// add langfuse to callbacks | |
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]); | |
} else { | |
payload = { | |
error: "Invalid callback value", | |
}; | |
} | |
setIsModalVisible(false); | |
form.resetFields(); | |
setSelectedCallback(null); | |
}); | |
}; | |
const handleCallbackChange = (value: string) => { | |
setSelectedCallback(value); | |
}; | |
if (!accessToken) { | |
return null; | |
} | |
console.log(`callbacks: ${callbacks}`); | |
return ( | |
<div className="w-full mx-4"> | |
<Grid numItems={1} className="gap-2 p-8 w-full mt-2"> | |
<TabGroup> | |
<TabList variant="line" defaultValue="1"> | |
<Tab value="1">Logging Callbacks</Tab> | |
<Tab value="2">Alerting Types</Tab> | |
<Tab value="3">Alerting Settings</Tab> | |
<Tab value="4">Email Alerts</Tab> | |
</TabList> | |
<TabPanels> | |
<TabPanel> | |
<Title level={4}>Active Logging Callbacks</Title> | |
<Grid numItems={2}> | |
<Card className="max-h-[50vh]"> | |
<Table> | |
<TableHead> | |
<TableRow> | |
<TableHeaderCell>Callback Name</TableHeaderCell> | |
{/* <TableHeaderCell>Callback Env Vars</TableHeaderCell> */} | |
</TableRow> | |
</TableHead> | |
<TableBody> | |
{callbacks | |
.map((callback, index) => ( | |
<TableRow key={index} className="flex justify-between"> | |
<TableCell> | |
<Text>{callback.name}</Text> | |
</TableCell> | |
<TableCell> | |
<Grid numItems={2} className="flex justify-between"> | |
<Icon | |
icon={PencilAltIcon} | |
size="sm" | |
onClick={() => { | |
setSelectedEditCallback(callback); | |
setShowEditCallback(true); | |
}} | |
/> | |
<Button | |
onClick={() => | |
serviceHealthCheck(accessToken, callback.name) | |
} | |
className="ml-2" | |
variant="secondary" | |
> | |
Test Callback | |
</Button> | |
</Grid> | |
</TableCell> | |
</TableRow> | |
))} | |
</TableBody> | |
</Table> | |
</Card> | |
</Grid> | |
<Button | |
className="mt-2" | |
onClick={() => setShowAddCallbacksModal(true)}> | |
Add Callback | |
</Button> | |
</TabPanel> | |
<TabPanel> | |
<Card> | |
<Text className="my-2"> | |
Alerts are only supported for Slack Webhook URLs. Get your | |
webhook urls from{" "} | |
<a | |
href="https://api.slack.com/messaging/webhooks" | |
target="_blank" | |
style={{ color: "blue" }} | |
> | |
here | |
</a> | |
</Text> | |
<Table> | |
<TableHead> | |
<TableRow> | |
<TableHeaderCell></TableHeaderCell> | |
<TableHeaderCell></TableHeaderCell> | |
<TableHeaderCell>Slack Webhook URL</TableHeaderCell> | |
</TableRow> | |
</TableHead> | |
<TableBody> | |
{Object.entries(alerts_to_UI_NAME).map( | |
([key, value], index) => ( | |
<TableRow key={index}> | |
<TableCell> | |
{key == "region_outage_alerts" ? ( | |
premiumUser ? ( | |
<Switch | |
id="switch" | |
name="switch" | |
checked={isAlertOn(key)} | |
onChange={() => handleSwitchChange(key)} | |
/> | |
) : ( | |
<Button className="flex items-center justify-center"> | |
<a | |
href="https://forms.gle/W3U4PZpJGFHWtHyA9" | |
target="_blank" | |
> | |
✨ Enterprise Feature | |
</a> | |
</Button> | |
) | |
) : ( | |
<Switch | |
id="switch" | |
name="switch" | |
checked={isAlertOn(key)} | |
onChange={() => handleSwitchChange(key)} | |
/> | |
)} | |
</TableCell> | |
<TableCell> | |
<Text>{value}</Text> | |
</TableCell> | |
<TableCell> | |
<TextInput | |
name={key} | |
type="password" | |
defaultValue={ | |
alertToWebhooks && alertToWebhooks[key] | |
? alertToWebhooks[key] | |
: (catchAllWebhookURL as string) | |
} | |
></TextInput> | |
</TableCell> | |
</TableRow> | |
) | |
)} | |
</TableBody> | |
</Table> | |
<Button size="xs" className="mt-2" onClick={handleSaveAlerts}> | |
Save Changes | |
</Button> | |
<Button | |
onClick={() => serviceHealthCheck(accessToken, "slack")} | |
className="mx-2" | |
> | |
Test Alerts | |
</Button> | |
</Card> | |
</TabPanel> | |
<TabPanel> | |
<AlertingSettings | |
accessToken={accessToken} | |
premiumUser={premiumUser} | |
/> | |
</TabPanel> | |
<TabPanel> | |
<Card> | |
<Title level={4}>Email Settings</Title> | |
<Text> | |
<a href="https://docs.litellm.ai/docs/proxy/email" target="_blank" style={{ color: "blue" }}> LiteLLM Docs: email alerts</a> <br/> | |
</Text> | |
<div className="flex w-full"> | |
{alerts | |
.filter((alert) => alert.name === "email") | |
.map((alert, index) => ( | |
<TableCell key={index}> | |
<ul> | |
<Grid numItems={2}> | |
{Object.entries(alert.variables ?? {}).map(([key, value]) => ( | |
<li key={key} className="mx-2 my-2"> | |
{ premiumUser!= true && (key === "EMAIL_LOGO_URL" || key === "EMAIL_SUPPORT_CONTACT") ? ( | |
<div> | |
<a | |
href="https://forms.gle/W3U4PZpJGFHWtHyA9" | |
target="_blank" | |
> | |
<Text className="mt-2"> | |
{" "} | |
✨ {key} | |
</Text> | |
</a> | |
<TextInput | |
name={key} | |
defaultValue={value as string} | |
type="password" | |
disabled={true} | |
style={{ width: "400px" }} | |
/> | |
</div> | |
) : ( | |
<div> | |
<Text className="mt-2">{key}</Text> | |
<TextInput | |
name={key} | |
defaultValue={value as string} | |
type="password" | |
style={{ width: "400px" }} | |
/> | |
</div> | |
)} | |
{/* Added descriptions for input fields */} | |
<p style={{ fontSize: "small", fontStyle: "italic" }}> | |
{key === "SMTP_HOST" && ( | |
<div style={{ color: "gray" }}> | |
Enter the SMTP host address, e.g. `smtp.resend.com` | |
<span style={{ color: "red" }}> Required * </span> | |
</div> | |
)} | |
{key === "SMTP_PORT" && ( | |
<div style={{ color: "gray" }}> | |
Enter the SMTP port number, e.g. `587` | |
<span style={{ color: "red" }}> Required * </span> | |
</div> | |
)} | |
{key === "SMTP_USERNAME" && ( | |
<div style={{ color: "gray" }}> | |
Enter the SMTP username, e.g. `username` | |
<span style={{ color: "red" }}> Required * </span> | |
</div> | |
)} | |
{key === "SMTP_PASSWORD" && ( | |
<span style={{ color: "red" }}> Required * </span> | |
)} | |
{key === "SMTP_SENDER_EMAIL" && ( | |
<div style={{ color: "gray" }}> | |
Enter the sender email address, e.g. `[email protected]` | |
<span style={{ color: "red" }}> Required * </span> | |
</div> | |
)} | |
{key === "TEST_EMAIL_ADDRESS" && ( | |
<div style={{ color: "gray" }}> | |
Email Address to send `Test Email Alert` to. example: `[email protected]` | |
<span style={{ color: "red" }}> Required * </span> | |
</div> | |
) | |
} | |
{key === "EMAIL_LOGO_URL" && ( | |
<div style={{ color: "gray" }}> | |
(Optional) Customize the Logo that appears in the email, pass a url to your logo | |
</div> | |
) | |
} | |
{key === "EMAIL_SUPPORT_CONTACT" && ( | |
<div style={{ color: "gray" }}> | |
(Optional) Customize the support email address that appears in the email. Default is [email protected] | |
</div> | |
) | |
} | |
</p> | |
</li> | |
))} | |
</Grid> | |
</ul> | |
</TableCell> | |
))} | |
</div> | |
<Button | |
className="mt-2" | |
onClick={() => handleSaveEmailSettings()} | |
> | |
Save Changes | |
</Button> | |
<Button | |
onClick={() => | |
serviceHealthCheck(accessToken, "email") | |
} | |
className="mx-2" | |
> | |
Test Email Alerts | |
</Button> | |
</Card> | |
</TabPanel> | |
</TabPanels> | |
</TabGroup> | |
</Grid> | |
<Modal | |
title="Add Logging Callback" | |
visible={showAddCallbacksModal} | |
width={800} | |
onCancel= {() => setShowAddCallbacksModal(false)} | |
footer={null} | |
> | |
<a href="https://docs.litellm.ai/docs/proxy/logging" className="mb-8 mt-4" target="_blank" style={{ color: "blue" }}> LiteLLM Docs: Logging</a> | |
<Form | |
form={form} | |
onFinish={addNewCallbackCall} | |
labelCol={{ span: 8 }} | |
wrapperCol={{ span: 16 }} | |
labelAlign="left" | |
> | |
<> | |
<FormItem | |
label="Callback" | |
name="callback" | |
rules={[{ required: true, message: "Please select a callback" }]} | |
> | |
<Select | |
onChange={(value) => { | |
const selectedCallback = allCallbacks[value]; | |
if (selectedCallback) { | |
console.log(selectedCallback.ui_callback_name); | |
handleSelectedCallbackChange(selectedCallback); | |
} | |
}} | |
> | |
{allCallbacks && | |
Object.values(allCallbacks).map((callback) => ( | |
<SelectItem | |
key={callback.litellm_callback_name} | |
value={callback.litellm_callback_name} | |
> | |
{callback.ui_callback_name} | |
</SelectItem> | |
))} | |
</Select> | |
</FormItem> | |
{ | |
selectedCallbackParams && selectedCallbackParams.map((param) => ( | |
<FormItem | |
label={param} | |
name={param} | |
key={param} | |
rules={[{ required: true, message: "Please enter the value for " + param}]} | |
> | |
<TextInput type="password" /> | |
</FormItem> | |
)) | |
} | |
<div style={{ textAlign: "right", marginTop: "10px" }}> | |
<Button2 htmlType="submit">Save</Button2> | |
</div> | |
</> | |
</Form> | |
</Modal> | |
<Modal | |
visible={showEditCallback} | |
width={800} | |
title={`Edit ${selectedEditCallback?.name } Settings`} | |
onCancel= {() => setShowEditCallback(false)} | |
footer={null} | |
> | |
<Form | |
form={form} | |
onFinish={updateCallbackCall} | |
labelCol={{ span: 8 }} | |
wrapperCol={{ span: 16 }} | |
labelAlign="left" | |
> | |
<> | |
{ | |
selectedEditCallback && selectedEditCallback.variables && Object.entries(selectedEditCallback.variables).map(([param, value]) => ( | |
<FormItem | |
label={param} | |
name={param} | |
key={param} | |
> | |
<TextInput type="password" defaultValue={value as string}/> | |
</FormItem> | |
)) | |
} | |
</> | |
<div style={{ textAlign: "right", marginTop: "10px" }}> | |
<Button2 htmlType="submit">Save</Button2> | |
</div> | |
</Form> | |
</Modal> | |
</div> | |
); | |
}; | |
export default Settings; | |