OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

_modals.php

10 KB
<?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()">&times;</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()">&times;</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>