Quelltext des aktuellen Standes
<?php
/**
* Gemeinsame Modals des Lager-Moduls (Übersicht & Buchungshistorie).
* Design 1:1 wie die Ticket-Modals (Overlay + .modal-card, .open-Toggle).
*
* Erwartet im Scope: $this (View) und $categories (für das Artikel-Formular).
*/
$skCategories = $categories ?? [];
?>
<!-- ── Buchungs-Modal (Ein-/Ausbuchen) ─────────────────── -->
<div class="modal-overlay sk-modal" id="bookModal">
<div class="modal-card">
<div class="modal-header">
<h2 id="modalTitle"><?= $this->te('Einbuchen') ?></h2>
<button aria-label="<?= $this->te('Schließen') ?>" type="button" class="modal-close" onclick="skCloseBook()">×</button>
</div>
<form method="POST" id="bookForm">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<div class="modal-body">
<div class="form-group">
<label for="modalQty"><?= $this->te('Anzahl') ?></label>
<input type="number" id="modalQty" name="quantity" min="1" value="1" required class="form-control">
</div>
<div class="form-group">
<label for="modalReason" id="modalReasonLabel"><?= $this->te('Bemerkung (optional)') ?></label>
<input type="text" id="modalReason" name="reason" class="form-control"
placeholder="<?= $this->te('z. B. Lieferung, Empfänger…') ?>" maxlength="255">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="skCloseBook()"><?= $this->te('Abbrechen') ?></button>
<button type="submit" class="btn btn-primary" id="modalSubmitBtn"><?= $this->te('Buchen') ?></button>
</div>
</form>
</div>
</div>
<!-- ── Artikel-Modal (Anlegen & Bearbeiten) ────────────── -->
<div class="modal-overlay sk-modal" id="articleModal">
<div class="modal-card">
<div class="modal-header">
<h2 id="am-title"><?= $this->te('Neuer Artikel') ?></h2>
<button aria-label="<?= $this->te('Schließen') ?>" type="button" class="modal-close" onclick="skCloseArticle()">×</button>
</div>
<form method="POST" id="am-form" action="/stock">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<div class="modal-body">
<div class="form-group">
<label for="am-category"><?= $this->te('Kategorie') ?> <span class="text-danger">*</span></label>
<select id="am-category" name="category_id" class="form-control" required>
<option value=""><?= $this->te('— bitte wählen —') ?></option>
<?php foreach ($skCategories as $cat): ?>
<option value="<?= (int)$cat['id'] ?>"><?= $this->e($cat['icon']) ?> <?= $this->e($cat['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="am-name"><?= $this->te('Bezeichnung') ?> <span class="text-danger">*</span></label>
<input type="text" id="am-name" name="name" class="form-control" required maxlength="255"
placeholder='z. B. Dell Monitor 24"'>
</div>
<div class="form-group">
<label for="am-model"><?= $this->te('Modell / Artikelnummer') ?></label>
<input type="text" id="am-model" name="model" class="form-control" maxlength="255"
placeholder="z. B. P2422H">
</div>
<div class="form-group">
<label for="am-description"><?= $this->te('Beschreibung') ?></label>
<textarea id="am-description" name="description" class="form-control" rows="2"
placeholder="<?= $this->te('Optionale Zusatzinfos') ?>"></textarea>
</div>
<div class="sk-modal-row2">
<div class="form-group" id="am-qty-group">
<label for="am-quantity"><?= $this->te('Anfangsbestand') ?></label>
<input type="number" id="am-quantity" name="quantity" class="form-control" min="0" value="0">
<span class="form-hint"><?= $this->te('Wird als erste Einbuchung gespeichert.') ?></span>
</div>
<div class="form-group">
<label for="am-min"><?= $this->te('Mindestbestand') ?></label>
<input type="number" id="am-min" name="min_quantity" class="form-control" min="0" value="0">
<span class="form-hint"><?= $this->te('Markiert den Artikel gelb bei Unterschreitung.') ?></span>
</div>
</div>
<div class="form-group">
<label for="am-location"><?= $this->te('Lagerort') ?></label>
<input type="text" id="am-location" name="location" class="form-control" maxlength="255"
placeholder="<?= $this->te('z. B. Regal 3, IT-Lager Raum 1.12') ?>">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="skCloseArticle()"><?= $this->te('Abbrechen') ?></button>
<button type="submit" class="btn btn-primary" id="am-submit"><?= $this->te('Anlegen') ?></button>
</div>
</form>
</div>
</div>
<style>
/* ── Lager-Modals: Basis kommt aus app.css (.modal-overlay/.modal-card),
hier nur Zustands-Toggle und Lager-spezifische Maße ── */
.sk-modal { display:none; z-index:1055; }
.sk-modal.open { display:flex; }
.sk-modal .modal-card { max-width:480px; }
#bookModal .modal-card { max-width:420px; }
.sk-modal .form-group:last-child { margin-bottom:0; }
.sk-modal textarea.form-control { resize:vertical; min-height:60px; }
.sk-modal-row2 { display:grid; grid-template-columns:1fr 1fr; gap:1rem; }
@media (max-width:480px) { .sk-modal-row2 { grid-template-columns:1fr; } .sk-modal { padding:1rem; } }
</style>
<script>
/* ── Buchungs-Modal ─────────────────────────────────── */
function openModal(type, id, name, maxQty) {
var form = document.getElementById('bookForm');
var title = document.getElementById('modalTitle');
var lbl = document.getElementById('modalReasonLabel');
var submitBtn = document.getElementById('modalSubmitBtn');
var qtyInput = document.getElementById('modalQty');
form.action = '/stock/' + id + '/' + (type === 'in' ? 'book-in' : 'book-out');
title.textContent = (type === 'in' ? '+ Einbuchen' : '− Ausbuchen') + ': ' + name;
lbl.textContent = type === 'out' ? 'Empfänger / Grund (empfohlen)' : 'Bemerkung (optional)';
submitBtn.textContent = type === 'in' ? '+ Einbuchen' : '− Ausbuchen';
submitBtn.style.background = type === 'in' ? '#059669' : '#dc2626';
submitBtn.style.borderColor = type === 'in' ? '#059669' : '#dc2626';
qtyInput.value = 1;
if (type === 'out' && maxQty !== undefined) qtyInput.max = maxQty;
else qtyInput.removeAttribute('max');
document.getElementById('modalReason').value = '';
document.getElementById('bookModal').classList.add('open');
setTimeout(function () { qtyInput.focus(); }, 50);
}
function skCloseBook() { document.getElementById('bookModal').classList.remove('open'); }
/* ── Artikel-Modal (Anlegen / Bearbeiten) ───────────── */
function skOpenArticle(el) {
var modal = document.getElementById('articleModal');
var form = document.getElementById('am-form');
var title = document.getElementById('am-title');
var submit = document.getElementById('am-submit');
var qtyGrp = document.getElementById('am-qty-group');
if (el && el.dataset && el.dataset.id) {
var d = el.dataset;
title.textContent = 'Artikel bearbeiten';
submit.textContent = 'Speichern';
form.action = '/stock/' + d.id + '/update';
document.getElementById('am-category').value = d.category || '';
document.getElementById('am-name').value = d.name || '';
document.getElementById('am-model').value = d.model || '';
document.getElementById('am-description').value = d.desc || '';
document.getElementById('am-min').value = d.min || '0';
document.getElementById('am-location').value = d.location || '';
qtyGrp.style.display = 'none'; // Bestand wird über Buchungen geändert
} else {
title.textContent = 'Neuer Artikel';
submit.textContent = 'Anlegen';
form.action = '/stock';
form.reset();
document.getElementById('am-quantity').value = '0';
document.getElementById('am-min').value = '0';
qtyGrp.style.display = '';
}
modal.classList.add('open');
setTimeout(function () { document.getElementById('am-category').focus(); }, 50);
}
function skCloseArticle() { document.getElementById('articleModal').classList.remove('open'); }
/* ── Schließen per Overlay-Klick & Escape ───────────── */
document.addEventListener('click', function (e) {
if (e.target.classList && e.target.classList.contains('sk-modal')) {
e.target.classList.remove('open');
}
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
var open = document.querySelectorAll('.sk-modal.open');
for (var i = 0; i < open.length; i++) open[i].classList.remove('open');
}
});
</script>