fix(web): confirm before removing models from a provider instance (#18400)

This commit is contained in:
chanx
2026-08-18 11:22:08 +08:00
committed by GitHub
parent d02723ca76
commit c6ec7d4eee
4 changed files with 51 additions and 22 deletions

View File

@@ -9,6 +9,7 @@ export default {
delete: 'Delete',
deleteModalTitle: 'Are you sure to delete it ?',
deleteThem: 'Are you sure to delete them ?',
removeModalTitle: 'Are you sure to remove it ?',
ok: 'Ok',
cancel: 'Cancel',
yes: 'Yes',

View File

@@ -9,6 +9,7 @@ export default {
delete: '删除',
deleteModalTitle: '确定删除吗?',
deleteThem: '确定要删除吗?',
removeModalTitle: '确定移除吗?',
ok: '确认',
cancel: '取消',
yes: '是',

View File

@@ -14,7 +14,9 @@
* limitations under the License.
*/
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import { Minus, Plus } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ModelRowProps } from '../interface';
import { ModelTypeBadges } from './model-type-badges';
import { ModelVerifyButton } from './model-verify-button';
@@ -31,6 +33,23 @@ export function ModelRow({
onEdit,
editLabel,
}: ModelRowProps) {
const { t } = useTranslation();
// Add / remove toggle. When the model is already attached the click is
// intercepted by `ConfirmDeleteDialog` (which acts as the trigger), so
// the button itself must not carry the remove handler - removal runs
// from the dialog's confirm action instead.
const toggleButton = (
<button
type="button"
className="size-6 flex items-center justify-center rounded-md transition-colors text-text-secondary"
onClick={isAdded ? undefined : onAdd}
aria-label={isAdded ? `Remove ${model.name}` : `Add ${model.name}`}
>
{isAdded ? <Minus className="size-4" /> : <Plus className="size-4" />}
</button>
);
return (
<li
key={model.name}
@@ -62,18 +81,15 @@ export function ModelRow({
/>
{!hideActions && (
<button
type="button"
className="size-6 flex items-center justify-center rounded-md transition-colors text-text-secondary"
onClick={() => (isAdded ? onRemove() : onAdd())}
aria-label={isAdded ? `Remove ${model.name}` : `Add ${model.name}`}
<ConfirmDeleteDialog
hidden={!isAdded}
onOk={onRemove}
title={t('common.removeModalTitle')}
okButtonText={t('common.remove')}
content={{ title: model.name }}
>
{isAdded ? (
<Minus className="size-4" />
) : (
<Plus className="size-4" />
)}
</button>
{toggleButton}
</ConfirmDeleteDialog>
)}
</div>
</li>

View File

@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import { Button } from '@/components/ui/button';
import { SearchInput } from '@/components/ui/input';
import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
@@ -317,18 +318,28 @@ export function ModelsSection(props: ModelsSectionProps) {
{tSetting('batchVerifyModels')}
</Button>
{!hideActions && (
<Button
variant="outline"
size="sm"
onClick={handleBatchToggleModels}
disabled={batchLoading || filteredModels.length === 0}
data-testid="models-batch-toggle"
// When the toggle is in "remove all" mode the click opens a
// confirmation dialog instead of mutating directly; the button
// acts as the dialog trigger, so the handler moves to `onOk`.
<ConfirmDeleteDialog
hidden={!allFilteredAdded}
onOk={handleBatchToggleModels}
title={t('common.removeModalTitle')}
okButtonText={t('common.remove')}
>
{batchLoading && <Loader2 className="size-3 animate-spin" />}
{allFilteredAdded
? tSetting('batchRemoveModels')
: tSetting('batchAddModels')}
</Button>
<Button
variant="outline"
size="sm"
onClick={allFilteredAdded ? undefined : handleBatchToggleModels}
disabled={batchLoading || filteredModels.length === 0}
data-testid="models-batch-toggle"
>
{batchLoading && <Loader2 className="size-3 animate-spin" />}
{allFilteredAdded
? tSetting('batchRemoveModels')
: tSetting('batchAddModels')}
</Button>
</ConfirmDeleteDialog>
)}
</div>