mirror of
https://github.com/aaif-goose/goose.git
synced 2026-07-03 14:10:03 +02:00
Desktop UI for deleting custom providers (#7042)
This commit is contained in:
@@ -11,6 +11,7 @@ import { Plus } from 'lucide-react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog';
|
||||
import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm';
|
||||
import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal';
|
||||
import { useModelAndProvider } from '../../ModelAndProviderContext';
|
||||
import type { View } from '../../../utils/navigationUtils';
|
||||
|
||||
const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) {
|
||||
@@ -65,10 +66,13 @@ function ProviderCards({
|
||||
const [showCustomProviderModal, setShowCustomProviderModal] = useState(false);
|
||||
const [showSwitchModelModal, setShowSwitchModelModal] = useState(false);
|
||||
const [switchModelProvider, setSwitchModelProvider] = useState<string | null>(null);
|
||||
const [isActiveProvider, setIsActiveProvider] = useState(false);
|
||||
const { getCurrentModelAndProvider } = useModelAndProvider();
|
||||
const [editingProvider, setEditingProvider] = useState<{
|
||||
id: string;
|
||||
config: DeclarativeProviderConfig;
|
||||
isEditable: boolean;
|
||||
providerType: string;
|
||||
} | null>(null);
|
||||
|
||||
const handleProviderLaunchWithModelSelection = useCallback((provider: ProviderDetails) => {
|
||||
@@ -92,14 +96,24 @@ function ProviderCards({
|
||||
id: provider.name,
|
||||
config: result.data.config,
|
||||
isEditable: result.data.is_editable,
|
||||
providerType: provider.provider_type,
|
||||
});
|
||||
|
||||
// Check if this is the active provider
|
||||
try {
|
||||
const providerModel = await getCurrentModelAndProvider();
|
||||
setIsActiveProvider(provider.name === providerModel.provider);
|
||||
} catch {
|
||||
setIsActiveProvider(false);
|
||||
}
|
||||
|
||||
setShowCustomProviderModal(true);
|
||||
}
|
||||
} else {
|
||||
openModal(provider);
|
||||
}
|
||||
},
|
||||
[openModal]
|
||||
[openModal, getCurrentModelAndProvider]
|
||||
);
|
||||
|
||||
const handleUpdateCustomProvider = useCallback(
|
||||
@@ -124,9 +138,26 @@ function ProviderCards({
|
||||
[editingProvider, refreshProviders]
|
||||
);
|
||||
|
||||
const handleDeleteCustomProvider = useCallback(async () => {
|
||||
if (!editingProvider) return;
|
||||
|
||||
const { removeCustomProvider } = await import('../../../api');
|
||||
await removeCustomProvider({
|
||||
path: { id: editingProvider.id },
|
||||
throwOnError: true,
|
||||
});
|
||||
setShowCustomProviderModal(false);
|
||||
setEditingProvider(null);
|
||||
setIsActiveProvider(false);
|
||||
if (refreshProviders) {
|
||||
refreshProviders();
|
||||
}
|
||||
}, [editingProvider, refreshProviders]);
|
||||
|
||||
const handleCloseModal = useCallback(() => {
|
||||
setShowCustomProviderModal(false);
|
||||
setEditingProvider(null);
|
||||
setIsActiveProvider(false);
|
||||
}, []);
|
||||
|
||||
const onCloseProviderConfig = useCallback(() => {
|
||||
@@ -178,8 +209,10 @@ function ProviderCards({
|
||||
const providerCards = useMemo(() => {
|
||||
// providers needs to be an array
|
||||
const providersArray = Array.isArray(providers) ? providers : [];
|
||||
// Sort providers alphabetically by name
|
||||
const sortedProviders = [...providersArray].sort((a, b) => a.name.localeCompare(b.name));
|
||||
// Sort providers alphabetically by display name
|
||||
const sortedProviders = [...providersArray].sort((a, b) =>
|
||||
a.metadata.display_name.localeCompare(b.metadata.display_name)
|
||||
);
|
||||
const cards = sortedProviders.map((provider) => (
|
||||
<ProviderCard
|
||||
key={provider.name}
|
||||
@@ -222,6 +255,10 @@ function ProviderCards({
|
||||
isEditable={editable}
|
||||
onSubmit={editingProvider ? handleUpdateCustomProvider : handleCreateCustomProvider}
|
||||
onCancel={handleCloseModal}
|
||||
onDelete={
|
||||
editingProvider?.providerType === 'Custom' ? handleDeleteCustomProvider : undefined
|
||||
}
|
||||
isActiveProvider={isActiveProvider}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>{' '}
|
||||
|
||||
+62
-6
@@ -4,10 +4,13 @@ import { Select } from '../../../../../ui/Select';
|
||||
import { Button } from '../../../../../ui/button';
|
||||
import { SecureStorageNotice } from '../SecureStorageNotice';
|
||||
import { UpdateCustomProviderRequest } from '../../../../../../api';
|
||||
import { Trash2, AlertTriangle } from 'lucide-react';
|
||||
|
||||
interface CustomProviderFormProps {
|
||||
onSubmit: (data: UpdateCustomProviderRequest) => void;
|
||||
onCancel: () => void;
|
||||
onDelete?: () => Promise<void>;
|
||||
isActiveProvider?: boolean;
|
||||
initialData: UpdateCustomProviderRequest | null;
|
||||
isEditable?: boolean;
|
||||
}
|
||||
@@ -15,6 +18,8 @@ interface CustomProviderFormProps {
|
||||
export default function CustomProviderForm({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
onDelete,
|
||||
isActiveProvider = false,
|
||||
initialData,
|
||||
isEditable,
|
||||
}: CustomProviderFormProps) {
|
||||
@@ -26,6 +31,7 @@ export default function CustomProviderForm({
|
||||
const [requiresApiKey, setRequiresApiKey] = useState(false);
|
||||
const [supportsStreaming, setSupportsStreaming] = useState(true);
|
||||
const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});
|
||||
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
@@ -257,12 +263,62 @@ export default function CustomProviderForm({
|
||||
</>
|
||||
)}
|
||||
<SecureStorageNotice />
|
||||
<div className="flex justify-end space-x-2 pt-4">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">{initialData ? 'Update Provider' : 'Create Provider'}</Button>
|
||||
</div>
|
||||
|
||||
{showDeleteConfirmation ? (
|
||||
<div className="pt-4 space-y-3">
|
||||
{isActiveProvider ? (
|
||||
<div className="px-4 py-3 bg-yellow-600/20 border border-yellow-500/30 rounded">
|
||||
<p className="text-yellow-500 text-sm flex items-start">
|
||||
<AlertTriangle className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
||||
<span>
|
||||
You cannot delete this provider while it's currently in use. Please switch to a
|
||||
different model first.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 py-3 bg-red-900/20 border border-red-500/30 rounded">
|
||||
<p className="text-red-400 text-sm">
|
||||
Are you sure you want to delete this custom provider? This will permanently remove
|
||||
the provider and its stored API key. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-end space-x-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setShowDeleteConfirmation(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
{!isActiveProvider && (
|
||||
<Button type="button" variant="destructive" onClick={onDelete}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
Confirm Delete
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-end space-x-2 pt-4">
|
||||
{initialData && onDelete && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="text-red-500 hover:text-red-600 mr-auto"
|
||||
onClick={() => setShowDeleteConfirmation(true)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
Delete Provider
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">{initialData ? 'Update Provider' : 'Create Provider'}</Button>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user