mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-22 02:07:13 +00:00
fix(wizard): prevent duplicate model additions in scan page
- Track added model names in state - Disable already-added models with 'Added' badge - Clear selection after successful add - Add Next button at bottom when models have been added
This commit is contained in:
+56
-23
@@ -1155,6 +1155,9 @@ function WizardModelScan({
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [manualModelName, setManualModelName] = useState('');
|
const [manualModelName, setManualModelName] = useState('');
|
||||||
const [manualAdding, setManualAdding] = useState(false);
|
const [manualAdding, setManualAdding] = useState(false);
|
||||||
|
const [addedModelNames, setAddedModelNames] = useState<Set<string>>(
|
||||||
|
new Set(initialSelectedModels),
|
||||||
|
);
|
||||||
|
|
||||||
// Auto-scan on mount
|
// Auto-scan on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1216,7 +1219,13 @@ function WizardModelScan({
|
|||||||
toast.success(
|
toast.success(
|
||||||
t('wizard.provider.modelsAdded', { count: selectedModels.size }),
|
t('wizard.provider.modelsAdded', { count: selectedModels.size }),
|
||||||
);
|
);
|
||||||
onModelsAdded();
|
setAddedModelNames((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
for (const name of selectedModels) next.add(name);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setSelectedModels(new Set());
|
||||||
|
onSelectedModelsChange(new Set());
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(t('wizard.provider.modelsAddError'));
|
toast.error(t('wizard.provider.modelsAddError'));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1238,8 +1247,8 @@ function WizardModelScan({
|
|||||||
extra_args: {},
|
extra_args: {},
|
||||||
} as never);
|
} as never);
|
||||||
toast.success(t('wizard.provider.modelsAdded', { count: 1 }));
|
toast.success(t('wizard.provider.modelsAdded', { count: 1 }));
|
||||||
|
setAddedModelNames((prev) => new Set(prev).add(name));
|
||||||
setManualModelName('');
|
setManualModelName('');
|
||||||
onModelsAdded();
|
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(t('wizard.provider.modelsAddError'));
|
toast.error(t('wizard.provider.modelsAddError'));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1282,29 +1291,43 @@ function WizardModelScan({
|
|||||||
{scannedModels.length > 0 ? (
|
{scannedModels.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<div className="max-h-48 overflow-y-auto space-y-2">
|
<div className="max-h-48 overflow-y-auto space-y-2">
|
||||||
{filteredModels.map((model) => (
|
{filteredModels.map((model) => {
|
||||||
<label
|
const isAdded = addedModelNames.has(model.name);
|
||||||
key={model.name}
|
return (
|
||||||
className="flex items-center gap-3 p-3 rounded-lg border cursor-pointer hover:bg-accent transition-colors"
|
<label
|
||||||
>
|
key={model.name}
|
||||||
<input
|
className={cn(
|
||||||
type="checkbox"
|
'flex items-center gap-3 p-3 rounded-lg border transition-colors',
|
||||||
checked={selectedModels.has(model.name)}
|
isAdded
|
||||||
onChange={() => toggleModel(model.name)}
|
? 'opacity-50 cursor-not-allowed bg-muted/50'
|
||||||
className="w-4 h-4 rounded"
|
: 'cursor-pointer hover:bg-accent',
|
||||||
/>
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
>
|
||||||
<span className="text-sm font-medium truncate block">
|
<input
|
||||||
{model.name}
|
type="checkbox"
|
||||||
</span>
|
checked={selectedModels.has(model.name) || isAdded}
|
||||||
{model.context_length && (
|
disabled={isAdded}
|
||||||
<span className="text-xs text-muted-foreground">
|
onChange={() => toggleModel(model.name)}
|
||||||
ctx: {model.context_length.toLocaleString()}
|
className="w-4 h-4 rounded"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<span className="text-sm font-medium truncate block">
|
||||||
|
{model.name}
|
||||||
|
</span>
|
||||||
|
{model.context_length && (
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
ctx: {model.context_length.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{isAdded && (
|
||||||
|
<span className="text-xs text-green-600 font-medium shrink-0">
|
||||||
|
{t('wizard.provider.alreadyAdded')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</label>
|
||||||
</label>
|
);
|
||||||
))}
|
})}
|
||||||
{filteredModels.length === 0 && (
|
{filteredModels.length === 0 && (
|
||||||
<p className="text-sm text-muted-foreground text-center py-4">
|
<p className="text-sm text-muted-foreground text-center py-4">
|
||||||
{t('wizard.provider.noMatch')}
|
{t('wizard.provider.noMatch')}
|
||||||
@@ -1365,6 +1388,16 @@ function WizardModelScan({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Next button when models have been added */}
|
||||||
|
{addedModelNames.size > 0 && (
|
||||||
|
<div className="flex justify-end pt-2 border-t">
|
||||||
|
<Button onClick={onModelsAdded}>
|
||||||
|
{t('wizard.next')}
|
||||||
|
<ArrowRight className="w-4 h-4 ml-1.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1873,6 +1873,7 @@ const enUS = {
|
|||||||
manualModelPlaceholder: 'Enter model name to add manually',
|
manualModelPlaceholder: 'Enter model name to add manually',
|
||||||
addManual: 'Add',
|
addManual: 'Add',
|
||||||
saveAndNext: 'Save & Next',
|
saveAndNext: 'Save & Next',
|
||||||
|
alreadyAdded: 'Added',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'Choose Model Source',
|
title: 'Choose Model Source',
|
||||||
|
|||||||
@@ -1731,6 +1731,7 @@ const esES = {
|
|||||||
manualModelPlaceholder: 'Ingresa el nombre del modelo para agregar manualmente',
|
manualModelPlaceholder: 'Ingresa el nombre del modelo para agregar manualmente',
|
||||||
addManual: 'Agregar',
|
addManual: 'Agregar',
|
||||||
saveAndNext: 'Guardar y siguiente',
|
saveAndNext: 'Guardar y siguiente',
|
||||||
|
alreadyAdded: 'Agregado',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'Elegir fuente del modelo',
|
title: 'Elegir fuente del modelo',
|
||||||
|
|||||||
@@ -1792,6 +1792,7 @@ const jaJP = {
|
|||||||
manualModelPlaceholder: 'モデル名を入力して手動追加',
|
manualModelPlaceholder: 'モデル名を入力して手動追加',
|
||||||
addManual: '追加',
|
addManual: '追加',
|
||||||
saveAndNext: '保存して次へ',
|
saveAndNext: '保存して次へ',
|
||||||
|
alreadyAdded: '追加済み',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'モデルソースを選択',
|
title: 'モデルソースを選択',
|
||||||
|
|||||||
@@ -1701,6 +1701,7 @@ const ruRU = {
|
|||||||
manualModelPlaceholder: 'Введите имя модели для ручного добавления',
|
manualModelPlaceholder: 'Введите имя модели для ручного добавления',
|
||||||
addManual: 'Добавить',
|
addManual: 'Добавить',
|
||||||
saveAndNext: 'Сохранить и далее',
|
saveAndNext: 'Сохранить и далее',
|
||||||
|
alreadyAdded: 'Добавлено',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'Выберите источник модели',
|
title: 'Выберите источник модели',
|
||||||
|
|||||||
@@ -1665,6 +1665,7 @@ const thTH = {
|
|||||||
manualModelPlaceholder: 'กรอกชื่อโมเดลเพื่อเพิ่มด้วยตนเอง',
|
manualModelPlaceholder: 'กรอกชื่อโมเดลเพื่อเพิ่มด้วยตนเอง',
|
||||||
addManual: 'เพิ่ม',
|
addManual: 'เพิ่ม',
|
||||||
saveAndNext: 'บันทึกและถัดไป',
|
saveAndNext: 'บันทึกและถัดไป',
|
||||||
|
alreadyAdded: 'เพิ่มแล้ว',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'เลือกแหล่งโมเดล',
|
title: 'เลือกแหล่งโมเดล',
|
||||||
|
|||||||
@@ -1691,6 +1691,7 @@ const viVN = {
|
|||||||
manualModelPlaceholder: 'Nhập tên mô hình để thêm thủ công',
|
manualModelPlaceholder: 'Nhập tên mô hình để thêm thủ công',
|
||||||
addManual: 'Thêm',
|
addManual: 'Thêm',
|
||||||
saveAndNext: 'Lưu & Tiếp theo',
|
saveAndNext: 'Lưu & Tiếp theo',
|
||||||
|
alreadyAdded: 'Đã thêm',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: 'Chọn nguồn mô hình',
|
title: 'Chọn nguồn mô hình',
|
||||||
|
|||||||
@@ -1789,6 +1789,7 @@ const zhHans = {
|
|||||||
manualModelPlaceholder: '输入模型名称手动添加',
|
manualModelPlaceholder: '输入模型名称手动添加',
|
||||||
addManual: '添加',
|
addManual: '添加',
|
||||||
saveAndNext: '保存并下一步',
|
saveAndNext: '保存并下一步',
|
||||||
|
alreadyAdded: '已添加',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: '选择模型来源',
|
title: '选择模型来源',
|
||||||
|
|||||||
@@ -1612,6 +1612,7 @@ const zhHant = {
|
|||||||
manualModelPlaceholder: '輸入模型名稱手動新增',
|
manualModelPlaceholder: '輸入模型名稱手動新增',
|
||||||
addManual: '新增',
|
addManual: '新增',
|
||||||
saveAndNext: '儲存並下一步',
|
saveAndNext: '儲存並下一步',
|
||||||
|
alreadyAdded: '已新增',
|
||||||
},
|
},
|
||||||
modelSource: {
|
modelSource: {
|
||||||
title: '選擇模型來源',
|
title: '選擇模型來源',
|
||||||
|
|||||||
Reference in New Issue
Block a user