OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

types_index.php

17 KB
<?php $this->layout('layouts/admin', [
    'page_title'         => 'Modelle / Typen',
    'current_admin_page' => 'assets',
    'breadcrumbs'        => [['label' => 'Modelle / Typen']],
]); ?>

<!-- Neues Modell -->
<div class="card card-outline card-primary mb-3">
    <div class="card-header">
        <h3 class="card-title"><i class="fas fa-plus"></i> <?= $this->te('Neues Modell anlegen') ?></h3>
        <p class="text-muted small mb-0"><?= $this->te('Modelle werden einem Hersteller zugeordnet und k&ouml;nnen einer Kategorie zugewiesen werden.') ?></p>
    </div>
    <div class="card-body">
            <form action="/admin/assets/types" method="POST" class="form">
                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                <div class="settings-grid" style="grid-template-columns:1fr 1fr 1fr;">
                    <div class="form-group">
                        <label><?= $this->te('Name *') ?></label>
                        <input type="text" name="name" class="form-control" placeholder="<?= $this->te('z.B. Latitude 3530') ?>" required maxlength="255">
                    </div>
                    <div class="form-group">
                        <label for="f-621c-manufacturer_id"><?= $this->te('Hersteller *') ?></label>
                        <select id="f-621c-manufacturer_id" name="manufacturer_id" class="form-control" required>
                            <option value=""><?= $this->te('— Hersteller wählen —') ?></option>
                            <?php foreach ($manufacturers as $m): ?>
                                <option value="<?= $m['id'] ?>"><?= $this->e($m['name']) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="f-621c-category_id"><?= $this->te('Kategorie (optional)') ?></label>
                        <select id="f-621c-category_id" name="category_id" class="form-control">
                            <option value=""><?= $this->te('— Alle Kategorien —') ?></option>
                            <?php foreach ($categories as $cat): ?>
                                <option value="<?= $cat['id'] ?>"><?= $this->e($cat['name']) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="form-group" style="grid-column:1/-1;">
                        <label><?= $this->te('Beschreibung') ?></label>
                        <input type="text" name="description" class="form-control" placeholder="<?= $this->te('Kurze Beschreibung') ?>" maxlength="500">
                    </div>
                    <div class="form-group" style="grid-column:1/-1;">
                        <label><?= $this->te('Technische Daten') ?></label>
                        <textarea name="specs" class="form-control" rows="3" placeholder="<?= $this->te('CPU, RAM, Display, …') ?>"></textarea>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-primary"><?= $this->te('Modell anlegen') ?></button>
                    </div>
                </div>
            </form>
        </div>
</div>

