Quelltext des aktuellen Standes
<?php
$this->layout('layouts/main', [
'page_title' => $page_title,
'current_route' => $current_route,
'full_width' => true,
]);
?>
<style>
a { text-decoration: none; }
a:hover { text-decoration: none; }
.ldc-wrap { padding: 0; }
/* Form wrapper */
.ldc-layout {
display: grid;
grid-template-columns: 1fr 280px;
gap: 1.5rem;
align-items: start;
}
/* Cards */
.ldc-card {
background: var(--color-bg);
border: 1px solid var(--gray-200);
border-radius: 0.25rem;
overflow: hidden;
margin-bottom: 1.25rem;
}
.ldc-card:last-child { margin-bottom: 0; }
/* Karte mit durchsuchbarem Dropdown darf nicht clippen, sonst verschwindet
die aufgeklappte Liste hinter dem Kartenrand. */
.ldc-card--dropdown { overflow: visible; }
.ldc-card--dropdown .ldc-section-head { border-radius: 0.25rem 0.25rem 0 0; }
.ldc-section-head {
display: flex; align-items: center; gap: 0.5rem;
font-size: 0.82rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.06em; color: var(--gray-500);
padding: 0.9rem 1.25rem; border-bottom: 1px solid var(--gray-100);
background: var(--gray-50);
}
.ldc-section-head svg { color: var(--amber-600); flex-shrink: 0; }
.ldc-section-body { padding: 1.25rem; }
/* Fields */
.ldc-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; }
.ldc-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin-bottom: 1rem; }
.ldc-field { margin-bottom: 1rem; }
.ldc-field:last-child { margin-bottom: 0; }
.ldc-field label { display: block; font-size: 0.82rem; font-weight: 600; color: var(--gray-700); margin-bottom: 0.35rem; }
.ldc-req { color: var(--red-500); }
.ldc-field input[type=text],
.ldc-field input[type=date],
.ldc-field textarea,
.ldc-field select {
width: 100%; padding: 0.55rem 0.75rem;
border: 1px solid var(--gray-300); border-radius: 0.25rem;
font-size: 0.88rem; outline: none; font-family: inherit;
transition: border-color 0.15s, box-shadow 0.15s;
background: var(--color-bg);
}
.ldc-field input:focus,
.ldc-field textarea:focus,
.ldc-field select:focus {
border-color: var(--amber-600);
box-shadow: 0 0 0 3px rgba(217,119,6,0.12);
}
.ldc-field textarea { resize: vertical; min-height: 100px; }
.ldc-hint { font-size: 0.75rem; color: var(--gray-400); margin-top: 0.3rem; line-height: 1.4; }
/* Image drop zone */
.ldc-img-drop {
border: 2px dashed var(--gray-300); border-radius: 0.25rem; padding: 1.75rem;
text-align: center; cursor: pointer; transition: border-color 0.15s, background 0.15s;
background: var(--gray-50);
}
.ldc-img-drop:hover,
.ldc-img-drop.dragover { border-color: var(--amber-600); background: var(--amber-50); }
.ldc-img-drop-icon { display: block; margin: 0 auto 0.5rem; opacity: 0.4; }
.ldc-img-drop-text { font-size: 0.82rem; color: var(--gray-400); }
.ldc-img-drop-text strong { color: var(--amber-600); }
.ldc-img-preview { display: none; max-width: 180px; max-height: 140px; border-radius: 0.25rem; margin: 0 auto 0.75rem; }
.ldc-img-preview.visible { display: block; }
/* Accessories */
.ldc-acc-list { display: flex; flex-direction: column; gap: 0.5rem; }
.ldc-acc-row { display: flex; gap: 0.5rem; align-items: center; }
.ldc-acc-row input[type=text] {
flex: 1; padding: 0.5rem 0.75rem; border: 1px solid var(--gray-300);
border-radius: 0.25rem; font-size: 0.88rem; outline: none; font-family: inherit;
background: var(--color-bg); box-sizing: border-box;
}
.ldc-acc-row input[type=text]:focus { border-color: var(--amber-600); box-shadow: 0 0 0 2px rgba(217,119,6,0.12); }
.ldc-acc-row input.desc { flex: 0.6; color: var(--gray-500); font-size: 0.82rem; }
.ldc-acc-handle { color: var(--gray-300); cursor: grab; flex-shrink: 0; }
.ldc-acc-remove {
background: none; border: none; color: var(--gray-300); cursor: pointer;
padding: 0.25rem; border-radius: 0.25rem; transition: color 0.15s, background 0.15s;
flex-shrink: 0;
}
.ldc-acc-remove:hover { color: var(--red-500); background: var(--red-100); }
.ldc-add-acc {
display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 0.75rem;
font-size: 0.82rem; font-weight: 600; color: var(--amber-600);
background: none; border: none; cursor: pointer; padding: 0;
}
.ldc-add-acc:hover { color: var(--amber-700); }
.ldc-acc-placeholder {
font-size: 0.82rem; color: var(--gray-400); text-align: center;
padding: 1rem; border: 1.5px dashed var(--gray-200); border-radius: 0.25rem;
}
/* Submit bar */
.ldc-submit-bar {
display: flex; gap: 0.75rem; justify-content: flex-end; align-items: center;
padding: 1rem 1.25rem; background: var(--gray-50); border-top: 1px solid var(--gray-200);
border-radius: 0 0 0.25rem 0.25rem;
}
/* Sidebar help card */
.ldc-help-card {
background: var(--color-bg); border: 1px solid var(--gray-200); border-radius: 0.25rem; overflow: hidden;
position: sticky; top: 1rem;
}
.ldc-help-head {
display: flex; align-items: center; gap: 0.5rem;
font-size: 0.82rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.06em; color: var(--gray-500);
padding: 0.9rem 1.25rem; border-bottom: 1px solid var(--gray-100);
background: var(--gray-50);
}
.ldc-help-body { padding: 1.25rem; }
.ldc-help-item { margin-bottom: 1rem; }
.ldc-help-item:last-child { margin-bottom: 0; }
.ldc-help-label { font-size: 0.78rem; font-weight: 700; color: var(--gray-700); margin-bottom: 0.25rem; }
.ldc-help-text { font-size: 0.78rem; color: var(--gray-500); line-height: 1.5; }
/* Auswahlfelder nutzen die zentrale durchsuchbare Dropdown-Komponente (app.js/app.css). */
@media (max-width: 768px) {
.ldc-body { padding: 1.25rem 1rem 2rem; }
.ldc-layout { grid-template-columns: 1fr; }
.ldc-row-2, .ldc-row-3 { grid-template-columns: 1fr; }
.ldc-help-card { position: static; }
}
</style>
<div class="ldc-wrap">
<!-- Content Header -->
<div class="content-header">
<div>
<h1><?= $this->te('Neues Leihgerät anlegen') ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item"><a href="/loans"><?= $this->te('Leihgeräte') ?></a></li>
<li class="breadcrumb-item"><a href="/loans/devices"><?= $this->te('Geräte') ?></a></li>
<li class="breadcrumb-item active"><?= $this->te('Neues Gerät') ?></li>
</ol>
</div>
</div>
<!-- Body -->
<div class="page-body">
<form method="POST" action="/loans/devices" enctype="multipart/form-data" id="deviceForm">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<div class="ldc-layout">
<!-- Main column -->
<div class="ldc-main">
<!-- Stammdaten -->
<div class="ldc-card ldc-card--dropdown">
<h2 class="ldc-section-head">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
<?= $this->te('Stammdaten') ?>
</h2>
<div class="ldc-section-body">
<div class="ldc-row-2">
<div class="ldc-field">
<label for="title"><?= $this->te('Bezeichnung') ?> <span class="ldc-req">*</span></label>
<input type="text" id="title" name="title" required maxlength="255" placeholder="<?= $this->te('z. B. MacBook Pro 14"') ?>">
</div>
<div class="ldc-field">
<label for="type"><?= $this->te('Typ / Kategorie') ?> <span class="ldc-req">*</span></label>
<input type="text" id="type" name="type" required list="type-options" placeholder="<?= $this->te('z. B. Laptop, Kamera, Beamer…') ?>">
<datalist id="type-options">
<?php foreach ($types as $t): ?>
<option value="<?= $this->e($t['type']) ?>">
<?php endforeach; ?>
</datalist>
</div>
</div>
<div class="ldc-row-3">
<div class="ldc-field">
<label for="serial_number"><?= $this->te('Seriennummer') ?></label>
<input type="text" id="serial_number" name="serial_number" placeholder="S/N">
</div>
<div class="ldc-field">
<label for="inventory_number"><?= $this->te('Inventarnummer') ?></label>
<input type="text" id="inventory_number" name="inventory_number" placeholder="<?= $this->te('Inv-Nr.') ?>">
</div>
</div>
<div class="ldc-row-3">
<div class="ldc-field">
<label for="storage_location"><?= $this->te('Lagerort') ?></label>
<input type="text" id="storage_location" name="storage_location" placeholder="<?= $this->te('z. B. Schrank IT-1, Fach 3') ?>">
</div>
<div class="ldc-field">
<label for="purchase_price"><?= $this->te('Anschaffungswert ({cur})', ['cur' => $this->currency()]) ?></label>
<input type="text" id="purchase_price" name="purchase_price" inputmode="decimal" placeholder="z. B. 899,00">
</div>
<div class="ldc-field">
<label for="purchase_date"><?= $this->te('Kaufdatum') ?></label>
<input type="date" id="purchase_date" name="purchase_date">
</div>
</div>
<?php if (!empty($linkable_assets ?? [])): ?>
<div class="ldc-field">
<label for="asset_id"><?= $this->te('Mit Inventar-Asset verknüpfen (optional)') ?></label>
<select id="asset_id" name="asset_id" class="form-control">
<option value=""><?= $this->te('– nicht verknüpft –') ?></option>
<?php foreach ($linkable_assets as $a):
$label = $a['inventory_number'];
$extra = trim(($a['manufacturer_name'] ?? '') . ' ' . ($a['type_name'] ?? ''));
if ($extra !== '') { $label .= ' – ' . $extra; }
if (!empty($a['serial_number'])) { $label .= ' (S/N: ' . $a['serial_number'] . ')'; }
?>
<option value="<?= (int)$a['id'] ?>"
data-inv="<?= $this->e($a['inventory_number'] ?? '') ?>"
data-sn="<?= $this->e($a['serial_number'] ?? '') ?>"><?= $this->e($label) ?></option>
<?php endforeach; ?>
</select>
<p class="ldc-hint" style="margin-top:0.25rem;"><?= $this->te('Verknüpft das Leihgerät mit einem bestehenden Asset und markiert es dort als Leihgerät. Beim Verknüpfen werden Inventar- und Seriennummer automatisch übernommen.') ?></p>
</div>
<?php endif; ?>
</div>
</div>
<!-- Bild -->
<div class="ldc-card">
<h2 class="ldc-section-head">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>
<?= $this->te('Bild (optional)') ?>
</h2>
<div class="ldc-section-body">
<div class="ldc-img-drop" id="imgDrop" onclick="document.getElementById('imageInput').click()">
<img class="ldc-img-preview" id="imgPreview" src="" alt="<?= $this->te('Vorschau') ?>">
<svg class="ldc-img-drop-icon" id="imgPlaceholder" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>
<p class="ldc-img-drop-text" id="imgText"><?= $this->t('<strong>Klicken</strong> oder Bild hier ablegen') ?></p>
<p class="ldc-hint" style="margin-top:0.25rem;"><?= $this->te('JPEG, PNG, GIF, WebP') ?></p>
</div>
<input type="file" id="imageInput" name="image" accept="image/*" style="display:none">
</div>
</div>
<!-- Beschreibung & Notizen -->
<div class="ldc-card">
<h2 class="ldc-section-head">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
<?= $this->te('Beschreibung & Notizen') ?>
</h2>
<div class="ldc-section-body">
<div class="ldc-field">
<label for="description"><?= $this->te('Beschreibung') ?></label>
<textarea id="description" name="description" rows="3" placeholder="<?= $this->te('Kurze Beschreibung des Geräts, Ausstattungsmerkmale…') ?>"></textarea>
</div>
<div class="ldc-field">
<label for="notes"><?= $this->te('Interne Notizen') ?></label>
<textarea id="notes" name="notes" rows="3" placeholder="<?= $this->te('Interne Hinweise, Besonderheiten, Wartungsnotizen…') ?>"></textarea>
<p class="ldc-hint"><?= $this->te('Nur für Mitarbeitende sichtbar.') ?></p>
</div>
<div class="ldc-field" style="margin-top:0.75rem;">
<label style="display:flex;align-items:center;gap:0.5rem;font-weight:500;cursor:pointer;">
<input type="checkbox" name="wp_requestable" value="1" checked style="width:auto;margin:0;">
<?= $this->te('Im Self-Service anforderbar') ?>
</label>
<p class="ldc-hint"><?= $this->te('Wenn aktiv, können Mitarbeitende dieses Gerät selbst reservieren – im Self-Service-Portal und über das WordPress-Plugin.') ?></p>
</div>
</div>
</div>
<!-- Zubehör -->
<div class="ldc-card">
<h2 class="ldc-section-head">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.24 12.24a6 6 0 00-8.49-8.49L5 10.5V19h8.5z"/><line x1="16" y1="8" x2="2" y2="22"/></svg>
<?= $this->te('Zubehör') ?>
</h2>
<div class="ldc-section-body">
<div class="ldc-acc-list" id="accList">
<p class="ldc-acc-placeholder" id="accPlaceholder"><?= $this->te('Noch kein Zubehör hinzugefügt.') ?></p>
</div>
<button type="button" class="ldc-add-acc" onclick="addAccessory()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
<?= $this->te('Zubehör hinzufügen') ?>
</button>
<p class="ldc-hint"><?= $this->te('z. B. Netzteil, Tasche, HDMI-Adapter, Maus – wird bei der Ausgabe zum Abhaken angezeigt.') ?></p>
</div>
<div class="ldc-submit-bar">
<a href="/loans/devices" class="btn btn-default"><?= $this->te('Abbrechen') ?></a>
<button type="submit" class="btn btn-primary">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="margin-right:0.3rem;"><path d="M20 6L9 17l-5-5"/></svg>
<?= $this->te('Gerät anlegen') ?>
</button>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="ldc-aside">
<div class="ldc-help-card">
<div class="ldc-help-head">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<?= $this->te('Hinweise') ?>
</div>
<div class="ldc-help-body">
<div class="ldc-help-item">
<div class="ldc-help-label"><?= $this->te('Bezeichnung & Typ') ?></div>
<div class="ldc-help-text"><?= $this->te('Wählen Sie eine eindeutige Bezeichnung. Der Typ (z. B. „Laptop", „Beamer") wird für Filter und Kalenderanzeige genutzt.') ?></div>
</div>
<div class="ldc-help-item">
<div class="ldc-help-label"><?= $this->te('Seriennummer / Inventarnummer') ?></div>
<div class="ldc-help-text"><?= $this->te('Tragen Sie Seriennummer und/oder Ihre interne Inventarnummer ein – hilfreich für die Zuordnung im Inventar.') ?></div>
</div>
<div class="ldc-help-item">
<div class="ldc-help-label"><?= $this->te('Zubehör') ?></div>
<div class="ldc-help-text"><?= $this->t('Jedes Zubehörteil erscheint beim Ausgeben als Checkbox. Bei der Rücknahme müssen <strong>alle</strong> mitgegebenen Teile abgehakt werden.') ?></div>
</div>
<div class="ldc-help-item">
<div class="ldc-help-label"><?= $this->te('Bild') ?></div>
<div class="ldc-help-text"><?= $this->te('Wird im Kalender, der Geräteliste und im Ausgabeformular angezeigt. JPEG oder PNG empfohlen.') ?></div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<script>
// Image preview
(function() {
var input = document.getElementById('imageInput');
var preview = document.getElementById('imgPreview');
var icon = document.getElementById('imgPlaceholder');
var text = document.getElementById('imgText');
var drop = document.getElementById('imgDrop');
function showPreview(file) {
var reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.classList.add('visible');
icon.style.display = 'none';
text.textContent = file.name;
};
reader.readAsDataURL(file);
}
input.addEventListener('change', function() { if (this.files[0]) showPreview(this.files[0]); });
drop.addEventListener('dragover', function(e) { e.preventDefault(); drop.classList.add('dragover'); });
drop.addEventListener('dragleave', function() { drop.classList.remove('dragover'); });
drop.addEventListener('drop', function(e) {
e.preventDefault(); drop.classList.remove('dragover');
if (e.dataTransfer.files[0]) { input.files = e.dataTransfer.files; showPreview(e.dataTransfer.files[0]); }
});
})();
// Accessories
function addAccessory(name, desc) {
var placeholder = document.getElementById('accPlaceholder');
if (placeholder) placeholder.remove();
var list = document.getElementById('accList');
var row = document.createElement('div');
row.className = 'ldc-acc-row';
row.innerHTML =
'<input type="text" name="accessory_name[]" placeholder="Bezeichnung (z. B. Netzteil)" value="' + esc(name || '') + '" required>' +
'<input type="text" name="accessory_desc[]" class="desc" placeholder="Beschreibung (optional)" value="' + esc(desc || '') + '">' +
'<button type="button" class="ldc-acc-remove" title="Entfernen" onclick="removeRow(this)">✕</button>';
list.appendChild(row);
row.querySelector('input').focus();
}
function removeRow(btn) {
btn.closest('.ldc-acc-row').remove();
if (document.querySelectorAll('.ldc-acc-row').length === 0) {
var list = document.getElementById('accList');
var p = document.createElement('p');
p.className = 'ldc-acc-placeholder';
p.id = 'accPlaceholder';
p.textContent = 'Noch kein Zubehör hinzugefügt.';
list.appendChild(p);
}
}
function esc(s) {
return String(s).replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
}
// Beim Verknüpfen Inventar- und Seriennummer aus dem Asset übernehmen.
(function () {
var sel = document.getElementById('asset_id');
if (!sel) return;
sel.addEventListener('change', function () {
var opt = sel.options[sel.selectedIndex];
if (!opt || !opt.value) return; // "nicht verknüpft" → bestehende Werte behalten
var inv = opt.getAttribute('data-inv') || '';
var sn = opt.getAttribute('data-sn') || '';
var invField = document.getElementById('inventory_number');
var snField = document.getElementById('serial_number');
if (invField && inv !== '') { invField.value = inv; }
if (snField && sn !== '') { snField.value = sn; }
});
})();
</script>