ui: change add model modal to 'switch model' and add form validation (#1878)
This commit is contained in:
@@ -10,7 +10,7 @@ import { useConfig } from '../../../ConfigContext';
|
|||||||
import { changeModel as switchModel } from '../index';
|
import { changeModel as switchModel } from '../index';
|
||||||
import type { View } from '../../../../App';
|
import type { View } from '../../../../App';
|
||||||
|
|
||||||
const ModalButtons = ({ onSubmit, onCancel }) => (
|
const ModalButtons = ({ onSubmit, onCancel, isValid, validationErrors }) => (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -18,7 +18,7 @@ const ModalButtons = ({ onSubmit, onCancel }) => (
|
|||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
className="w-full h-[60px] rounded-none border-borderSubtle text-base hover:bg-bgSubtle text-textProminent font-regular"
|
className="w-full h-[60px] rounded-none border-borderSubtle text-base hover:bg-bgSubtle text-textProminent font-regular"
|
||||||
>
|
>
|
||||||
Add model
|
Select model
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -42,12 +42,53 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
const [provider, setProvider] = useState<string | null>(null);
|
const [provider, setProvider] = useState<string | null>(null);
|
||||||
const [model, setModel] = useState<string>('');
|
const [model, setModel] = useState<string>('');
|
||||||
const [isCustomModel, setIsCustomModel] = useState(false);
|
const [isCustomModel, setIsCustomModel] = useState(false);
|
||||||
|
const [validationErrors, setValidationErrors] = useState({
|
||||||
|
provider: '',
|
||||||
|
model: '',
|
||||||
|
});
|
||||||
|
const [isValid, setIsValid] = useState(true);
|
||||||
|
const [attemptedSubmit, setAttemptedSubmit] = useState(false);
|
||||||
|
|
||||||
|
// Validate form data
|
||||||
|
const validateForm = () => {
|
||||||
|
const errors = {
|
||||||
|
provider: '',
|
||||||
|
model: '',
|
||||||
|
};
|
||||||
|
let formIsValid = true;
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
errors.provider = 'Please select a provider';
|
||||||
|
formIsValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
errors.model = 'Please select or enter a model';
|
||||||
|
formIsValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValidationErrors(errors);
|
||||||
|
setIsValid(formIsValid);
|
||||||
|
return formIsValid;
|
||||||
|
};
|
||||||
|
|
||||||
const changeModel = async () => {
|
const changeModel = async () => {
|
||||||
await switchModel({ model: model, provider: provider, writeToConfig: upsert });
|
setAttemptedSubmit(true);
|
||||||
onClose(); // Add this line to close the modal after changing the model
|
const isFormValid = validateForm();
|
||||||
|
|
||||||
|
if (isFormValid) {
|
||||||
|
await switchModel({ model: model, provider: provider, writeToConfig: upsert });
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Re-validate when inputs change and after attempted submission
|
||||||
|
useEffect(() => {
|
||||||
|
if (attemptedSubmit) {
|
||||||
|
validateForm();
|
||||||
|
}
|
||||||
|
}, [provider, model, attemptedSubmit]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -114,11 +155,21 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="z-10">
|
<div className="z-10">
|
||||||
<Modal onClose={onClose} footer={<ModalButtons onSubmit={changeModel} onCancel={onClose} />}>
|
<Modal
|
||||||
|
onClose={onClose}
|
||||||
|
footer={
|
||||||
|
<ModalButtons
|
||||||
|
onSubmit={changeModel}
|
||||||
|
onCancel={onClose}
|
||||||
|
isValid={isValid}
|
||||||
|
validationErrors={validationErrors}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<div className="flex flex-col items-center gap-8">
|
<div className="flex flex-col items-center gap-8">
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3">
|
||||||
<Plus size={24} className="text-textStandard" />
|
<Plus size={24} className="text-textStandard" />
|
||||||
<div className="text-textStandard font-medium">Add model</div>
|
<div className="text-textStandard font-medium">Switch models</div>
|
||||||
<div className="text-textSubtle text-center">
|
<div className="text-textSubtle text-center">
|
||||||
Configure your AI model providers by adding their API keys. Your keys are stored
|
Configure your AI model providers by adding their API keys. Your keys are stored
|
||||||
securely and encrypted locally.
|
securely and encrypted locally.
|
||||||
@@ -137,40 +188,50 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex flex-col gap-4">
|
<div className="w-full flex flex-col gap-4">
|
||||||
<Select
|
<div>
|
||||||
options={providerOptions}
|
<Select
|
||||||
value={providerOptions.find((option) => option.value === provider) || null}
|
options={providerOptions}
|
||||||
onChange={(option) => {
|
value={providerOptions.find((option) => option.value === provider) || null}
|
||||||
if (option?.value === 'configure_providers') {
|
onChange={(option) => {
|
||||||
// Navigate to ConfigureProviders view
|
if (option?.value === 'configure_providers') {
|
||||||
setView('ConfigureProviders');
|
// Navigate to ConfigureProviders view
|
||||||
onClose(); // Close the current modal
|
setView('ConfigureProviders');
|
||||||
} else {
|
onClose(); // Close the current modal
|
||||||
setProvider(option?.value || null);
|
} else {
|
||||||
setModel('');
|
setProvider(option?.value || null);
|
||||||
setIsCustomModel(false);
|
setModel('');
|
||||||
}
|
setIsCustomModel(false);
|
||||||
}}
|
}
|
||||||
placeholder="Provider"
|
}}
|
||||||
isClearable
|
placeholder="Provider"
|
||||||
/>
|
isClearable
|
||||||
|
/>
|
||||||
|
{attemptedSubmit && validationErrors.provider && (
|
||||||
|
<div className="text-red-500 text-sm mt-1">{validationErrors.provider}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{provider && (
|
{provider && (
|
||||||
<>
|
<>
|
||||||
{!isCustomModel ? (
|
{!isCustomModel ? (
|
||||||
<Select
|
<div>
|
||||||
options={filteredModelOptions}
|
<Select
|
||||||
onChange={handleModelChange}
|
options={filteredModelOptions}
|
||||||
value={model ? { value: model, label: model } : null}
|
onChange={handleModelChange}
|
||||||
placeholder="Select a model"
|
value={model ? { value: model, label: model } : null}
|
||||||
/>
|
placeholder="Select a model"
|
||||||
|
/>
|
||||||
|
{attemptedSubmit && validationErrors.model && (
|
||||||
|
<div className="text-red-500 text-sm mt-1">{validationErrors.model}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<label className="text-sm text-textSubtle">Custom model name</label>
|
<label className="text-sm text-textSubtle">Custom model name</label>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsCustomModel(false)}
|
onClick={() => setIsCustomModel(false)}
|
||||||
className="text-sm text-blue-500 hover:text-blue-700"
|
className="text-sm text-textSubtle"
|
||||||
>
|
>
|
||||||
Back to model list
|
Back to model list
|
||||||
</button>
|
</button>
|
||||||
@@ -181,6 +242,9 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
onChange={(event) => setModel(event.target.value)}
|
onChange={(event) => setModel(event.target.value)}
|
||||||
value={model}
|
value={model}
|
||||||
/>
|
/>
|
||||||
|
{attemptedSubmit && validationErrors.model && (
|
||||||
|
<div className="text-red-500 text-sm mt-1">{validationErrors.model}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user