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
; })}
-
- {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) => )}
}