diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 9d30ce4..ee4372d 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -68,7 +68,7 @@ export default function Dashboard() { return () => clearInterval(timer); }, [refresh]); - // Фильтрация шаблонов: сначала по типу (vm/lxc), потом по ОС. + // Шаблоны строго по типу: при выбранном "vm" — только VM, при "lxc" — только LXC. const typeTemplates = useMemo( () => templates.filter((t) => t.guest_type === guestType), [templates, guestType] @@ -78,12 +78,17 @@ export default function Dashboard() { [typeTemplates, selectedOS] ); - // Сбрасываем выбранный тариф при смене типа — иначе может быть выбран - // шаблон другого типа. + // При смене типа — сбросить выбор ОС и тарифа. Иначе может остаться шаблон + // другого типа или несуществующая ОС. useEffect(() => { setTemplateId(""); setSelectedPreset(null); - }, [guestType]); + // Если текущая выбранная ОС недоступна для нового типа — сбросить на дефолт. + if (!OS_OPTIONS.some((os) => os.key === selectedOS && filterByOS(typeTemplates, os.key).length > 0)) { + const firstAvailable = OS_OPTIONS.find((os) => filterByOS(typeTemplates, os.key).length > 0); + setSelectedOS(firstAvailable?.key || "ubuntu"); + } + }, [guestType, typeTemplates]); // Авто-выбор первого подходящего шаблона при изменении фильтров. useEffect(() => { @@ -122,7 +127,7 @@ export default function Dashboard() { name, template_id: Number(templateId), ciuser: isLxc ? "" : (ciuser || ""), - cipassword: isLxc ? (cipassword || "") : (cipassword || ""), + cipassword: cipassword || "", }); setName(""); setCipassword(""); @@ -155,6 +160,8 @@ export default function Dashboard() { const submitLabel = isLxc ? (busy ? "⏳ Создаём..." : "📦 Создать LXC") : (busy ? "⏳ Создаём..." : "🚀 Развернуть VM"); + const hasTypeTemplates = typeTemplates.length > 0; + const typeLabel = isLxc ? "LXC-контейнеров" : "VM (виртуальных машин)"; return (
@@ -206,8 +213,6 @@ export default function Dashboard() {
-
{OS_OPTIONS.map((os) => { const active = selectedOS === os.key; const available = filterByOS(typeTemplates, os.key).length > 0; return ; })}
-
setName(event.target.value)} placeholder="my-server" required />
{isLxc ? ( @@ -218,9 +223,84 @@ export default function Dashboard() {
setCipassword(event.target.value)} placeholder="••••••" required={isLxc} title={isLxc ? "Задайте root-пароль для LXC" : "Пароль для cloud-init"} />
-
{PRESETS.map((preset) => { const match = matchPreset(osTemplates, preset); const available = Boolean(match); return ; })}
+ {/* Система и тариф — только если есть шаблоны этого типа. */} + {!hasTypeTemplates ? ( +
+
+
📦
+
Нет шаблонов для {typeLabel}
+
+ Перейдите в раздел «Шаблоны» слева → + нажмите «🔍 Найти шаблоны на Proxmox» → + импортируйте нужный шаблон. После этого он появится здесь. +
+
+
+ ) : ( + <> +
+ +
{OS_OPTIONS.map((os) => { + const active = selectedOS === os.key; + const available = filterByOS(typeTemplates, os.key).length > 0; + return ( + + ); + })}
+
- +
+ +
+ +
{PRESETS.map((preset) => { + const match = matchPreset(osTemplates, preset); + const available = Boolean(match); + return ( + + ); + })}
+
+
+ + )} + + } {loading ?
Загрузка...
: instances.length === 0 ?
☁️
Нет активных VPS
Нажмите «+ Новый VM» или «+ Новый LXC» чтобы создать
:
{instances.map((instance) => )}
}