ui: setting configuration (#1597)

This commit is contained in:
Lily Delalande
2025-03-10 16:52:38 -07:00
committed by GitHub
parent c0e719eaba
commit 6afaa8242b
4 changed files with 127 additions and 37 deletions
+5 -5
View File
@@ -71,13 +71,13 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
setConfig(response.data.config || {});
};
const upsert = async (key: string, value: unknown, isSecret?: boolean) => {
const upsert = async (key: string, value: unknown, isSecret: boolean = false) => {
console.log('trying to upsert', key, value, isSecret);
const query: UpsertConfigQuery = {
key,
value,
is_secret: isSecret || null,
key: key,
value: value,
is_secret: isSecret,
};
await upsertConfig({
body: query,
});
@@ -6,51 +6,37 @@ import ProviderSetupActions from './subcomponents/ProviderSetupActions';
import ProviderLogo from './subcomponents/ProviderLogo';
import { useProviderModal } from './ProviderModalProvider';
import { SecureStorageNotice } from './subcomponents/SecureStorageNotice';
import DefaultSubmitHandler from './subcomponents/handlers/DefaultSubmitHandler';
import { DefaultSubmitHandler } from './subcomponents/handlers/DefaultSubmitHandler';
import OllamaSubmitHandler from './subcomponents/handlers/OllamaSubmitHandler';
import OllamaForm from './subcomponents/forms/OllamaForm';
import { useConfig } from '../../../ConfigContext';
const customSubmitHandler = {
const customSubmitHandlerMap = {
provider_name: OllamaSubmitHandler, // example
};
const customForms = {
const customFormsMap = {
provider_name: OllamaForm, // example
};
export default function ProviderConfigurationModal() {
const { upsert } = useConfig();
const { isOpen, currentProvider, modalProps, closeModal } = useProviderModal();
const [configValues, setConfigValues] = useState({});
useEffect(() => {
if (currentProvider) {
// Initialize form with default values
const initialValues = {};
// FIXME
// if (currentProvider.parameters) {
// currentProvider.parameters.forEach((param) => {
// initialValues[param.name] = param.default || '';
// });
// }
setConfigValues(initialValues);
} else {
setConfigValues({});
}
}, [currentProvider]);
if (!isOpen || !currentProvider) return null;
const headerText = `Configure ${currentProvider.metadata.display_name}`;
const descriptionText = `Add your API key(s) for this provider to integrate into Goose`;
const SubmitHandler = customSubmitHandler[currentProvider.name] || DefaultSubmitHandler;
const FormComponent = customForms[currentProvider.name] || DefaultProviderSetupForm;
const SubmitHandler = customSubmitHandlerMap[currentProvider.name] || DefaultSubmitHandler;
const FormComponent = customFormsMap[currentProvider.name] || DefaultProviderSetupForm;
const handleSubmitForm = (e) => {
e.preventDefault();
console.log('Form submitted for:', currentProvider.name);
SubmitHandler(configValues);
SubmitHandler(upsert, currentProvider, configValues);
// Close the modal unless the custom handler explicitly returns false
// This gives custom handlers the ability to keep the modal open if needed
@@ -62,10 +62,7 @@ export default function DefaultProviderSetupForm({
) : (
requiredParameters.map((parameter) => (
<div key={parameter.name}>
<label className="block text-sm font-medium text-gray-700 mb-1">
{parameter.name}
<span className="text-red-500 ml-1">*</span>
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">{parameter.name}</label>
<Input
type={parameter.secret ? 'password' : 'text'}
value={configValues[parameter.name] || ''}
@@ -1,7 +1,114 @@
export default function DefaultSubmitHandler(configValues) {
// Log each field value individually for clarity
console.log('Field values:');
Object.entries(configValues).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
import { useConfig } from '../../../../../ConfigContext';
import React from 'react';
/**
* Custom hook for provider configuration submission
* Returns a submit handler function and submission state
*/
export const useDefaultSubmit = () => {
const { upsert } = useConfig();
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [error, setError] = React.useState(null);
const [isSuccess, setIsSuccess] = React.useState(false);
/**
* Submit handler for provider configuration
* @param {Object} provider - The provider object with metadata
* @param {Object} configValues - The form values to be submitted
* @param {Function} onSuccess - Optional callback for successful submission
*/
const handleSubmit = async (provider, configValues, onSuccess) => {
setIsSubmitting(true);
setError(null);
setIsSuccess(false);
try {
const parameters = provider.metadata.config_keys || [];
// Create an array of promises for all the upsert operations
const upsertPromises = parameters.map((parameter) => {
// Skip parameters that don't have a value and aren't required
if (!configValues[parameter.name] && !parameter.required) {
return Promise.resolve();
}
// For required parameters with no value, use the default if available
const value =
configValues[parameter.name] !== undefined
? configValues[parameter.name]
: parameter.default;
// Skip if there's still no value
if (value === undefined || value === null) {
return Promise.resolve();
}
// Create the provider-specific config key
// Format: provider.{provider_name}.{parameter_name}
const configKey = `provider.${provider.name}.${parameter.name}`;
// Pass the is_secret flag from the parameter definition
return upsert(configKey, value, parameter.secret || false);
});
// Wait for all upsert operations to complete
await Promise.all(upsertPromises);
setIsSuccess(true);
// Call the success callback if provided
if (onSuccess) {
onSuccess();
}
} catch (err) {
console.error('Failed to save provider configuration:', err);
setError('Failed to save configuration. Please try again.');
} finally {
setIsSubmitting(false);
}
};
return {
handleSubmit,
isSubmitting,
error,
isSuccess,
};
};
/**
* Standalone function to submit provider configuration
* Useful for components that don't want to use the hook
*/
export const DefaultSubmitHandler = async (upsertFn, provider, configValues) => {
const parameters = provider.metadata.config_keys || [];
const upsertPromises = parameters.map((parameter) => {
// Skip parameters that don't have a value and aren't required
if (!configValues[parameter.name] && !parameter.required) {
return Promise.resolve();
}
// For required parameters with no value, use the default if available
const value =
configValues[parameter.name] !== undefined ? configValues[parameter.name] : parameter.default;
// Skip if there's still no value
if (value === undefined || value === null) {
return Promise.resolve();
}
// Create the provider-specific config key
const configKey = `${parameter.name}`;
// Explicitly define is_secret as a boolean (true/false) or null
// This is critical for Rust's Option<bool> type
const isSecret = parameter.secret === true;
// Pass the is_secret flag from the parameter definition
return upsertFn(configKey, value, isSecret);
});
}
// Wait for all upsert operations to complete
return Promise.all(upsertPromises);
};