OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

form.php

40 KB
<?php $this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => $current_route,
    'full_width'    => true,
    'extra_css'     => ['/assets/css/person-search.css', '/assets/css/company-search.css'],
    'extra_js'      => ['/assets/js/person-search.js', '/assets/js/company-search.js'],
]); ?>
<?php $isEdit = ($asset !== null); $prefill = $prefill ?? []; ?>
<?php
// Der Vorschlag, eine unbekannte Firma anzulegen, setzt das Adressbuch und
// das Recht voraus, es zu pflegen.
$kannFirmaAnlegen = !empty($contacts_enabled) && $this->hasCapability('manage_contacts');
?>

<style>
a { text-decoration:none; }
.inv-wrap { padding:0; }

/* Form layout */
.inv-form-card { background:var(--color-bg); border:1px solid var(--gray-200); border-radius:0.25rem; padding:1.75rem 2rem; }
.inv-form-section { margin-bottom:1.75rem; }
.inv-form-section-title { font-size:0.78rem; font-weight:700; text-transform:uppercase; letter-spacing:0.05em; color:var(--gray-500); margin-bottom:0.85rem; padding-bottom:0.5rem; border-bottom:1px solid var(--gray-100); }
.inv-form-grid { display:grid; grid-template-columns:1fr 1fr 1fr; gap:1rem; }
.inv-form-grid-2 { display:grid; grid-template-columns:1fr 1fr; gap:1rem; }
/* Formulargruppen: zentrale .form-group/.form-control; Abstände liefern die Grid-Gaps */
.inv-form-section .form-group { margin-bottom:0; display:flex; flex-direction:column; gap:0.35rem; }
.inv-form-actions { display:flex; gap:0.75rem; flex-wrap:wrap; align-items:center; margin-top:1.5rem; padding-top:1.25rem; border-top:1px solid var(--gray-100); }

/* Barcode-Scanner-Button */
.scan-btn { flex-shrink:0; width:2.4rem; height:2.4rem; border:1.5px solid var(--gray-300); border-radius:0.25rem;
    background:var(--gray-50); color:var(--gray-500); cursor:pointer; display:inline-flex; align-items:center; justify-content:center;
    transition:background 0.15s,border-color 0.15s,color 0.15s; font-size:1.05rem; padding:0; }
.scan-btn:hover { background:var(--color-primary-pale); border-color:var(--color-primary); color:var(--color-primary); }
@keyframes bc-scan { 0%{top:0} 100%{top:calc(100% - 2px)} }

/* Standort-Dropdown: gemeinsam genutzte Innen-Elemente (Pfeil, Suche, Optionsliste).
   Das eigentliche Auswahlfeld nutzt die zentrale .ss-*-Komponente aus app.js/app.css. */
.ss-arrow { flex-shrink:0; font-size:0.65rem; color:var(--gray-400); transition:transform 0.15s; }
.ss-search {
    padding:0.5rem 0.75rem; border:none; border-bottom:1px solid var(--gray-200);
    font-size:0.85rem; width:100%; box-sizing:border-box; outline:none;
    background:var(--gray-50);
}
.ss-options { max-height:200px; overflow-y:auto; }
.ss-option { padding:0.5rem 0.75rem; font-size:0.85rem; cursor:pointer; color:var(--gray-700); }
.ss-option:hover, .ss-option.active { background:var(--color-primary-pale); color:var(--color-primary-strong); }
.ss-empty { padding:0.5rem 0.75rem; font-size:0.83rem; color:var(--gray-400); }

