diff --git a/ui/desktop/src/components/ConfigContext.tsx b/ui/desktop/src/components/ConfigContext.tsx index 0c5927c7dfc4..5ca7c47e68b1 100644 --- a/ui/desktop/src/components/ConfigContext.tsx +++ b/ui/desktop/src/components/ConfigContext.tsx @@ -71,13 +71,13 @@ export const ConfigProvider: React.FC = ({ 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, }); diff --git a/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx b/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx index 99d6ddb61b0c..d1cb2c897b00 100644 --- a/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx +++ b/ui/desktop/src/components/settings_v2/providers/modal/ProviderConfiguationModal.tsx @@ -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 diff --git a/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/forms/DefaultProviderSetupForm.tsx b/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/forms/DefaultProviderSetupForm.tsx index 171f893a7e11..3fc5a2a16031 100644 --- a/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/forms/DefaultProviderSetupForm.tsx +++ b/ui/desktop/src/components/settings_v2/providers/modal/subcomponents/forms/DefaultProviderSetupForm.tsx @@ -62,10 +62,7 @@ export default function DefaultProviderSetupForm({ ) : ( requiredParameters.map((parameter) => (
- + { - 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 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); +};