<!-- Liste -->
<div class="card card-outline card-primary mb-3">
    <div class="card-header">
        <h3 class="card-title"><i class="fas fa-list"></i> <?= $this->t('Modelle') ?> (<?= count($types) ?>)</h3>
        <p class="text-muted small mb-0"><?= $this->te('Alle konfigurierten Modelle / Typen. Bilder k&ouml;nnen direkt hochgeladen werden.') ?></p>
    </div>
    <div class="card-body table-responsive p-0">
            <?php if (empty($types)): ?>
                <div style="padding:2rem;text-align:center;color:var(--gray-400);"><?= $this->te('Noch keine Modelle vorhanden.') ?></div>
            <?php else: ?>
            <div class="admin-table-wrap">
                <table style="width:100%;border-collapse:collapse;font-size:0.875rem;min-width:400px;">
                    <thead>
                        <tr>
                            <?php foreach (['Name', 'Hersteller', 'Kategorie', 'Bilder', 'Assets', ''] as $th): ?>
                            <th scope="col" style="padding:0.65rem 1rem;text-align:left;font-size:0.72rem;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:var(--gray-500);background:var(--gray-50);border-bottom:2px solid var(--gray-200);"><?= $th ?></th>
                            <?php endforeach; ?>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($types as $t):
                            $imgs = $type_images[(int)$t['id']] ?? [];
                        ?>
                        <tr>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:middle;">
                                <strong><?= $this->e($t['name']) ?></strong>
                                <?php if ($t['description']): ?>
                                    <small style="display:block;font-size:0.75rem;color:var(--gray-400);"><?= $this->e(mb_substr($t['description'], 0, 80)) ?></small>
                                <?php endif; ?>
                            </td>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:middle;font-size:0.85rem;"><?= $this->e($t['manufacturer_name']) ?></td>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:middle;font-size:0.82rem;color:var(--gray-500);"><?= $t['category_name'] ? $this->e($t['category_name']) : '—' ?></td>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:top;padding-top:0.85rem;">
                                <!-- Vorhandene Bilder -->
                                <div class="ti-thumbs" id="thumbs-<?= $t['id'] ?>">
                                    <?php foreach ($imgs as $img): ?>
                                    <div class="ti-thumb" id="img-<?= $img['id'] ?>">
                                        <img src="/uploads/asset-types/<?= $this->e($img['filename']) ?>" alt=""
                                             class="ti-thumb-img" onclick="openLightbox(this.src)">
                                        <button type="button" class="ti-del-btn"
                                                onclick="deleteImage(<?= $img['id'] ?>, <?= $t['id'] ?>)"
                                                title="<?= $this->te('Bild löschen') ?>">&#215;</button>
                                    </div>
                                    <?php endforeach; ?>
                                </div>
                                <!-- Upload-Button -->
                                <label class="ti-upload-label">
                                    <input type="file" accept="image/jpeg,image/png,image/gif,image/webp"
                                           style="display:none;" onchange="uploadImage(this, <?= $t['id'] ?>)">
                                    <?= $this->te('+ Bild') ?>
                                </label>
                            </td>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:middle;font-size:0.85rem;"><?= (int)$t['asset_count'] ?></td>
                            <td style="padding:0.65rem 1rem;border-bottom:1px solid var(--gray-100);vertical-align:middle;">
                                <div style="display:flex;gap:0.4rem;">
                                    <a href="/admin/assets/types/<?= $t['id'] ?>/fields" class="btn btn-sm btn-secondary" title="<?= $this->te('Zusatzfelder verwalten') ?>"><?= $this->te('🔧 Felder') ?></a>
                                    <button onclick="toggleTypeEdit(<?= $t['id'] ?>)" class="btn btn-sm btn-secondary"><?= $this->te('Bearbeiten') ?></button>
                                    <?php if ((int)$t['asset_count'] === 0): ?>
                                    <form method="POST" action="/admin/assets/types/<?= $t['id'] ?>/delete" style="margin:0;"
                                          onsubmit="return confirm('Modell <?= addslashes($this->e($t['name'])) ?> löschen? Alle Bilder werden ebenfalls gelöscht.')">
                                        <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                        <button aria-label="<?= $this->te('Löschen') ?>" type="submit" class="btn btn-sm btn-danger-outline">&#215;</button>
                                    </form>
                                    <?php endif; ?>
                                </div>
                            </td>
                        </tr>
                        <!-- Bearbeiten-Zeile -->
                        <tr id="typeEdit-<?= $t['id'] ?>" style="display:none;background:var(--sky-50);">
                            <td colspan="6" style="padding:1rem 1.25rem;border-bottom:1px solid var(--gray-200);">
                                <form method="POST" action="/admin/assets/types/<?= $t['id'] ?>" style="display:flex;gap:0.75rem;flex-wrap:wrap;align-items:flex-end;">
                                    <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                    <input type="hidden" name="_method" value="PUT">
                                    <div style="display:flex;flex-direction:column;gap:0.25rem;flex:1;min-width:140px;">
                                        <label style="font-size:0.75rem;font-weight:700;color:var(--gray-700);"><?= $this->te('Name') ?></label>
                                        <input type="text" name="name" value="<?= $this->e($t['name']) ?>" class="form-control" required>
                                    </div>
                                    <div style="display:flex;flex-direction:column;gap:0.25rem;min-width:150px;">
                                        <label style="font-size:0.75rem;font-weight:700;color:var(--gray-700);"><?= $this->te('Hersteller') ?></label>
                                        <select name="manufacturer_id" class="form-control" required>
                                            <?php foreach ($manufacturers as $m): ?>
                                                <option value="<?= $m['id'] ?>" <?= (int)$t['manufacturer_id'] === (int)$m['id'] ? 'selected' : '' ?>><?= $this->e($m['name']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div style="display:flex;flex-direction:column;gap:0.25rem;min-width:150px;">
                                        <label style="font-size:0.75rem;font-weight:700;color:var(--gray-700);"><?= $this->te('Kategorie') ?></label>
                                        <select name="category_id" class="form-control">
                                            <option value=""><?= $this->te('— Alle —') ?></option>
                                            <?php foreach ($categories as $cat): ?>
                                                <option value="<?= $cat['id'] ?>" <?= (int)$t['category_id'] === (int)$cat['id'] ? 'selected' : '' ?>><?= $this->e($cat['name']) ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </div>
                                    <div style="display:flex;flex-direction:column;gap:0.25rem;flex:2;min-width:200px;">
                                        <label style="font-size:0.75rem;font-weight:700;color:var(--gray-700);"><?= $this->te('Beschreibung') ?></label>
                                        <input type="text" name="description" value="<?= $this->e($t['description'] ?? '') ?>" class="form-control">
                                    </div>
                                    <div style="display:flex;flex-direction:column;gap:0.25rem;width:100%;">
                                        <label style="font-size:0.75rem;font-weight:700;color:var(--gray-700);"><?= $this->te('Technische Daten') ?></label>
                                        <textarea name="specs" class="form-control" rows="2"><?= $this->e($t['specs'] ?? '') ?></textarea>
                                    </div>
                                    <button type="submit" class="btn btn-primary"><?= $this->te('&#128190; Speichern') ?></button>
                                    <button type="button" onclick="toggleTypeEdit(<?= $t['id'] ?>)" class="btn btn-secondary"><?= $this->te('Abbrechen') ?></button>
                                </form>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
            <?php endif; ?>
        </div>
    </div>
</div>

<div class="card card-outline card-primary mb-3">
    <div class="card-body">
        <div style="display:flex;gap:0.75rem;flex-wrap:wrap;">
            <a href="/admin/assets/categories" class="btn btn-default"><i class="fas fa-arrow-left"></i> <?= $this->te('Kategorien') ?></a>
            <a href="/admin/assets/manufacturers" class="btn btn-default"><i class="fas fa-industry"></i> <?= $this->te('Hersteller') ?></a>
            <a href="/inventory" class="btn btn-primary"><i class="fas fa-boxes"></i> <?= $this->te('Zum Inventar') ?></a>
        </div>
    </div>
</div>

<style>
.ti-thumbs { display:flex; flex-wrap:wrap; gap:0.35rem; margin-bottom:0.4rem; }
.ti-thumb  { position:relative; display:inline-block; }
.ti-thumb-img { width:52px; height:52px; object-fit:cover; border-radius:0.35rem; border:1px solid var(--gray-200); display:block; cursor:pointer; }
.ti-thumb-img:hover { opacity:0.82; }
.ti-del-btn { position:absolute; top:-5px; right:-5px; width:16px; height:16px; border-radius:50%;
    background:var(--red-600); color:var(--color-on-accent); border:none; cursor:pointer; font-size:0.65rem; line-height:16px;
    text-align:center; padding:0; }
.ti-del-btn:hover { background:var(--red-700); }
.ti-upload-label { display:inline-flex; align-items:center; gap:0.2rem; padding:0.22rem 0.55rem;
    border:1.5px dashed var(--gray-300); border-radius:0.4rem; font-size:0.75rem; font-weight:600;
    color:var(--gray-500); cursor:pointer; transition:all 0.15s; white-space:nowrap; }
.ti-upload-label:hover { border-color:var(--blue-600); color:var(--blue-600); background:var(--sky-50); }
.ti-upload-label.loading { opacity:0.5; pointer-events:none; }
</style>

<script>
var csrf = document.querySelector('meta[name="csrf-token"]');
function getCsrf() { return csrf ? csrf.content : ''; }

function toggleTypeEdit(id) {
    var row = document.getElementById('typeEdit-' + id);
    row.style.display = row.style.display === 'none' ? '' : 'none';
}

function uploadImage(input, typeId) {
    if (!input.files || !input.files[0]) return;
    var label = input.closest('label');
    label.classList.add('loading');

    var fd = new FormData();
    fd.append('image', input.files[0]);

    fetch('/admin/assets/types/' + typeId + '/images', {
        method: 'POST',
        headers: { 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'XMLHttpRequest' },
        body: fd
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        label.classList.remove('loading');
        input.value = '';
        if (!d.ok) { alert(d.error || 'Fehler beim Hochladen.'); return; }

        var thumbs = document.getElementById('thumbs-' + typeId);
        var div = document.createElement('div');
        div.className = 'ti-thumb';
        div.id = 'img-' + d.image_id;
        div.innerHTML =
            '<img src="' + d.url + '" alt="" class="ti-thumb-img" onclick="openLightbox(this.src)">' +
            '<button type="button" class="ti-del-btn" onclick="deleteImage(' + d.image_id + ',' + typeId + ')" title="Bild l\u00f6schen">\u00d7</button>';
        thumbs.appendChild(div);
    })
    .catch(function(e) {
        label.classList.remove('loading');
        input.value = '';
        alert('Netzwerkfehler: ' + e.message);
    });
}

function deleteImage(imageId, typeId) {
    if (!confirm('Bild wirklich l\u00f6schen?')) return;
    fetch('/admin/assets/types/images/' + imageId + '/delete', {
        method: 'POST',
        headers: { 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded' }
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        if (d.ok) {
            var el = document.getElementById('img-' + imageId);
            if (el) el.remove();
        } else {
            alert(d.error || 'Fehler beim L\u00f6schen.');
        }
    })
    .catch(function(e) { alert('Netzwerkfehler: ' + e.message); });
}

function openLightbox(src) {
    var lb = document.getElementById('ti-lightbox');
    if (!lb) {
        lb = document.createElement('div');
        lb.id = 'ti-lightbox';
        lb.style.cssText = 'display:flex;position:fixed;inset:0;background:rgba(0,0,0,0.85);z-index:9999;align-items:center;justify-content:center;cursor:pointer;';
        lb.innerHTML = '<img style="max-width:90vw;max-height:90vh;border-radius:0.5rem;">';
        lb.addEventListener('click', function() { lb.style.display = 'none'; });
        document.body.appendChild(lb);
    }
    lb.querySelector('img').src = src;
    lb.style.display = 'flex';
}
</script>