/* Location dropdown */
.loc-wrap { position:relative; }
.loc-display {
    display:flex; align-items:center; justify-content:space-between; gap:0.4rem;
    padding:0.55rem 0.75rem; border:1.5px solid var(--gray-300); border-radius:0.25rem;
    font-size:0.88rem; background:var(--color-bg); cursor:pointer; user-select:none;
    transition:border-color 0.15s, box-shadow 0.15s;
}
.loc-display:focus, .loc-display.open { border-color:var(--color-primary); box-shadow:0 0 0 3px rgba(var(--color-primary-rgb), 0.15); outline:none; }
.loc-display-text { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.loc-display-text.placeholder { color:var(--gray-400); }
.loc-clear { flex-shrink:0; font-size:0.85rem; color:var(--gray-400); line-height:1; cursor:pointer; padding:0 0.1rem; }
.loc-clear:hover { color:var(--red-600); }
.loc-dropdown {
    display:none; position:absolute; top:calc(100% + 3px); left:0; right:0; z-index:200;
    background:var(--color-bg); border:1.5px solid var(--gray-300); border-radius:0.25rem;
    box-shadow:0 8px 24px rgba(0,0,0,0.1); overflow:hidden;
}
.loc-dropdown.open { display:block; }

@media(max-width:900px) {
    .inv-hero { padding:1rem 1.25rem 1.5rem; }
    .inv-form-grid { grid-template-columns:1fr 1fr; }
}
@media(max-width:560px) {
    .inv-form-grid, .inv-form-grid-2 { grid-template-columns:1fr; }
}
</style>

<div class="inv-wrap">
    <!-- Content Header -->
    <div class="content-header">
        <div>
            <h1><?= $isEdit ? 'Asset bearbeiten' : 'Neues Asset anlegen' ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/inventory"><?= $this->te('Inventar') ?></a></li>
                <?php if ($isEdit): ?>
                <li class="breadcrumb-item"><a href="/inventory/<?= $asset['id'] ?>"><?= $this->e($asset['inventory_number']) ?></a></li>
                <?php endif; ?>
                <li class="breadcrumb-item active"><?= $isEdit ? 'Bearbeiten' : 'Neues Asset' ?></li>
            </ol>
        </div>
    </div>

    <div class="page-body">
        <div class="inv-form-card">
            <form method="POST" action="<?= $isEdit ? '/inventory/' . $asset['id'] : '/inventory' ?>">
                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                <?php if ($isEdit): ?>
                    <input type="hidden" name="_method" value="PUT">
                    <!-- Optimistic Locking: erkennt parallele Änderungen anderer Nutzer -->
                    <input type="hidden" name="loaded_updated_at" value="<?= $this->e($asset['updated_at'] ?? '') ?>">
                <?php endif; ?>

                <!-- Identifikation -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Identifikation') ?></div>
                    <div class="form-group" style="margin-bottom:1rem;">
                        <label for="name"><?= $this->te('Name / Bezeichnung') ?></label>
                        <input class="form-control" type="text" id="name" name="name"
                               value="<?= $this->e($asset['name'] ?? ($prefill['name'] ?? '')) ?>"
                               placeholder="<?= $this->te('Leer lassen → wird auf Inventarnummer gesetzt') ?>"
                               maxlength="255">
                        <span class="form-hint"><?= $this->te('Freitext-Name für dieses Gerät, z.B. „AP Erdgeschoss Süd".') ?></span>
                    </div>
                    <div class="inv-form-grid-2">
                        <div class="form-group">
                            <label for="inventory_number"><?= $this->te('Inventarnummer') ?></label>
                            <input class="form-control" type="text" id="inventory_number" name="inventory_number"
                                   value="<?= $isEdit ? $this->e($asset['inventory_number']) : '' ?>"
                                   placeholder="<?= $this->te('Leer lassen für automatische Vergabe') ?>"
                                   maxlength="100">
                            <?php if (!$isEdit): ?>
                                <span class="form-hint"><?= $this->te('Wird automatisch generiert wenn leer gelassen.') ?></span>
                            <?php endif; ?>
                        </div>
                        <div class="form-group">
                            <label for="serial_number"><?= $this->te('Seriennummer') ?></label>
                            <div style="display:flex;gap:0.4rem;align-items:stretch;">
                                <input class="form-control" type="text" id="serial_number" name="serial_number"
                                       value="<?= $this->e($asset['serial_number'] ?? ($prefill['serial_number'] ?? '')) ?>"
                                       placeholder="<?= $this->te('z.B. 3GH7T8X') ?>" maxlength="255">
                                <button type="button" class="scan-btn" onclick="openBarcodeScanner('serial_number')" title="<?= $this->te('Seriennummer per Kamera scannen') ?>">&#128247;</button>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Klassifikation -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Klassifikation') ?></div>
                    <div class="inv-form-grid">
                        <div class="form-group">
                            <label for="category_id"><?= $this->te('Kategorie *') ?></label>
                            <select class="form-control" id="category_id" name="category_id" required onchange="filterTypes()">
                                <option value=""><?= $this->te('— Kategorie wählen —') ?></option>
                                <?php foreach ($categories as $cat): ?>
                                    <option value="<?= $cat['id'] ?>"
                                        data-useful-life="<?= (int)($cat['default_useful_life_months'] ?? 0) ?>"
                                        <?= ((int)($asset['category_id'] ?? $preselect_category) === (int)$cat['id']) ? 'selected' : '' ?>>
                                        <?= $this->e($cat['name']) ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="form-group">
                            <label for="status"><?= $this->te('Status') ?></label>
                            <select class="form-control" id="status" name="status">
                                <option value="active" <?= ($asset['status'] ?? 'active') === 'active' ? 'selected' : '' ?>><?= $this->te('Aktiv') ?></option>
                                <option value="storage" <?= ($asset['status'] ?? '') === 'storage' ? 'selected' : '' ?>><?= $this->te('Lager') ?></option>
                                <option value="decommissioned" <?= ($asset['status'] ?? '') === 'decommissioned' ? 'selected' : '' ?>><?= $this->te('Ausgemustert') ?></option>
                            </select>
                        </div>
                        <div class="form-group">
                            <label for="manufacturer_id"><?= $this->te('Hersteller') ?></label>
                            <select class="form-control" id="manufacturer_id" name="manufacturer_id" onchange="filterTypes()">
                                <option value=""><?= $this->te('— Hersteller wählen —') ?></option>
                                <?php foreach ($manufacturers as $m): ?>
                                    <option value="<?= $m['id'] ?>"
                                        <?= (int)($asset['manufacturer_id'] ?? 0) === (int)$m['id'] ? 'selected' : '' ?>>
                                        <?= $this->e($m['name']) ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="form-group">
                            <label for="type_id"><?= $this->te('Modell / Typ') ?></label>
                            <select class="form-control" id="type_id" name="type_id">
                                <option value=""><?= $this->te('— Modell wählen —') ?></option>
                                <?php foreach ($types as $t): ?>
                                    <option value="<?= $t['id'] ?>"
                                            data-manufacturer="<?= $t['manufacturer_id'] ?>"
                                            data-category="<?= $t['category_id'] ?: '' ?>"
                                        <?= (int)($asset['type_id'] ?? 0) === (int)$t['id'] ? 'selected' : '' ?>>
                                        <?= $this->e($t['manufacturer_name'] . ' – ' . $t['name']) ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                            <span class="form-hint"><?= $this->te('Wird nach Hersteller &amp; Kategorie gefiltert.') ?></span>
                        </div>
                    </div>
                </div>

                <!-- Zuweisung & Standort -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Zuweisung &amp; Standort') ?></div>
                    <div class="inv-form-grid-2">
                        <?php
                        // Führt das MDM die Zuweisung, wäre eine Eingabe hier beim
                        // nächsten Abgleich wieder überschrieben – das Feld bleibt
                        // dann lesbar, aber unveränderlich. Meldet das MDM keine
                        // Person, lässt der Abgleich die Zuweisung stehen und sie
                        // ist weiterhin von Hand pflegbar.
                        $mdmQuellen  = ['intune' => 'Intune', 'relution' => 'Relution'];
                        $mdmQuelle   = trim((string)($asset['mdm_source'] ?? ''));
                        // Beim Rückschreiben bleibt das Feld bedienbar: Die Änderung
                        // geht dann zuerst nach Intune und von dort beim nächsten
                        // Abgleich wieder zurück.
                        $mdmRueck    = !empty($mdm_writeback);
                        $mdmFuehrt   = $mdmQuelle !== '' && !empty($asset['mdm_person_id']) && !$mdmRueck;
                        $mdmName     = $mdmQuellen[$mdmQuelle] ?? $mdmQuelle;
                        ?>
                        <div class="form-group">
                            <label for="assigned_to"><?= $this->te('Zugewiesen an') ?></label>
                            <?php if ($mdmFuehrt): ?>
                            <input type="text" id="assigned_to" class="form-control" readonly
                                   value="<?= $this->e(($asset['assigned_person_name'] ?? '') !== '' ? $asset['assigned_person_name'] : ($asset['assigned_to'] ?? '')) ?>"
                                   aria-describedby="assigned-mdm-hint">
                            <p class="form-hint" id="assigned-mdm-hint">
                                <i class="fas fa-lock" aria-hidden="true"></i>
                                <?= $this->te('Wird von {mdm} geführt. Eine Änderung hier würde der nächste Abgleich überschreiben – die Zuweisung ändert sich in {mdm}.', ['mdm' => $mdmName]) ?>
                            </p>
                            <?php else: ?>
                            <input type="hidden" name="assigned_person_id" id="assigned_person_id"
                                   value="<?= (int)($asset['assigned_person_id'] ?? 0) ?: '' ?>">
                            <div class="psx-wrap">
                                <input type="text" id="assigned_to" name="assigned_to"
                                       class="form-control"
                                       value="<?= $this->e($asset['assigned_to'] ?? '') ?>"
                                       placeholder="<?= $this->te('Name oder Abteilung — oder Verzeichnis durchsuchen') ?>"
                                       maxlength="255"
                                       autocomplete="off"
                                       data-person-search="/api/persons/search"
                                       data-ps-min="1"
                                       data-ps-set="name"
                                       data-ps-fill-id="assigned_person_id"
                                       data-ps-badge="ps-badge-assigned">
                                <span class="psx-selected" id="ps-badge-assigned" style="display:none;">
                                    👤 <?= $this->te('Aus Verzeichnis') ?>
                                    <button type="button" class="psx-selected-clear" aria-label="<?= $this->te('Verknüpfung aufheben') ?>">×</button>
                                </span>
                            </div>
                            <?php if ($mdmRueck): ?>
                            <p class="form-hint">
                                <i class="fas fa-exchange-alt" aria-hidden="true"></i>
                                <?= $this->te('Die Änderung wird als primärer Benutzer nach {mdm} geschrieben. Nur Personen aus dem Entra-Verzeichnis sind möglich; gelingt das Schreiben nicht, bleibt die bisherige Zuweisung stehen.', ['mdm' => $mdmName]) ?>
                            </p>
                            <?php elseif ($mdmQuelle !== ''): ?>
                            <p class="form-hint">
                                <?= $this->te('Von {mdm} verwaltet, dort ist aber keine Person hinterlegt – eine Zuweisung hier bleibt erhalten.', ['mdm' => $mdmName]) ?>
                            </p>
                            <?php endif; ?>
                            <?php endif; ?>
                        </div>
                        <div class="form-group">
                            <label><?= $this->te('Standort') ?></label>
                            <input type="hidden" id="location_id" name="location_id" value="<?= (int)($asset['location_id'] ?? 0) ?: '' ?>">
                            <?php
                            // Find the pre-selected location's full path
                            $selectedLocPath = '';
                            if (!empty($asset['location_id'])) {
                                foreach ($locations as $loc) {
                                    if ((int)$loc['id'] === (int)$asset['location_id']) {
                                        $selectedLocPath = $loc['full_path'] ?? $loc['name'];
                                        break;
                                    }
                                }
                            }
                            ?>
                            <div class="loc-wrap">
                                <!-- Display button (like searchable select) -->
                                <div class="loc-display" id="loc-display" tabindex="0">
                                    <span class="loc-display-text <?= $selectedLocPath ? '' : 'placeholder' ?>" id="loc-display-text">
                                        <?= $selectedLocPath ? $this->e($selectedLocPath) : '— Standort wählen —' ?>
                                    </span>
                                    <span class="loc-clear" id="loc-clear" title="<?= $this->te('Standort entfernen') ?>" style="<?= $selectedLocPath ? '' : 'display:none' ?>">&#215;</span>
                                    <span class="ss-arrow">▼</span>
                                </div>
                                <div class="loc-dropdown" id="loc-dropdown">
                                    <input type="text" class="ss-search" id="loc-search" placeholder="<?= $this->te('Suchen…') ?>" autocomplete="off">
                                    <div class="ss-options" id="loc-options">
                                        <?php foreach ($locations as $loc): ?>
                                        <div class="ss-option" data-id="<?= $loc['id'] ?>" data-path="<?= $this->e($loc['full_path']) ?>">
                                            <?= $this->e($loc['full_path']) ?>
                                        </div>
                                        <?php endforeach; ?>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Kaufdaten -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Kaufdaten') ?></div>
                    <div class="inv-form-grid-2">
                        <div class="form-group">
                            <label for="purchase_date"><?= $this->te('Kaufdatum') ?></label>
                            <input class="form-control" type="date" id="purchase_date" name="purchase_date"
                                   value="<?= $this->e($asset['purchase_date'] ?? '') ?>">
                        </div>
                        <div class="form-group">
                            <label for="warranty_until"><?= $this->te('Garantie bis') ?></label>
                            <input class="form-control" type="date" id="warranty_until" name="warranty_until"
                                   value="<?= $this->e($asset['warranty_until'] ?? '') ?>">
                        </div>
                    </div>
                    <?php
                    /** @var ?\OpenNIT\Assets\Valuation $valuation */
                    $val = $valuation ?? null;
                    ?>
                    <?php if ($val && $val->aktiv()): ?>
                    <div class="inv-form-grid-2" style="margin-top:1rem;">
                        <?php if ($val->brauchtAnschaffungswert()): ?>
                        <div class="form-group">
                            <label for="purchase_price"><?= $this->te('Anschaffungswert ({cur})', ['cur' => $this->currency()]) ?></label>
                            <input class="form-control" type="text" inputmode="decimal" id="purchase_price" name="purchase_price"
                                   value="<?= $this->e(isset($asset['purchase_price']) && $asset['purchase_price'] !== null ? number_format((float)$asset['purchase_price'], 2, ',', '') : '') ?>"
                                   placeholder="<?= $this->te('z. B. 1499,00') ?>" maxlength="20">
                            <span class="form-hint"><?= $this->te(
                                'Grundlage für {zweck}. Ohne Betrag bleiben die Werte leer.',
                                ['zweck' => $this->t($val->abschreibung && $val->restwert
                                    ? 'Abschreibung und Restwert'
                                    : ($val->abschreibung ? 'die Abschreibung' : 'den Restwert'))]
                            ) ?></span>
                        </div>
                        <?php endif; ?>
                        <div class="form-group">
                            <label for="useful_life_months"><?= $this->te('Nutzungsdauer (Monate)') ?></label>
                            <input class="form-control" type="number" id="useful_life_months" name="useful_life_months"
                                   value="<?= $this->e(isset($asset['useful_life_months']) && $asset['useful_life_months'] !== null ? (string)(int)$asset['useful_life_months'] : '') ?>"
                                   min="1" max="<?= (int)($max_monate ?? 600) ?>" step="1" placeholder="z. B. 36">
                            <span class="form-hint"><?= $this->te('Läuft ab dem Kaufdatum. Die Kategorie kann den Wert vorbelegen.') ?></span>
                        </div>
                    </div>
                    <?php endif; ?>
                </div>

                <!-- Lieferant -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Lieferant') ?></div>
                    <div class="form-group" style="max-width:500px;">
                        <?php if ($contacts_enabled): ?>
                            <label for="supplier_company_id"><?= $this->te('Lieferant') ?></label>
                            <select class="form-control" id="supplier_company_id" name="supplier_company_id" <?= $kannFirmaAnlegen ? 'data-create-company' : '' ?>>
                                <option value=""><?= $this->te('— Kein Lieferant —') ?></option>
                                <?php foreach ($companies as $co): ?>
                                    <option value="<?= $co['id'] ?>"
                                        <?= (int)($asset['supplier_company_id'] ?? 0) === (int)$co['id'] ? 'selected' : '' ?>>
                                        <?= $this->e($co['name']) ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                            <span class="form-hint"><?= $this->te('Firma aus dem Kontaktbuch wählen.') ?></span>
                        <?php else: ?>
                            <label for="supplier_name"><?= $this->te('Lieferant') ?></label>
                            <input class="form-control" type="text" id="supplier_name" name="supplier_name"
                                   value="<?= $this->e($asset['supplier_name'] ?? '') ?>"
                                   placeholder="<?= $this->te('z.B. Mustermann GmbH') ?>" maxlength="255">
                        <?php endif; ?>
                    </div>
                </div>

                <!-- Zusatzfelder (dynamisch geladen) -->
                <div id="custom-fields-section" style="display:none;">
                    <div class="inv-form-section">
                        <div class="inv-form-section-title"><?= $this->te('Weitere Informationen') ?></div>
                        <div id="custom-fields-body"></div>
                    </div>
                </div>

                <!-- Notizen -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Notizen') ?></div>
                    <div class="form-group">
                        <label for="notes"><?= $this->te('Notizen / Anmerkungen') ?></label>
                        <textarea class="form-control" id="notes" name="notes" rows="4" placeholder="<?= $this->te('Interne Notizen…') ?>"><?= $this->e($asset['notes'] ?? '') ?></textarea>
                    </div>
                </div>

                <!-- Leihgerät -->
                <div class="inv-form-section">
                    <div class="inv-form-section-title"><?= $this->te('Leihgerät') ?></div>
                    <div class="form-group" style="flex-direction:row;align-items:center;gap:0.75rem;">
                        <input type="checkbox" id="is_loan_device" name="is_loan_device" value="1"
                               style="width:18px;height:18px;cursor:pointer;accent-color:var(--color-primary);"
                               <?= !empty($asset['is_loan_device']) ? 'checked' : '' ?>>
                        <label for="is_loan_device" style="margin:0;font-weight:500;cursor:pointer;">
                            <?= $this->te('Als Leihgerät markieren') ?>
                        </label>
                    </div>
                    <p style="font-size:0.8rem;color:var(--gray-500);margin:0.25rem 0 0;">
                        <?= $this->t('Aktiviert: Das Gerät steht im <strong>Leihgeräte-Modul</strong> direkt zum Verleihen zur Verfügung.') ?>
                    </p>
                </div>

                <div class="inv-form-actions">
                    <button type="submit" class="btn btn-primary">
                        <?= $isEdit ? '💾 Speichern' : '+ Asset anlegen' ?>
                    </button>
                    <a href="<?= $isEdit ? '/inventory/' . $asset['id'] : '/inventory' ?>" class="btn btn-default"><?= $this->te('Abbrechen') ?></a>
                </div>
            </form>
        </div>
    </div>
</div>

<script>
// ---- Typ-Filter nach Hersteller + Kategorie ----
// Die Modell-/Typ-Auswahl zeigt nur Einträge, die zum gewählten Hersteller und
// zur Kategorie passen. Nicht passende Optionen werden deaktiviert; die zentrale
// durchsuchbare Auswahl (app.js) blendet deaktivierte Optionen aus.
function filterTypes() {
    var mfrId = document.getElementById('manufacturer_id').value;
    var catId = document.getElementById('category_id').value;
    var sel   = document.getElementById('type_id');
    Array.from(sel.options).forEach(function(opt) {
        if (!opt.value) { opt.disabled = false; return; }
        var mfrMatch = !mfrId || opt.dataset.manufacturer === mfrId;
        var catMatch = !catId || !opt.dataset.category || opt.dataset.category === catId;
        opt.disabled = !(mfrMatch && catMatch);
        if (opt.disabled && sel.value === opt.value) sel.value = '';
    });
    if (window.ssSync) window.ssSync(sel);
}

// ---- Standort-Dropdown mit Suche ----
(function() {
    var hidden    = document.getElementById('location_id');
    var display   = document.getElementById('loc-display');
    var displayTxt= document.getElementById('loc-display-text');
    var clearBtn  = document.getElementById('loc-clear');
    var dropdown  = document.getElementById('loc-dropdown');
    var searchInp = document.getElementById('loc-search');
    var optList   = document.getElementById('loc-options');
    if (!display) return;

    function openDrop() {
        display.classList.add('open');
        dropdown.classList.add('open');
        searchInp.value = '';
        filterLocs('');
        searchInp.focus();
    }
    function closeDrop() {
        display.classList.remove('open');
        dropdown.classList.remove('open');
    }
    function pick(id, path) {
        hidden.value = id;
        displayTxt.textContent = path;
        displayTxt.classList.remove('placeholder');
        clearBtn.style.display = '';
        closeDrop();
    }
    function clearSelection() {
        hidden.value = '';
        displayTxt.textContent = '— Standort wählen —';
        displayTxt.classList.add('placeholder');
        clearBtn.style.display = 'none';
    }
    function filterLocs(q) {
        var any = false;
        Array.from(optList.querySelectorAll('.ss-option')).forEach(function(div) {
            var match = !q || div.dataset.path.toLowerCase().includes(q.toLowerCase());
            div.style.display = match ? '' : 'none';
            if (match) any = true;
        });
        var emptyEl = optList.querySelector('.ss-empty');
        if (!any && q) {
            if (!emptyEl) { emptyEl = document.createElement('div'); emptyEl.className = 'ss-empty'; emptyEl.textContent = 'Keine Ergebnisse'; optList.appendChild(emptyEl); }
            emptyEl.style.display = '';
        } else if (emptyEl) { emptyEl.style.display = 'none'; }
    }

    // Click on options
    optList.addEventListener('click', function(e) {
        var opt = e.target.closest('.ss-option');
        if (opt) pick(opt.dataset.id, opt.dataset.path);
    });

    display.addEventListener('click', function(e) {
        if (e.target === clearBtn || clearBtn.contains(e.target)) { e.stopPropagation(); clearSelection(); return; }
        dropdown.classList.contains('open') ? closeDrop() : openDrop();
    });
    display.addEventListener('keydown', function(e) {
        if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDrop(); }
        if (e.key === 'Escape') closeDrop();
    });
    searchInp.addEventListener('input', function() { filterLocs(searchInp.value); });
    document.addEventListener('click', function(e) {
        if (!display.closest('.loc-wrap').contains(e.target)) closeDrop();
    });
})();

