Files
2026-06-13 10:19:44 +02:00

233 lines
12 KiB
PHP
Executable File
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', 'Neuen Lack hinzufügen NeoNail DB')
@section('content')
<div class="row justify-content-center">
<div class="col-md-8 col-lg-7">
<div class="page-hero mb-4">
<h1><i class="fas fa-plus me-2" style="font-size:1.4rem;vertical-align:middle;opacity:.9;"></i>Neuen Lack hinzufügen</h1>
<p>Der Lack wird zum Katalog hinzugefügt und automatisch in Ihre Sammlung aufgenommen.</p>
</div>
<div class="card">
<div class="card-body p-4">
@if($errors->any())
<div class="alert alert-danger mb-4">
<i class="fas fa-exclamation-circle me-2"></i>
<strong>Bitte korrigieren Sie folgende Fehler:</strong>
<ul class="mb-0 mt-2 ps-3">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="{{ route('user-nail-polishes.store') }}" enctype="multipart/form-data">
@csrf
<div class="row g-3 mb-3">
<div class="col-md-7">
<label for="name" class="form-label">Name des Lackes *</label>
<input type="text" class="form-control @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name') }}"
placeholder="z.B. Classic Red" required>
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-5">
<label for="number" class="form-label">Nummer *</label>
<input type="text" class="form-control @error('number') is-invalid @enderror"
id="number" name="number" value="{{ old('number') }}"
placeholder="z.B. 001" required>
@error('number')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-md-6">
<label for="manufacturer_id" class="form-label">Hersteller auswählen</label>
<select class="form-select @error('manufacturer_id') is-invalid @enderror"
id="manufacturer_id" name="manufacturer_id">
<option value=""> Hersteller wählen </option>
@foreach(\App\Models\Manufacturer::orderBy('name')->get() as $m)
<option value="{{ $m->id }}" {{ old('manufacturer_id') == $m->id ? 'selected' : '' }}>
{{ $m->name }}
</option>
@endforeach
</select>
@error('manufacturer_id')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6">
<label for="new_manufacturer" class="form-label">Oder neuen Hersteller anlegen</label>
<input type="text" class="form-control @error('new_manufacturer') is-invalid @enderror"
id="new_manufacturer" name="new_manufacturer"
value="{{ old('new_manufacturer') }}"
placeholder="Name des neuen Herstellers">
@error('new_manufacturer')<div class="invalid-feedback">{{ $message }}</div>@enderror
<div class="form-text">Wird automatisch angelegt, falls noch nicht vorhanden.</div>
</div>
</div>
{{-- Image Upload --}}
<div class="mb-4">
<label class="form-label">Foto (optional)</label>
{{-- Tab-Umschalter --}}
<div class="d-flex gap-2 mb-3">
<button type="button" id="tab-file" onclick="switchTab('file')"
style="padding:6px 16px; border-radius:50px; font-size:.82rem; font-weight:600; cursor:pointer; transition:.18s;
background:#7c3aed; color:#fff; border:none;">
<i class="fas fa-upload me-1"></i>Datei
</button>
<button type="button" id="tab-url" onclick="switchTab('url')"
style="padding:6px 16px; border-radius:50px; font-size:.82rem; font-weight:600; cursor:pointer; transition:.18s;
background:#f3f4f6; color:#6b7280; border:none;">
<i class="fas fa-link me-1"></i>URL
</button>
</div>
{{-- Panel: Datei --}}
<div id="panel-file">
<div id="drop-zone"
style="border: 2px dashed #e5e7eb; border-radius: 13px; padding: 28px 20px; text-align: center; cursor: pointer; transition: border-color .2s, background .2s;"
onclick="document.getElementById('image').click()"
ondragover="event.preventDefault(); this.style.borderColor='#7c3aed'; this.style.background='#f5f3ff';"
ondragleave="this.style.borderColor='#e5e7eb'; this.style.background='';"
ondrop="handleDrop(event)">
<div id="preview-wrap" class="d-none mb-3">
<img id="preview-img" src="" alt="Vorschau"
style="max-height:160px; border-radius:10px; object-fit:cover;">
</div>
<div id="drop-label">
<i class="fas fa-camera fa-2x mb-2 d-block" style="color:#d1d5db;"></i>
<span style="font-size:.88rem; font-weight:600; color:#6b7280;">Klicken oder Foto aufnehmen</span><br>
<span style="font-size:.76rem; color:#9ca3af;">JPG, PNG, GIF · Max. 10 MB</span>
</div>
</div>
<input type="file" class="d-none @error('image') is-invalid @enderror"
id="image" name="image" accept="image/*">
@error('image')<div class="invalid-feedback d-block mt-1">{{ $message }}</div>@enderror
</div>
{{-- Panel: URL --}}
<div id="panel-url" style="display:none;">
<input type="url" id="image_url" name="image_url"
class="form-control @error('image_url') is-invalid @enderror"
placeholder="https://example.com/bild.jpg"
value="{{ old('image_url') }}"
oninput="previewUrl(this.value)">
@error('image_url')<div class="invalid-feedback">{{ $message }}</div>@enderror
<div class="form-text mb-2">Das Bild wird vom Server heruntergeladen und lokal gespeichert.</div>
<div id="url-preview-wrap" style="display:none; margin-top:10px;">
<img id="url-preview-img" src="" alt="Vorschau"
style="max-height:160px; border-radius:10px; object-fit:cover; border:2px solid #e5e7eb;"
onerror="this.parentElement.style.display='none'">
</div>
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary flex-fill">
<i class="fas fa-plus me-2"></i>Lack erstellen & hinzufügen
</button>
<a href="{{ route('user-nail-polishes.index') }}" class="btn btn-outline-secondary">
Abbrechen
</a>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const mSel = document.getElementById('manufacturer_id');
const mNew = document.getElementById('new_manufacturer');
const img = document.getElementById('image');
const prev = document.getElementById('preview-img');
const wrap = document.getElementById('preview-wrap');
const lbl = document.getElementById('drop-label');
const zone = document.getElementById('drop-zone');
// Hersteller-Toggle
mSel.addEventListener('change', () => {
if (mSel.value) { mNew.value = ''; mNew.disabled = true; }
else mNew.disabled = false;
});
mNew.addEventListener('input', () => {
if (mNew.value.trim()) { mSel.value = ''; mSel.disabled = true; }
else mSel.disabled = false;
});
if (mNew.value.trim()) mSel.disabled = true;
if (mSel.value) mNew.disabled = true;
// Datei-Vorschau
function showFilePreview(file) {
const reader = new FileReader();
reader.onload = e => {
prev.src = e.target.result;
wrap.classList.remove('d-none');
lbl.style.display = 'none';
zone.style.borderColor = '#7c3aed';
zone.style.background = '#f5f3ff';
};
reader.readAsDataURL(file);
}
img.addEventListener('change', function () {
if (this.files && this.files[0]) showFilePreview(this.files[0]);
});
window.handleDrop = function (e) {
e.preventDefault();
zone.style.borderColor = '#e5e7eb';
zone.style.background = '';
if (e.dataTransfer.files.length) {
const dt = new DataTransfer();
dt.items.add(e.dataTransfer.files[0]);
img.files = dt.files;
showFilePreview(e.dataTransfer.files[0]);
}
};
// Tab-Umschalter
window.switchTab = function (tab) {
const isFile = tab === 'file';
document.getElementById('panel-file').style.display = isFile ? '' : 'none';
document.getElementById('panel-url').style.display = isFile ? 'none' : '';
document.getElementById('tab-file').style.background = isFile ? '#7c3aed' : '#f3f4f6';
document.getElementById('tab-file').style.color = isFile ? '#fff' : '#6b7280';
document.getElementById('tab-url').style.background = isFile ? '#f3f4f6' : '#7c3aed';
document.getElementById('tab-url').style.color = isFile ? '#6b7280' : '#fff';
// Felder des inaktiven Tabs leeren
if (isFile) { document.getElementById('image_url').value = ''; document.getElementById('url-preview-wrap').style.display = 'none'; }
else { img.value = ''; wrap.classList.add('d-none'); lbl.style.display = ''; zone.style.borderColor = '#e5e7eb'; zone.style.background = ''; }
};
// URL-Vorschau
let urlTimer;
window.previewUrl = function (val) {
clearTimeout(urlTimer);
const pw = document.getElementById('url-preview-wrap');
const pi = document.getElementById('url-preview-img');
if (!val.trim()) { pw.style.display = 'none'; return; }
urlTimer = setTimeout(() => {
pi.src = val;
pw.style.display = '';
}, 600);
};
// Ggf. URL-Tab vorauswählen bei Validation-Fehler
@if(old('image_url'))
switchTab('url');
previewUrl('{{ old('image_url') }}');
@endif
});
</script>
@endsection