Files
HexaHost-Panel/resources/views/vms/create.blade.php
TheOnlyMace e00d92a75b Enhance VM creation request validation and UI.
- Introduced `prepareForValidation` method to handle input merging for `behind_traefik` and `devices`.
- Updated validation rules to conditionally require `subdomain` based on `behind_traefik`.
- Added custom attribute names and error messages for better user feedback.
- Improved the create VM form to dynamically show/hide subdomain fields based on the `behind_traefik` checkbox state.
- Adjusted the user selection logic to display a message when no customers are available.
2026-05-17 14:32:08 +02:00

113 lines
6.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('layouts.app')
@section('title', 'VM erstellen')
@section('heading', 'Neue VM erstellen')
@section('content')
<form method="POST" action="{{ route('vms.store') }}" class="max-w-3xl space-y-6">
@csrf
<section class="rounded-xl border border-slate-800 bg-slate-900/60 p-6 space-y-4">
<h2 class="font-semibold">Allgemein</h2>
<label class="block">
<span class="text-sm text-slate-400">VM-Name</span>
<input name="name" value="{{ old('name') }}" required class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2">
</label>
@if(auth()->user()->isAdmin())
<label class="block">
<span class="text-sm text-slate-400">Zugewiesener Benutzer</span>
<select name="user_id" class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2">
<option value=""> Mir selbst (Admin) </option>
@foreach($customers as $c)
<option value="{{ $c->id }}" @selected(old('user_id') == $c->id)>{{ $c->name }} ({{ $c->email }})</option>
@endforeach
</select>
@if($customers->isEmpty())
<p class="mt-1 text-xs text-slate-500">Noch keine Kundenkonten VM wird deinem Admin-Konto zugeordnet. Kunden unter „Benutzer“ anlegen.</p>
@endif
</label>
@endif
@php
$behindTraefik = filter_var(old('behind_traefik', '1'), FILTER_VALIDATE_BOOLEAN);
@endphp
<label class="flex items-center gap-2">
<input type="checkbox" name="behind_traefik" value="1" @checked($behindTraefik) id="behind_traefik" class="rounded border-slate-600 text-cyan-500">
<span class="text-sm">Hinter Traefik (Subdomain + DNS)</span>
</label>
<div id="traefik-fields" @if(!$behindTraefik) style="display:none" @endif>
<label class="block">
<span class="text-sm text-slate-400">Subdomain <span class="text-red-400">*</span></span>
<div class="mt-1 flex">
<input name="subdomain" id="subdomain" value="{{ old('subdomain') }}" placeholder="meine-vm"
class="w-full rounded-l-lg border border-slate-700 bg-slate-800 px-3 py-2 @error('subdomain') border-red-500 @enderror">
<span class="flex items-center rounded-r-lg border border-l-0 border-slate-700 bg-slate-800 px-3 text-sm text-slate-500">.{{ config('hosting.plesk.base_domain') }}</span>
</div>
@error('subdomain')<p class="mt-1 text-xs text-red-400">{{ $message }}</p>@enderror
<p class="mt-1 text-xs text-slate-500">Pflichtfeld bei „Hinter Traefik“ (nur a-z, 0-9, Bindestrich).</p>
</label>
</div>
<label class="block">
<span class="text-sm text-slate-400">Privater IP-Pool</span>
<select name="ip_pool_id" class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2">
<option value="">Standard-Pool</option>
@foreach($privatePools as $pool)
<option value="{{ $pool->id }}" @selected(old('ip_pool_id') == $pool->id)>{{ $pool->name }} ({{ $pool->freeIpsCount() }} frei)</option>
@endforeach
</select>
</label>
<p class="text-xs text-slate-500">Ohne Traefik wird zusätzlich eine öffentliche IP aus dem Public-Pool vergeben.</p>
</section>
<section class="rounded-xl border border-slate-800 bg-slate-900/60 p-6 space-y-4">
<h2 class="font-semibold">Ressourcen</h2>
<div class="grid gap-4 sm:grid-cols-3">
<label><span class="text-sm text-slate-400">vCPUs</span>
<input type="number" name="cpu" value="{{ old('cpu', config('hosting.defaults.cpu')) }}" min="1" max="32" class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2"></label>
<label><span class="text-sm text-slate-400">RAM (MB)</span>
<input type="number" name="ram" value="{{ old('ram', config('hosting.defaults.ram')) }}" min="512" class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2"></label>
<label><span class="text-sm text-slate-400">Disk (GB)</span>
<input type="number" name="disk" value="{{ old('disk', config('hosting.defaults.disk')) }}" min="10" class="mt-1 w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2"></label>
</div>
</section>
<section class="rounded-xl border border-slate-800 bg-slate-900/60 p-6 space-y-4">
<h2 class="font-semibold">Installations-ISO (optional)</h2>
<p class="text-xs text-slate-500">ISO wird beim Provisioning als CD-ROM eingebunden (Boot-Reihenfolge: CD zuerst).</p>
<select name="install_iso" class="w-full rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 text-sm">
<option value="">Keine ISO / Cloud-Init</option>
@foreach($isos as $iso)
<option value="{{ $iso['volid'] }}" @selected(old('install_iso') === $iso['volid'])>{{ $iso['label'] }}</option>
@endforeach
</select>
@if(empty($isos))
<p class="text-xs text-amber-400">Keine ISOs von Proxmox geladen Storage {{ config('hosting.proxmox.iso_storage') }} prüfen.</p>
@endif
</section>
<section class="rounded-xl border border-slate-800 bg-slate-900/60 p-6">
<h2 class="mb-4 font-semibold">Zusätzliche Geräte</h2>
@include('partials.vm-device-fields', ['deviceTypes' => $deviceTypes])
</section>
<button type="submit" class="rounded-lg bg-cyan-600 px-6 py-2.5 font-medium hover:bg-cyan-500">VM provisionieren</button>
</form>
@push('scripts')
<script>
const cb = document.getElementById('behind_traefik');
const fields = document.getElementById('traefik-fields');
const subdomain = document.getElementById('subdomain');
function toggle() {
const on = cb.checked;
fields.style.display = on ? 'block' : 'none';
if (subdomain) subdomain.required = on;
}
cb?.addEventListener('change', toggle);
toggle();
</script>
@endpush
@endsection