// Init filter on load
filterTypes();

</script>

<script>
// ---- Benutzerdefinierte Felder ----
var CF_SAVED = <?= json_encode($custom_field_values ?? [], JSON_FORCE_OBJECT) ?>;

function loadCustomFields() {
    var catId  = document.getElementById('category_id').value;
    var typeId = document.getElementById('type_id').value;
    var section = document.getElementById('custom-fields-section');
    var body    = document.getElementById('custom-fields-body');

    if (!catId && !typeId) {
        section.style.display = 'none';
        body.innerHTML = '';
        return;
    }

    var url = '/api/inventory/custom-fields?';
    if (catId)  url += 'category_id=' + catId + '&';
    if (typeId) url += 'type_id=' + typeId;

    fetch(url, {credentials: 'same-origin'})
        .then(function(r) { return r.json(); })
        .then(function(data) {
            var fields = data.fields || [];
            if (!fields.length) {
                section.style.display = 'none';
                body.innerHTML = '';
                return;
            }
            var html = '<div class="inv-form-grid">';
            fields.forEach(function(f) {
                // Gespeicherten Wert nehmen; fällt der weg, Standard-Wert des Feldes
                var savedVal = CF_SAVED[f.id] !== undefined ? CF_SAVED[f.id]
                             : (f.default_value || '');
                var req = f.is_required ? ' required' : '';
                html += '<div class="form-group">';
                html += '<label>' + escHtml(f.field_label) + (f.is_required ? ' <span class="text-danger">*</span>' : '') + '</label>';
                if (f.field_type === 'dropdown' && f.dropdown_options && f.dropdown_options.length) {
                    html += '<select class="form-control" name="custom_fields[' + f.id + ']"' + req + '>';
                    html += '<option value="">— wählen —</option>';
                    f.dropdown_options.forEach(function(opt) {
                        var sel = (savedVal === opt) ? ' selected' : '';
                        html += '<option value="' + escHtml(opt) + '"' + sel + '>' + escHtml(opt) + '</option>';
                    });
                    html += '</select>';
                } else {
                    html += '<input class="form-control" type="text" name="custom_fields[' + f.id + ']" value="' + escHtml(savedVal) + '" placeholder="' + escHtml(f.field_label) + '"' + req + '>';
                }
                html += '</div>';
            });
            html += '</div>';
            body.innerHTML = html;
            section.style.display = '';
        })
        .catch(function() {
            section.style.display = 'none';
        });
}

