Desktop UI for deleting custom providers (#7042)

This commit is contained in:
David Katz
2026-02-06 14:15:17 -05:00
committed by GitHub
parent 7ed5b4cf86
commit 0853e9fc11
2 changed files with 102 additions and 9 deletions
@@ -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>{' '}
@@ -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>
);
}