Quelltext des aktuellen Standes
<?php
$this->layout('layouts/main', [
'page_title' => $page_title,
'current_route' => 'vms',
'full_width' => true,
]);
$vm = $vm ?? null;
$hosts = $hosts ?? [];
$can_manage = $can_manage ?? false;
$isEdit = $vm !== null;
?>
<div class="vmf-wrap">
<div class="content-header">
<div>
<h1><?= $this->e($page_title) ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item"><a href="/vms"><?= $this->te('Virtualisierung') ?></a></li>
<?php if ($isEdit): ?>
<li class="breadcrumb-item"><a href="/vms/<?= $vm['id'] ?>"><?= $this->e($vm['name']) ?></a></li>
<li class="breadcrumb-item active"><?= $this->te('Bearbeiten') ?></li>
<?php else: ?>
<li class="breadcrumb-item active"><?= $this->te('Neue VM') ?></li>
<?php endif; ?>
</ol>
</div>
</div>
<form action="<?= $this->e($form_action) ?>" method="POST" class="vmf-layout">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<!-- Hauptspalte -->
<div class="vmf-main">
<section class="vmf-card">
<h2 class="vmf-section-head">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
<?= $this->te('Stammdaten') ?>
</h2>
<div class="vmf-card-body">
<div class="vmf-row-2">
<div class="vmf-field">
<label for="vmf_name"><?= $this->te('Name') ?> <span class="vmf-req">*</span></label>
<input type="text" id="vmf_name" name="name" required maxlength="255"
value="<?= $this->e($vm['name'] ?? '') ?>"
placeholder="<?= $this->te('z.B. web-server-01') ?>">
</div>
<div class="vmf-field">
<label for="vmf_status"><?= $this->te('Status') ?></label>
<select id="vmf_status" name="status">
<option value="running" <?= ($vm['status'] ?? 'running') === 'running' ? 'selected' : '' ?>><?= $this->te('Laufend') ?></option>
<option value="stopped" <?= ($vm['status'] ?? '') === 'stopped' ? 'selected' : '' ?>><?= $this->te('Gestoppt') ?></option>
<option value="paused" <?= ($vm['status'] ?? '') === 'paused' ? 'selected' : '' ?>><?= $this->te('Pausiert') ?></option>
<option value="error" <?= ($vm['status'] ?? '') === 'error' ? 'selected' : '' ?>><?= $this->te('Fehler') ?></option>
</select>
</div>
</div>
<div class="vmf-row-2">
<div class="vmf-field">
<label for="vmf_ip"><?= $this->te('IP-Adresse') ?></label>
<input type="text" id="vmf_ip" name="ip_address" maxlength="45"
value="<?= $this->e($vm['ip_address'] ?? '') ?>"
placeholder="z.B. 192.168.1.100">
<span class="vmf-hint"><?= $this->te('Wird automatisch in IPAM eingetragen, wenn ein Subnetz passt.') ?></span>
</div>
<div class="vmf-field">
<label for="vmf_os"><?= $this->te('Betriebssystem') ?></label>
<input type="text" id="vmf_os" name="os" maxlength="255"
value="<?= $this->e($vm['os'] ?? '') ?>"
placeholder="<?= $this->te('z.B. Ubuntu 22.04 LTS') ?>">
</div>
</div>
<div class="vmf-field">
<label for="vmf_host"><?= $this->te('Host') ?></label>
<select id="vmf_host" name="host_id">
<option value=""><?= $this->te('Kein Host zugewiesen') ?></option>
<?php foreach ($hosts as $h): ?>
<option value="<?= $h['id'] ?>" <?= ($vm['host_id'] ?? '') == $h['id'] ? 'selected' : '' ?>>
<?= $this->e($h['name']) ?>
<?php if (!empty($h['hypervisor'])): ?>(<?= $this->e($h['hypervisor']) ?>)<?php endif; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="vmf-field">
<label for="vmf_purpose"><?= $this->te('Zweck / Verwendungszweck') ?></label>
<textarea id="vmf_purpose" name="purpose" rows="3"
placeholder="<?= $this->te('Wofür wird diese VM genutzt?') ?>"><?= $this->e($vm['purpose'] ?? '') ?></textarea>
</div>
</div>
</section>
<section class="vmf-card">
<h2 class="vmf-section-head">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="2" y="3" width="20" height="14" rx="2"/><polyline points="8 21 12 17 16 21"/></svg>
<?= $this->te('Ressourcen') ?>
</h2>
<div class="vmf-card-body">
<div class="vmf-row-3">
<div class="vmf-field">
<label for="vmf_cpu"><?= $this->te('vCPU-Kerne') ?></label>
<input type="number" id="vmf_cpu" name="cpu_cores" min="1" max="1024"
value="<?= $this->e($vm['cpu_cores'] ?? '') ?>"
placeholder="z.B. 4">
</div>
<div class="vmf-field">
<label for="vmf_ram"><?= $this->t('RAM (GB)') ?></label>
<input type="number" id="vmf_ram" name="ram_gb" min="0" step="0.5"
value="<?= $this->e($vm['ram_gb'] ?? '') ?>"
placeholder="z.B. 8">
</div>
<div class="vmf-field">
<label for="vmf_disk"><?= $this->te('Speicher (GB)') ?></label>
<input type="number" id="vmf_disk" name="storage_gb" min="0" step="1"
value="<?= $this->e($vm['storage_gb'] ?? '') ?>"
placeholder="z.B. 100">
</div>
</div>
</div>
</section>
<section class="vmf-card">
<h2 class="vmf-section-head">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
<?= $this->te('Notizen') ?>
</h2>
<div class="vmf-card-body">
<div class="vmf-field">
<label for="vmf_notes"><?= $this->te('Interne Notizen') ?></label>
<textarea id="vmf_notes" name="notes" rows="4"
placeholder="<?= $this->te('Interne Anmerkungen, Konfigurationshinweise…') ?>"><?= $this->e($vm['notes'] ?? '') ?></textarea>
</div>
</div>
</section>
</div><!-- /vmf-main -->
<!-- Sidebar -->
<aside class="vmf-sidebar">
<?php if (!empty($hosts)): ?>
<section class="vmf-card">
<h2 class="vmf-section-head">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/></svg>
<?= $this->te('Verfügbare Hosts') ?>
</h2>
<div class="vmf-card-body">
<ul class="vmf-host-list">
<?php foreach ($hosts as $h): ?>
<li class="vmf-host-item <?= ($vm['host_id'] ?? '') == $h['id'] ? 'vmf-host-selected' : '' ?>">
<span class="vmf-host-name"><?= $this->e($h['name']) ?></span>
<span class="vmf-host-info">
<?= $h['cpu_cores'] ? (int)$h['cpu_cores'] . ' CPU' : '' ?>
<?= $h['ram_gb'] ? '· ' . number_format((float)$h['ram_gb'], 0) . ' GB RAM' : '' ?>
</span>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>
<?php endif; ?>
<div class="vmf-actions">
<button type="submit" class="btn btn-primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><polyline points="20 6 9 17 4 12"/></svg>
<?= $isEdit ? 'Änderungen speichern' : 'VM erstellen' ?>
</button>
<a href="<?= $isEdit ? '/vms/' . $vm['id'] : '/vms' ?>" class="btn btn-default"><?= $this->te('Abbrechen') ?></a>
</div>
</aside>
</form>
</div>
<style>
/* ====================================================
VM-Formular · Prefix: vmf-
==================================================== */
.vmf-wrap { display:flex; flex-direction:column; }
.vmf-layout { display:grid; grid-template-columns:1fr 320px; gap:1.5rem; padding:1.5rem 1rem 3rem; background:var(--gray-50); }
.vmf-main { display:flex; flex-direction:column; gap:1.25rem; }
.vmf-sidebar { display:flex; flex-direction:column; gap:1rem; }
.vmf-card { background:var(--color-bg); border-radius:0.25rem; box-shadow:0 1px 3px rgba(0,0,0,0.06); overflow:hidden; }
.vmf-section-head {
display:flex; align-items:center; gap:0.5rem;
font-size:0.8rem; font-weight:700; color:var(--gray-700);
text-transform:uppercase; letter-spacing:0.04em;
padding:0.875rem 1.25rem; border-bottom:1px solid var(--gray-100); margin:0;
background:var(--gray-50);
}
.vmf-section-head svg { color:var(--gray-400); flex-shrink:0; }
.vmf-card-body { padding:1.25rem; display:flex; flex-direction:column; gap:0.875rem; }
.vmf-field { display:flex; flex-direction:column; gap:0.35rem; }
.vmf-field label { font-size:0.8rem; font-weight:600; color:var(--gray-700); }
.vmf-field input, .vmf-field select, .vmf-field textarea {
width:100%; padding:0.5rem 0.75rem; border:1px solid var(--gray-300);
border-radius:0.25rem; font-size:0.875rem; color:var(--gray-900);
background:var(--color-bg); transition:border-color .15s;
font-family:inherit;
}
.vmf-field input:focus, .vmf-field select:focus, .vmf-field textarea:focus {
outline:none; border-color:var(--color-primary); box-shadow:0 0 0 2px rgba(var(--color-primary-rgb), 0.15);
}
.vmf-field textarea { resize:vertical; }
.vmf-hint { font-size:0.75rem; color:var(--gray-400); }
.vmf-req { color:var(--red-600); }
.vmf-row-2 { display:grid; grid-template-columns:1fr 1fr; gap:0.875rem; }
.vmf-row-3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:0.875rem; }
/* Host List */
.vmf-host-list { margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:0.375rem; }
.vmf-host-item {
padding:0.5rem 0.75rem; border-radius:0.25rem; border:1px solid var(--gray-100);
background:var(--gray-50); font-size:0.8rem;
}
.vmf-host-selected { border-color:var(--color-primary); background:var(--blue-50); }
.vmf-host-name { display:block; font-weight:500; color:var(--gray-700); }
.vmf-host-info { font-size:0.72rem; color:var(--gray-400); }
/* Actions */
.vmf-actions { display:flex; flex-direction:column; gap:0.5rem; }
@media (max-width:1100px) { .vmf-layout { grid-template-columns:1fr 280px; } }
@media (max-width:900px) { .vmf-layout { grid-template-columns:1fr; padding:1rem; } .vmf-sidebar { position:static; } }
@media (max-width:640px) { .vmf-row-2, .vmf-row-3 { grid-template-columns:1fr; } }
</style>