function escHtml(s) {
    return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}

// Nutzungsdauer aus der Kategorie vorbelegen. Ein bereits eingetragener Wert
// bleibt stehen – die Vorgabe füllt nur eine Lücke.
(function () {
    var kat   = document.getElementById('category_id');
    var dauer = document.getElementById('useful_life_months');
    if (!kat || !dauer) { return; }
    function vorbelegen() {
        if (dauer.value.trim() !== '') { return; }
        var opt = kat.options[kat.selectedIndex];
        var vorgabe = opt ? parseInt(opt.getAttribute('data-useful-life') || '0', 10) : 0;
        if (vorgabe > 0) { dauer.value = String(vorgabe); }
    }
    kat.addEventListener('change', vorbelegen);
    vorbelegen();
})();

// Felder neu laden wenn Kategorie oder Typ sich ändert
document.getElementById('category_id').addEventListener('change', loadCustomFields);
document.getElementById('type_id').addEventListener('change', loadCustomFields);

// Beim Laden sofort aufrufen (Edit-Modus)
loadCustomFields();
</script>

<!-- Barcode-Scanner-Modal -->
<div id="barcode-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;padding:1rem;">
    <div style="background:var(--color-bg);border-radius:0.5rem;overflow:hidden;width:100%;max-width:480px;box-shadow:0 8px 40px rgba(0,0,0,0.4);">
        <div style="display:flex;align-items:center;justify-content:space-between;padding:0.9rem 1.25rem;background:var(--gray-800);">
            <span style="color:var(--gray-100);font-weight:700;font-size:0.9rem;"><?= $this->te('&#128247; Barcode scannen') ?></span>
            <button type="button" onclick="closeBarcodeScanner()" class="modal-close" aria-label="<?= $this->te('Schließen') ?>">&#10005;</button>
        </div>
        <div style="position:relative;background:#000;aspect-ratio:16/9;max-height:300px;overflow:hidden;">
            <video id="barcode-video" autoplay playsinline muted style="width:100%;height:100%;object-fit:cover;display:block;"></video>
            <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;">
                <div style="width:75%;height:38%;border:2px solid rgba(var(--color-primary-rgb), 0.9);border-radius:4px;box-shadow:0 0 0 9999px rgba(0,0,0,0.35);position:relative;overflow:hidden;">
                    <div style="position:absolute;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--color-primary),transparent);animation:bc-scan 1.8s linear infinite;"></div>
                </div>
            </div>
        </div>
        <div style="padding:0.75rem 1.25rem;background:var(--gray-50);display:flex;align-items:center;gap:0.75rem;">
            <span id="barcode-status" style="font-size:0.82rem;color:var(--gray-500);flex:1;"><?= $this->te('Kamera wird gestartet…') ?></span>
            <button type="button" onclick="closeBarcodeScanner()" class="btn btn-sm btn-default"><?= $this->te('Abbrechen') ?></button>
        </div>
    </div>
