OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

create.php

23 KB
<?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&quot;') ?>">
                                </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 &amp; 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&auml;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&uuml;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&ouml;nnen Mitarbeitende dieses Ger&auml;t selbst reservieren &ndash; im Self-Service-Portal und &uuml;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&ouml;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&ouml;r hinzugef&uuml;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&ouml;r hinzuf&uuml;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&auml;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 &amp; Typ') ?></div>
                                <div class="ldc-help-text"><?= $this->te('W&auml;hlen Sie eine eindeutige Bezeichnung. Der Typ (z. B. „Laptop", „Beamer") wird f&uuml;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&uuml;r die Zuordnung im Inventar.') ?></div>
                            </div>
                            <div class="ldc-help-item">
                                <div class="ldc-help-label"><?= $this->te('Zubeh&ouml;r') ?></div>
                                <div class="ldc-help-text"><?= $this->t('Jedes Zubeh&ouml;rteil erscheint beim Ausgeben als Checkbox. Bei der R&uuml;cknahme m&uuml;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&auml;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)">&#10005;</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,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

// 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>