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 { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog';
|
||||||
import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm';
|
import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm';
|
||||||
import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal';
|
import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal';
|
||||||
|
import { useModelAndProvider } from '../../ModelAndProviderContext';
|
||||||
import type { View } from '../../../utils/navigationUtils';
|
import type { View } from '../../../utils/navigationUtils';
|
||||||
|
|
||||||
const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) {
|
const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) {
|
||||||
@@ -65,10 +66,13 @@ function ProviderCards({
|
|||||||
const [showCustomProviderModal, setShowCustomProviderModal] = useState(false);
|
const [showCustomProviderModal, setShowCustomProviderModal] = useState(false);
|
||||||
const [showSwitchModelModal, setShowSwitchModelModal] = useState(false);
|
const [showSwitchModelModal, setShowSwitchModelModal] = useState(false);
|
||||||
const [switchModelProvider, setSwitchModelProvider] = useState<string | null>(null);
|
const [switchModelProvider, setSwitchModelProvider] = useState<string | null>(null);
|
||||||
|
const [isActiveProvider, setIsActiveProvider] = useState(false);
|
||||||
|
const { getCurrentModelAndProvider } = useModelAndProvider();
|
||||||
const [editingProvider, setEditingProvider] = useState<{
|
const [editingProvider, setEditingProvider] = useState<{
|
||||||
id: string;
|
id: string;
|
||||||
config: DeclarativeProviderConfig;
|
config: DeclarativeProviderConfig;
|
||||||
isEditable: boolean;
|
isEditable: boolean;
|
||||||
|
providerType: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const handleProviderLaunchWithModelSelection = useCallback((provider: ProviderDetails) => {
|
const handleProviderLaunchWithModelSelection = useCallback((provider: ProviderDetails) => {
|
||||||
@@ -92,14 +96,24 @@ function ProviderCards({
|
|||||||
id: provider.name,
|
id: provider.name,
|
||||||
config: result.data.config,
|
config: result.data.config,
|
||||||
isEditable: result.data.is_editable,
|
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);
|
setShowCustomProviderModal(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
openModal(provider);
|
openModal(provider);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[openModal]
|
[openModal, getCurrentModelAndProvider]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdateCustomProvider = useCallback(
|
const handleUpdateCustomProvider = useCallback(
|
||||||
@@ -124,9 +138,26 @@ function ProviderCards({
|
|||||||
[editingProvider, refreshProviders]
|
[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(() => {
|
const handleCloseModal = useCallback(() => {
|
||||||
setShowCustomProviderModal(false);
|
setShowCustomProviderModal(false);
|
||||||
setEditingProvider(null);
|
setEditingProvider(null);
|
||||||
|
setIsActiveProvider(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onCloseProviderConfig = useCallback(() => {
|
const onCloseProviderConfig = useCallback(() => {
|
||||||
@@ -178,8 +209,10 @@ function ProviderCards({
|
|||||||
const providerCards = useMemo(() => {
|
const providerCards = useMemo(() => {
|
||||||
// providers needs to be an array
|
// providers needs to be an array
|
||||||
const providersArray = Array.isArray(providers) ? providers : [];
|
const providersArray = Array.isArray(providers) ? providers : [];
|
||||||
// Sort providers alphabetically by name
|
// Sort providers alphabetically by display name
|
||||||
const sortedProviders = [...providersArray].sort((a, b) => a.name.localeCompare(b.name));
|
const sortedProviders = [...providersArray].sort((a, b) =>
|
||||||
|
a.metadata.display_name.localeCompare(b.metadata.display_name)
|
||||||
|
);
|
||||||
const cards = sortedProviders.map((provider) => (
|
const cards = sortedProviders.map((provider) => (
|
||||||
<ProviderCard
|
<ProviderCard
|
||||||
key={provider.name}
|
key={provider.name}
|
||||||
@@ -222,6 +255,10 @@ function ProviderCards({
|
|||||||
isEditable={editable}
|
isEditable={editable}
|
||||||
onSubmit={editingProvider ? handleUpdateCustomProvider : handleCreateCustomProvider}
|
onSubmit={editingProvider ? handleUpdateCustomProvider : handleCreateCustomProvider}
|
||||||
onCancel={handleCloseModal}
|
onCancel={handleCloseModal}
|
||||||
|
onDelete={
|
||||||
|
editingProvider?.providerType === 'Custom' ? handleDeleteCustomProvider : undefined
|
||||||
|
}
|
||||||
|
isActiveProvider={isActiveProvider}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>{' '}
|
</Dialog>{' '}
|
||||||
|
|||||||
+62
-6
@@ -4,10 +4,13 @@ import { Select } from '../../../../../ui/Select';
|
|||||||
import { Button } from '../../../../../ui/button';
|
import { Button } from '../../../../../ui/button';
|
||||||
import { SecureStorageNotice } from '../SecureStorageNotice';
|
import { SecureStorageNotice } from '../SecureStorageNotice';
|
||||||
import { UpdateCustomProviderRequest } from '../../../../../../api';
|
import { UpdateCustomProviderRequest } from '../../../../../../api';
|
||||||
|
import { Trash2, AlertTriangle } from 'lucide-react';
|
||||||
|
|
||||||
interface CustomProviderFormProps {
|
interface CustomProviderFormProps {
|
||||||
onSubmit: (data: UpdateCustomProviderRequest) => void;
|
onSubmit: (data: UpdateCustomProviderRequest) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
onDelete?: () => Promise<void>;
|
||||||
|
isActiveProvider?: boolean;
|
||||||
initialData: UpdateCustomProviderRequest | null;
|
initialData: UpdateCustomProviderRequest | null;
|
||||||
isEditable?: boolean;
|
isEditable?: boolean;
|
||||||
}
|
}
|
||||||
@@ -15,6 +18,8 @@ interface CustomProviderFormProps {
|
|||||||
export default function CustomProviderForm({
|
export default function CustomProviderForm({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
onDelete,
|
||||||
|
isActiveProvider = false,
|
||||||
initialData,
|
initialData,
|
||||||
isEditable,
|
isEditable,
|
||||||
}: CustomProviderFormProps) {
|
}: CustomProviderFormProps) {
|
||||||
@@ -26,6 +31,7 @@ export default function CustomProviderForm({
|
|||||||
const [requiresApiKey, setRequiresApiKey] = useState(false);
|
const [requiresApiKey, setRequiresApiKey] = useState(false);
|
||||||
const [supportsStreaming, setSupportsStreaming] = useState(true);
|
const [supportsStreaming, setSupportsStreaming] = useState(true);
|
||||||
const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});
|
const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});
|
||||||
|
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
@@ -257,12 +263,62 @@ export default function CustomProviderForm({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<SecureStorageNotice />
|
<SecureStorageNotice />
|
||||||
<div className="flex justify-end space-x-2 pt-4">
|
|
||||||
<Button type="button" variant="outline" onClick={onCancel}>
|
{showDeleteConfirmation ? (
|
||||||
Cancel
|
<div className="pt-4 space-y-3">
|
||||||
</Button>
|
{isActiveProvider ? (
|
||||||
<Button type="submit">{initialData ? 'Update Provider' : 'Create Provider'}</Button>
|
<div className="px-4 py-3 bg-yellow-600/20 border border-yellow-500/30 rounded">
|
||||||
</div>
|
<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>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user