</div>
<script>
(function () {
    var _reader = null, _target = null, _controls = null;

    function loadZXing(cb) {
        if (window.ZXingBrowser) { cb(); return; }
        var s = document.createElement('script');
        s.src = '/assets/js/zxing-browser.min.js';
        s.onload = cb;
        s.onerror = function () {
            document.getElementById('barcode-status').textContent = '⚠ Scanner-Bibliothek konnte nicht geladen werden.';
        };
        document.head.appendChild(s);
    }

    window.openBarcodeScanner = function (target) {
        if (typeof target === 'string') target = document.getElementById(target);
        _target = target;
        if (!window.isSecureContext) {
            alert('Barcode-Scanner benötigt HTTPS.\nBitte über eine gesicherte Verbindung aufrufen.');
            return;
        }
        var modal  = document.getElementById('barcode-modal');
        var status = document.getElementById('barcode-status');
        modal.style.display = 'flex';
        status.textContent  = 'Scanner wird geladen…';

        loadZXing(function () {
            status.textContent = 'Kamera wird gestartet…';
            _reader = new window.ZXingBrowser.BrowserMultiFormatReader();
            var video = document.getElementById('barcode-video');
            _reader.decodeFromConstraints(
                { video: { facingMode: { ideal: 'environment' }, width: { ideal: 1280 }, height: { ideal: 720 } } },
                video,
                function (result, err, controls) {
                    _controls = controls;
                    if (result) {
                        if (_target) {
                            _target.value = result.getText();
                            _target.focus();
                            _target.dispatchEvent(new Event('input',  { bubbles: true }));
                            _target.dispatchEvent(new Event('change', { bubbles: true }));
                        }
                        window.closeBarcodeScanner();
                        return;
                    }
                    if (err && err.name !== 'NotFoundException') {
                        status.textContent = err.name === 'NotAllowedError'
                            ? '⚠ Kamerazugriff verweigert – bitte in den Browser-Einstellungen erlauben.'
                            : '⚠ ' + (err.message || err.name);
                    } else {
                        status.textContent = 'Barcode in den Rahmen halten…';
                    }
                }
            ).catch(function (err) {
                status.textContent = err.name === 'NotAllowedError'
                    ? '⚠ Kamerazugriff verweigert – bitte in den Browser-Einstellungen erlauben.'
                    : '⚠ ' + (err.message || err.name);
            });
        });
    };

    window.closeBarcodeScanner = function () {
        if (_controls) { try { _controls.stop(); } catch (_) {} _controls = null; }
        if (_reader)   { try { _reader.reset(); }   catch (_) {} _reader   = null; }
        var v = document.getElementById('barcode-video');
        if (v) { v.srcObject = null; v.pause(); }
        document.getElementById('barcode-modal').style.display = 'none';
        _target = null;
    };

    document.getElementById('barcode-modal').addEventListener('click', function (e) {
        if (e.target === this) window.closeBarcodeScanner();
    });
}());
</script>

<?php if ($kannFirmaAnlegen): ?>
<?= $this->render('partials/_company_quick_create') ?>
<?php endif; ?>