OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

show.php

54 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $entry['name'],
    'current_route' => 'licenses',
    'full_width'    => true,
    // Die Zuordnung nutzt die zentrale Personensuche.
    'extra_css'     => ['/assets/css/person-search.css'],
    'extra_js'      => ['/assets/js/person-search.js'],
]);

require_once dirname(__DIR__) . '/partials/_attachment_helpers.php';

$e = $entry;
$contractsOn    = ($modules['contracts'] ?? true) === true;
$additionalFiles = $e['additional_files'] ?? [];
$children        = $e['children'] ?? [];
$parent          = $e['parent'] ?? null;

$statusCfg = [
    'active'    => ['label' => 'Aktiv',       'cls' => 'status-pill--success'],
    'trial'     => ['label' => 'Test',         'cls' => 'status-pill--info'],
    'expired'   => ['label' => 'Abgelaufen',   'cls' => 'status-pill--danger'],
    'cancelled' => ['label' => 'Gekündigt',    'cls' => 'status-pill--neutral'],
];
$typeColors = [
    'subscription' => '#1d4ed8', 'perpetual'  => '#15803d',
    'volume'       => '#7c3aed', 'oem'        => '#374151',
    'open_source'  => '#0891b2', 'trial'      => '#d97706',
];

$stCfg = $statusCfg[$e['status']] ?? $statusCfg['active'];
$tc    = $typeColors[$e['type']] ?? '#6b7280';

$seatsUsed = (int)$e['seats_used'];
$seatsTot  = $e['seats'] !== null ? (int)$e['seats'] : null;
$seatPct   = ($seatsTot > 0) ? min(100, (int)round($seatsUsed * 100 / $seatsTot)) : 0;
$seatWarn  = $seatPct >= 100 ? 'bg-danger' : ($seatPct >= 80 ? 'bg-warning' : 'bg-info');

$daysLeft = null;
if (!empty($e['valid_until'])) {
    $daysLeft = (int)ceil((strtotime($e['valid_until']) - time()) / 86400);
}
$expiryBox = $daysLeft !== null
    ? ($daysLeft < 0 ? 'bg-danger' : ($daysLeft <= 30 ? 'bg-warning' : 'bg-secondary'))
    : 'bg-secondary';
?>

<style>
/* ════════════════════════════════════════════════════════
   License Show  –  prefix: lvs-
   (CSS cloned from cs- / contracts show pattern)
   ════════════════════════════════════════════════════════ */

/* ── Layout ────────────────────────────────────────────── */
.lvs-layout { display: grid; grid-template-columns: 1fr 330px; gap: 1.5rem; align-items: start; }
.lvs-main    { display: flex; flex-direction: column; min-width: 0; }
.lvs-sidebar { display: flex; flex-direction: column; position: sticky; top: 1rem; }

/* Titel mit Icon (modul-spezifischer Zusatz zur Standard-Detail-Karte) */
.lvs-layout .detail-card-title { display: flex; align-items: center; gap: 0.5rem; margin: 0; }
.lvs-layout .detail-card-title svg { color: var(--gray-400); flex-shrink: 0; }
.lvs-text { margin: 0; font-size: 0.9rem; color: var(--gray-700); line-height: 1.65; white-space: pre-wrap; }
.lvs-empty { margin: 0; font-size: 0.85rem; color: var(--gray-400); }

/* ── File list ─────────────────────────────────────────── */
.lvs-file-row {
    display: flex; align-items: center; gap: 0.75rem;
    padding: 0.625rem 0.75rem; background: var(--gray-50);
    border-radius: 0.25rem; border: 1px solid var(--gray-100);
}
.lvs-file-info { flex: 1; min-width: 0; }
.lvs-file-name { color: var(--blue-600); font-size: 0.84rem; font-weight: 500; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lvs-file-meta { font-size: 0.7rem; color: var(--gray-400); display: block; }
.lvs-file-dl, .lvs-file-del {
    width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
    border-radius: 0.375rem; border: none; cursor: pointer; flex-shrink: 0;
    transition: all 0.15s; text-decoration: none;
}
.lvs-file-dl  { color: var(--gray-500); background: var(--gray-200); }
.lvs-file-dl:hover  { background: var(--blue-100); color: var(--blue-600); text-decoration: none; }
.lvs-file-del { color: var(--gray-400); background: var(--gray-100); }
.lvs-file-del:hover { background: var(--red-100); color: var(--red-600); }

/* ── Contract cards in main ────────────────────────────── */
.lvs-contract-row {
    display: flex; align-items: center; gap: 0.75rem;
    padding: 0.625rem 0.75rem; background: var(--gray-50);
    border-radius: 0.25rem; border: 1px solid var(--gray-100);
    text-decoration: none; color: inherit; transition: border-color .15s;
}
.lvs-contract-row:hover { border-color: var(--color-primary-soft); text-decoration: none; }
.lvs-contract-info { flex: 1; min-width: 0; }
.lvs-contract-name { font-size: 0.84rem; font-weight: 500; color: var(--blue-600); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lvs-contract-meta { font-size: 0.7rem; color: var(--gray-400); display: block; }

/* ── Detail DL ─────────────────────────────────────────── */
.lvs-dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0.5rem 1rem; align-items: baseline; }
.lvs-dl dt { font-size: 0.72rem; color: var(--gray-400); text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; }
.lvs-dl dd { margin: 0; font-size: 0.875rem; color: var(--gray-900); font-weight: 500; }

/* ── Seat bar ────────────────────────────────────────────── */
.lvs-seat-track { height: 6px; background: var(--gray-200); border-radius: 3px; overflow: hidden; margin-top: 4px; }
.lvs-seat-fill  { height: 100%; border-radius: 3px; }

/* ── License key ───────────────────────────────────────── */
.lvs-key-box {
    font-family: 'Courier New', monospace; font-size: 0.78rem;
    background: var(--code-dark-bg); color: var(--code-dark-text);
    padding: 0.6rem 0.75rem; border-radius: 4px;
    word-break: break-all; letter-spacing: 0.04em; margin-bottom: 0.5rem;
}
.lvs-key-masked { filter: blur(5px); user-select: none; transition: filter .2s; }
.lvs-key-actions { display: flex; gap: 0.375rem; }
.lvs-key-btn {
    flex: 1; padding: 0.3rem 0; border: 1px solid var(--gray-200); border-radius: 4px;
    background: var(--gray-50); cursor: pointer; font-size: 0.75rem; color: var(--gray-700);
    transition: all .15s;
}
.lvs-key-btn:hover { background: var(--gray-100); border-color: var(--gray-300); }

/* ── Responsive ────────────────────────────────────────── */
@media (max-width: 1100px) { .lvs-layout { grid-template-columns: 1fr 300px; } }
@media (max-width: 900px)  { .lvs-layout { grid-template-columns: 1fr; } .lvs-sidebar { position: static; } }
@media (max-width: 480px)  { .lvs-dl { grid-template-columns: 1fr; gap: 0.1rem; } .lvs-dl dt { margin-top: 0.625rem; } .lvs-dl dt:first-child { margin-top: 0; } }
</style>

<div class="lvs-wrap">

    <!-- Content Header -->
    <div class="content-header">
        <div>
            <h1>
                <span class="status-pill <?= $stCfg['cls'] ?>" style="vertical-align:middle;margin-right:0.5rem;"><?= $stCfg['label'] ?></span>
                <?= $this->e($e['name']) ?>
                <?php $extSrc = $e['external_source'] ?? 'manual'; ?>
                <?php if ($extSrc === 'entra'): ?>
                    <span style="display:inline-block;background:var(--sky-100);color:var(--sky-800);border:1px solid var(--sky-200);border-radius:4px;font-size:0.7rem;padding:0.15rem 0.5rem;margin-left:0.4rem;vertical-align:middle;font-weight:500;"
                          title="Aus Microsoft Entra synchronisiert<?= !empty($e['last_synced_at']) ? ' am ' . date('d.m.Y H:i', strtotime($e['last_synced_at'])) : '' ?>">
                        <?= $this->te('&#9729; Entra') ?>
                    </span>
                <?php endif; ?>
            </h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/licenses"><?= $this->te('Lizenzen') ?></a></li>
                <?php if ($parent): ?>
                <li class="breadcrumb-item"><a href="/licenses/<?= (int)$parent['id'] ?>"><?= $this->e(mb_strimwidth($parent['name'], 0, 40, '…')) ?></a></li>
                <?php endif; ?>
                <li class="breadcrumb-item active"><?= $this->e(mb_strimwidth($e['name'], 0, 50, '…')) ?></li>
            </ol>
        </div>
        <?php if ($can_manage): ?>
        <div class="content-header-actions">
            <a href="/licenses/<?= (int)$e['id'] ?>/edit" class="btn btn-default"><?= $this->te('Bearbeiten') ?></a>
            <form action="/licenses/<?= (int)$e['id'] ?>/delete" method="POST" style="display:inline;">
                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                <button type="button" class="btn btn-danger"
                        onclick="itConfirmSubmit(this, 'Lizenz löschen?', 'Die Lizenz „<?= $this->e(addslashes($e['name'])) ?>“ wird mit allen Dateien unwiderruflich gelöscht.', 'Löschen')"><?= $this->te('Löschen') ?></button>
            </form>
        </div>
        <?php endif; ?>
    </div>

    <!-- ═══ BODY ════════════════════════════════════════════════ -->
    <div class="page-body">

        <!-- KPI Small Boxes (schmal wie /inventory) -->
        <div class="kpi-row">
            <!-- Gültig bis -->
            <?php if ($daysLeft !== null): ?>
            <div class="kpi-col">
                <div class="small-box <?= $expiryBox ?>">
                    <div class="inner">
                        <h3><?= date('d.m.Y', strtotime($e['valid_until'])) ?></h3>
                        <p><?= $this->t('Gültig bis') ?> <?= $daysLeft < 0 ? '(abgelaufen)' : '(in ' . $daysLeft . ' Tagen)' ?></p>
                    </div>
                    <div class="icon"><i class="fas fa-clock"></i></div>
                </div>
            </div>
            <?php endif; ?>

            <!-- Seats -->
            <?php if ($seatsTot !== null): ?>
            <div class="kpi-col">
                <div class="small-box <?= $seatWarn ?>">
                    <div class="inner">
                        <h3><?= $seatsUsed ?> / <?= $seatsTot ?></h3>
                        <p><?= $this->t('Seats belegt') ?> (<?= $seatPct ?>%)</p>
                    </div>
                    <div class="icon"><i class="fas fa-users"></i></div>
                </div>
            </div>
            <?php elseif ($seatsUsed > 0): ?>
            <div class="kpi-col">
                <div class="small-box bg-info">
                    <div class="inner">
                        <h3><?= $seatsUsed ?></h3>
                        <p><?= $this->te('Seats genutzt') ?></p>
                    </div>
                    <div class="icon"><i class="fas fa-users"></i></div>
                </div>
            </div>
            <?php endif; ?>

            <!-- Kosten -->
            <?php if (!empty($e['cost_amount'])): ?>
            <div class="kpi-col">
                <div class="small-box bg-primary">
                    <div class="inner">
                        <h3><?= $this->money((float)$e['cost_amount'], 2) ?></h3>
                        <p><?= $this->e($cost_labels[$e['cost_interval'] ?? ''] ?? 'Kosten') ?></p>
                    </div>
                    <div class="icon"><i class="fas fa-euro-sign"></i></div>
                </div>
            </div>
            <?php endif; ?>
        </div>

        <div class="lvs-layout">

            <!-- ── HAUPTSPALTE ──────────────────────────────────── -->
            <main class="lvs-main">

                <!-- Notizen -->
                <?php if (!empty($e['notes'])): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
                            <?= $this->te('Notizen') ?>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                        <p class="lvs-text"><?= $this->e($e['notes']) ?></p>
                    </div>
                </section>
                <?php endif; ?>

                <!-- Zuordnung -->
                <?php
                $zuordnungen   = $assignments ?? [];
                $zugeordnet    = (int)($assigned_count ?? 0);
                // 'manual' heisst selbst gepflegt; jede andere Quelle fuehrt die
                // Belegung selbst und erneuert sie bei jedem Lauf.
                $abgeglichen   = ($e['external_source'] ?? 'manual') !== 'manual';
                // Die gepflegte Zahl und die Zuordnungen koennen auseinanderliegen:
                // bei abgeglichenen Lizenzen kommt sie aus der Quelle, bei selbst
                // gepflegten stammt sie aus der Zeit vor den Zuordnungen.
                $weichtAb      = $zugeordnet !== $seatsUsed;
                ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <i class="fas fa-user-check"></i>
                            <?= $this->t('Zugewiesen an') ?>
                            <?php if ($zugeordnet > 0): ?>
                            <span style="background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:9999px;font-size:0.7rem;font-weight:600;padding:0.1rem 0.4rem;"><?= $zugeordnet ?></span>
                            <?php endif; ?>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                        <?php if ($weichtAb): ?>
                        <div class="alert alert-info">
                            <?php if ($abgeglichen): ?>
                                <?= $this->t('Hier sind <strong>{zugeordnet}</strong> Zuordnungen erfasst, die Lizenz meldet <strong>{belegt}</strong> belegte Plätze. Die gemeldete Zahl kommt aus dem Abgleich und bleibt maßgeblich; die Zuordnungen halten fest, wem der Platz bei euch zugedacht ist.', ['zugeordnet' => $this->e($zugeordnet), 'belegt' => $this->e($seatsUsed)]) ?>
                            <?php else: ?>
                                <?= $this->t('Hier sind <strong>{zugeordnet}</strong> Zuordnungen erfasst, hinterlegt sind <strong>{belegt}</strong> belegte Plätze.', ['zugeordnet' => $this->e($zugeordnet), 'belegt' => $this->e($seatsUsed)]) ?>
                                <?php if ($can_manage): ?>
                                <form method="POST" action="/licenses/<?= (int)$e['id'] ?>/seats-from-assignments" style="display:inline;margin-left:.4rem;">
                                    <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                    <button type="submit" class="btn btn-xs btn-default"><?= $this->te('Aus den Zuordnungen übernehmen') ?></button>
                                </form>
                                <?php endif; ?>
                            <?php endif; ?>
                        </div>
                        <?php endif; ?>

                        <?php if ($zuordnungen === []): ?>
                        <div class="empty-state">
                            <span class="empty-icon"><i class="fas fa-user-check"></i></span>
                            <strong><?= $this->te('Noch niemandem zugewiesen') ?></strong>
                            <p><?= $this->te('Halte fest, welches Gerät oder welche Person einen Platz dieser Lizenz belegt.') ?></p>
                        </div>
                        <?php else: ?>
                        <div class="table-wrapper">
                            <table class="data-table">
                                <thead><tr><th scope="col"><?= $this->te('Ziel') ?></th><th scope="col"><?= $this->te('Herkunft') ?></th><th scope="col"><?= $this->te('Notiz') ?></th><th scope="col"><?= $this->te('Seit') ?></th><?php if ($can_manage): ?><th scope="col"></th><?php endif; ?></tr></thead>
                                <tbody>
                                <?php foreach ($zuordnungen as $z): ?>
                                    <tr>
                                        <td>
                                            <?php if ($z['subject_type'] === 'asset'): ?>
                                                <i class="fas fa-desktop" style="color:var(--gray-400);"></i>
                                                <a class="table-link" href="/inventory/<?= (int)$z['subject_id'] ?>"><?= $this->e((string)($z['asset_name'] ?: $z['inventory_number'])) ?></a>
                                                <?php if (!empty($z['inventory_number']) && !empty($z['asset_name'])): ?>
                                                    <span class="text-muted">(<?= $this->e((string)$z['inventory_number']) ?>)</span>
                                                <?php endif; ?>
                                            <?php else: ?>
                                                <i class="fas fa-user" style="color:var(--gray-400);"></i>
                                                <?= $this->e((string)$z['person_name']) ?>
                                                <?php if (!empty($z['department'])): ?>
                                                    <span class="text-muted">(<?= $this->e((string)$z['department']) ?>)</span>
                                                <?php endif; ?>
                                            <?php endif; ?>
                                        </td>
                                        <td>
                                            <?php if (($z['source'] ?? 'manual') === 'entra'): ?>
                                                <span class="badge" title="<?= $this->te('Aus Microsoft Entra übernommen; wird beim nächsten Abgleich aktualisiert') ?>"><?= $this->te('Entra') ?></span>
                                            <?php else: ?>
                                                <span class="text-muted"><?= $this->te('Von Hand') ?></span>
                                            <?php endif; ?>
                                        </td>
                                        <td><?= $this->e((string)($z['note'] ?? '')) ?></td>
                                        <td><?= $this->e(date('d.m.Y', strtotime((string)$z['assigned_at']))) ?></td>
                                        <?php if ($can_manage): ?>
                                        <td style="text-align:right;">
                                            <?php if (($z['source'] ?? 'manual') === 'entra'): ?>
                                                <!-- Aufheben hätte keinen Bestand: Der nächste Abgleich trüge die
                                                     Zuordnung wieder ein. Geändert wird sie in Entra. -->
                                                <span class="text-muted" style="font-size:.8rem;" title="<?= $this->te('Diese Zuordnung stammt aus Entra und wird dort geändert') ?>">&mdash;</span>
                                            <?php else: ?>
                                            <form method="POST" action="/licenses/<?= (int)$e['id'] ?>/assignments/<?= (int)$z['id'] ?>/delete" onsubmit="return confirm('Zuordnung aufheben?');">
                                                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                                <button type="submit" class="btn btn-xs btn-outline-danger" title="<?= $this->te('Zuordnung aufheben') ?>"><i class="fas fa-times"></i></button>
                                            </form>
                                            <?php endif; ?>
                                        </td>
                                        <?php endif; ?>
                                    </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                        <?php endif; ?>

                        <?php if ($can_manage): ?>
                        <form method="POST" action="/licenses/<?= (int)$e['id'] ?>/assignments" style="margin-top:1rem;">
                            <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                            <div class="form-group">
                                <label for="lic-subject-type"><?= $this->te('Was soll den Platz belegen?') ?></label>
                                <select name="subject_type" class="form-control form-control-sm" id="lic-subject-type">
                                    <option value="person"><?= $this->te('Person aus dem Verzeichnis') ?></option>
                                    <option value="asset"><?= $this->te('Gerät aus dem Inventar') ?></option>
                                </select>
                            </div>
                            <input type="hidden" name="subject_id" id="lic-subject-id" value="">
                            <div class="form-group" id="lic-person-wrap">
                                <label><?= $this->te('Person') ?></label>
                                <input class="form-control form-control-sm person-search-input" type="text" id="lic-person-search"
                                       placeholder="<?= $this->te('Name oder E-Mail suchen…') ?>" autocomplete="off"
                                       data-person-search="/api/persons/search" data-ps-set="name" data-ps-fill-id="lic-subject-id">
                            </div>
                            <div class="form-group" id="lic-asset-wrap" style="display:none;">
                                <label><?= $this->te('Gerät') ?></label>
                                <input class="form-control form-control-sm" type="text" id="lic-asset-search" list="lic-asset-list"
                                       placeholder="<?= $this->te('Name, Inventar- oder Seriennummer suchen…') ?>" autocomplete="off">
                                <datalist id="lic-asset-list"></datalist>
                                <small class="text-muted" id="lic-asset-hint"><?= $this->te('Mindestens zwei Zeichen eingeben, dann aus der Liste wählen.') ?></small>
                            </div>
                            <div class="form-group">
                                <label><?= $this->te('Notiz') ?> <span class="form-hint"><?= $this->te('(optional)') ?></span></label>
                                <input type="text" name="note" class="form-control form-control-sm" placeholder="<?= $this->te('z. B. Projektzuordnung') ?>">
                            </div>
                            <button type="submit" class="btn btn-primary btn-sm"><?= $this->te('Zuweisen') ?></button>
                        </form>
                        <?php endif; ?>
                    </div>
                </section>

                <!-- Software-Inventar: welches Programm zu dieser Lizenz gehört -->
                <?php if (!empty($software_enabled)): ?>
                <?php
                $swZugeordnet = $linked_titles ?? [];
                $swVorschlag  = $title_suggestions ?? [];
                $swGeraete    = 0;
                foreach ($swZugeordnet as $swT) { $swGeraete = max($swGeraete, (int)$swT['geraete']); }
                ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <i class="fas fa-box-open"></i>
                            <?= $this->t('Software-Inventar') ?>
                            <?php if (!empty($swZugeordnet)): ?>
                                <span class="badge"><?= count($swZugeordnet) ?></span>
                            <?php endif; ?>
                        </h2>
                        <a href="/licenses/abgleich" class="btn btn-default btn-xs"><?= $this->te('Abgleich ansehen') ?></a>
                    </div>
                    <div class="detail-card-body">
                        <?php if (empty($swZugeordnet)): ?>
                            <p class="text-muted" style="margin-top:0;font-size:.87rem;">
                                <?= $this->te('Noch kein Programm zugeordnet. Erst mit einer Zuordnung lässt sich sagen, ob die gekauften Plätze zur tatsächlichen Installation passen &ndash; geraten wird sie nicht.') ?>
                            </p>
                        <?php else: ?>
                            <div class="table-wrapper">
                                <table class="data-table">
                                    <thead><tr><th scope="col"><?= $this->te('Programm') ?></th><th scope="col"><?= $this->te('Hersteller') ?></th><th scope="col"><?= $this->te('Geräte') ?></th><?php if ($can_manage): ?><th scope="col"></th><?php endif; ?></tr></thead>
                                    <tbody>
                                    <?php foreach ($swZugeordnet as $swT): ?>
                                        <tr>
                                            <td><a href="/software/<?= (int)$swT['id'] ?>" class="table-link"><?= $this->e((string)$swT['name']) ?></a></td>
                                            <td class="text-muted"><?= $this->e((string)$swT['publisher']) ?: '—' ?></td>
                                            <td><strong><?= (int)$swT['geraete'] ?></strong></td>
                                            <?php if ($can_manage): ?>
                                            <td style="text-align:right;">
                                                <form method="POST" action="/licenses/<?= (int)$e['id'] ?>/software/<?= (int)$swT['id'] ?>/delete" onsubmit="return confirm('Zuordnung aufheben?');">
                                                    <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                                    <button type="submit" class="btn btn-xs btn-outline-danger" title="<?= $this->te('Zuordnung aufheben') ?>"><i class="fas fa-times"></i></button>
                                                </form>
                                            </td>
                                            <?php endif; ?>
                                        </tr>
                                    <?php endforeach; ?>
                                    </tbody>
                                </table>
                            </div>
                            <?php
                            $swPlaetze = (int)($e['seats'] ?? 0);
                            $swUrteil  = OpenNIT\Software\LicenseComparison::differenz([
                                'seats' => $swPlaetze, 'installationen' => $swGeraete,
                            ]);
                            ?>
                            <p style="margin:.75rem 0 0;">
                                <strong><?= $swGeraete ?></strong> <?= $this->te('Gerät(e) mit diesem Programm') ?>
                                <?php if ($swPlaetze > 0): ?>
                                    gegenüber <strong><?= $swPlaetze ?></strong> <?= $this->te('Platz/Plätzen &ndash;') ?>
                                    <span class="status-pill <?= $swUrteil['ton'] === 'fehlend' ? 'status-pill--danger' : ($swUrteil['ton'] === 'genau' ? 'status-pill--warning' : 'status-pill--success') ?>">
                                        <?= $this->e($swUrteil['text']) ?>
                                    </span>
                                <?php endif; ?>
                            </p>
                        <?php endif; ?>

                        <?php if ($can_manage && !empty($swVorschlag)): ?>
                        <div style="margin-top:1rem;">
                            <div class="form-group" style="margin-bottom:.4rem;font-weight:600;font-size:.85rem;"><?= $this->te('Vorschläge aus dem Inventar') ?></div>
                            <p class="text-muted" style="margin:0 0 .5rem;font-size:.82rem;">
                                <?= $this->te('Dem Namen nach passend &ndash; bitte prüfen, bevor Sie übernehmen.') ?>
                            </p>
                            <?php foreach ($swVorschlag as $swV): ?>
                            <form method="POST" action="/licenses/<?= (int)$e['id'] ?>/software" style="display:inline-block;margin:0 .35rem .35rem 0;">
                                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                <input type="hidden" name="title_id" value="<?= (int)$swV['id'] ?>">
                                <button type="submit" class="btn btn-default btn-xs"
                                        title="<?= $this->e((string)$swV['publisher']) ?> · auf <?= (int)$swV['geraete'] ?> Gerät(en)">
                                    + <?= $this->e((string)$swV['name']) ?>
                                    <span class="text-muted">(<?= (int)$swV['geraete'] ?>)</span>
                                </button>
                            </form>
                            <?php endforeach; ?>
                        </div>
                        <?php endif; ?>
                    </div>
                </section>
                <?php endif; ?>

                <!-- Lizenzdateien -->
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
                            <?= $this->te('Lizenzdateien') ?>
                            <?php $totalFiles = (!empty($file_info) ? 1 : 0) + count($additionalFiles); ?>
                            <?php if ($totalFiles > 0): ?>
                            <span style="background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:9999px;font-size:0.7rem;font-weight:600;padding:0.1rem 0.4rem;"><?= $totalFiles ?></span>
                            <?php endif; ?>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                    <?php if (!empty($additionalFiles)): ?>
                    <div style="display:flex;flex-direction:column;gap:0.4rem;">
                        <?php foreach ($additionalFiles as $af): ?>
                        <div class="lvs-file-row">
                            <div style="flex-shrink:0;width:28px;display:flex;align-items:center;justify-content:center;">
                                <svg viewBox="0 0 24 24" fill="none" stroke="#15803d" stroke-width="2" width="20" height="20"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
                            </div>
                            <div class="lvs-file-info">
                                <span class="lvs-file-name"><?= $this->e($af['file_name']) ?></span>
                                <span class="lvs-file-meta"><?= !empty($af['file_size']) ? round($af['file_size']/1024,1).' KB' : '' ?><?= !empty($af['uploaded_by_name']) ? ' · '.$this->e($af['uploaded_by_name']) : '' ?></span>
                            </div>
                            <?php
                                // PDFs/Bilder in der gemeinsamen Lightbox öffnen (statt Download).
                                $afUrl = '/licenses/' . (int)$e['id'] . '/files/' . (int)$af['id'];
                                echo att_view_link($afUrl, $af['file_name'], $af['mime_type'] ?? '', 'lvs-file-dl');
                            ?>
                            <a href="<?= $afUrl ?>?download=1" class="lvs-file-dl" title="<?= $this->te('Herunterladen') ?>">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
                            </a>
                            <?php if ($can_manage): ?>
                            <form action="/licenses/<?= (int)$e['id'] ?>/files/<?= (int)$af['id'] ?>/delete" method="POST" style="margin:0;">
                                <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                                <button type="button" class="lvs-file-del" title="<?= $this->te('Löschen') ?>"
                                        onclick="itConfirmSubmit(this, 'Datei löschen?', 'Die Datei „<?= $this->e(addslashes($af['file_name'])) ?>“ wird gelöscht.', 'Löschen')">
                                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
                                </button>
                            </form>
                            <?php endif; ?>
                        </div>
                        <?php endforeach; ?>
                    </div>
                    <?php else: ?>
                    <p class="lvs-empty"><?= $this->te('Keine Lizenzdateien hochgeladen.') ?>
                        <?php if (!empty($file_info) && $can_manage): ?>
                        <br><small style="color:var(--gray-400);"><?= $this->t('Eine Datei ist im alten Format gespeichert — <a href="/licenses/{id}/file?download=1">herunterladen</a>', ['id' => (int)$e['id']]) ?></small>
                        <?php endif; ?>
                    </p>
                    <?php endif; ?>
                    <?php if ($can_manage): ?>
                    <form action="/licenses/<?= (int)$e['id'] ?>/files" method="POST" enctype="multipart/form-data" style="margin-top:0.875rem;padding-top:0.875rem;border-top:1px solid var(--gray-100);">
                        <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                        <div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
                            <input type="file" name="license_files[]" multiple accept=".lic,.key,.xml,.txt,.pdf,.zip,.bin"
                                   style="flex:1;min-width:0;font-size:0.8rem;padding:.25rem .5rem;border:1px solid var(--gray-300);border-radius:4px;background:var(--color-bg);">
                            <button type="submit" class="btn btn-sm btn-default">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
                                <?= $this->te('Hochladen') ?>
                            </button>
                        </div>
                    </form>
                    <?php endif; ?>
                    </div>
                </section>

                <!-- Unterlizenzen -->
                <?php if (!empty($children)): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
                            <?= $this->te('Unterlizenzen') ?>
                            <span style="background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:9999px;font-size:0.7rem;font-weight:600;padding:0.1rem 0.4rem;"><?= count($children) ?></span>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                    <?php
                    $scCfg = ['active'=>['label'=>'Aktiv','cls'=>'status-pill--success'],'trial'=>['label'=>'Test','cls'=>'status-pill--info'],'expired'=>['label'=>'Abgelaufen','cls'=>'status-pill--danger'],'cancelled'=>['label'=>'Gekündigt','cls'=>'status-pill--neutral'],'archived'=>['label'=>'Archiviert','cls'=>'status-pill--neutral']];
                    ?>
                    <div style="display:flex;flex-direction:column;gap:0.4rem;">
                        <?php foreach ($children as $ch):
                            $chSc = $scCfg[$ch['status']] ?? $scCfg['active'];
                            $chDays = !empty($ch['valid_until']) ? (int)ceil((strtotime($ch['valid_until'])-time())/86400) : null;
                        ?>
                        <a href="/licenses/<?= (int)$ch['id'] ?>" class="lvs-contract-row">
                            <span style="flex-shrink:0;font-size:1rem;">&#128273;</span>
                            <div class="lvs-contract-info">
                                <span class="lvs-contract-name"><?= $this->e($ch['name']) ?></span>
                                <span class="lvs-contract-meta">
                                    <span class="status-pill <?= $chSc['cls'] ?>" style="font-size:0.68rem;padding:0.05rem 0.4rem;"><?= $chSc['label'] ?></span>
                                    <?php if (!empty($ch['vendor'])): ?> · <?= $this->e($ch['vendor']) ?><?php endif; ?>
                                    <?php if ($chDays !== null): ?> · <?= $chDays < 0 ? 'abgelaufen' : 'bis '.date('d.m.Y', strtotime($ch['valid_until'])) ?><?php endif; ?>
                                    <?php if ($ch['type'] === 'perpetual'): ?> <?= $this->te('· ∞ Dauerlizenz') ?><?php endif; ?>
                                </span>
                            </div>
                            <svg viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2" width="14" height="14"><polyline points="9 18 15 12 9 6"/></svg>
                        </a>
                        <?php endforeach; ?>
                    </div>
                    <?php if ($can_manage): ?>
                    <div style="margin-top:0.875rem;padding-top:0.875rem;border-top:1px solid var(--gray-100);">
                        <a href="/licenses/create?parent_id=<?= (int)$e['id'] ?>" class="btn btn-sm btn-default">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="13" height="13"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                            <?= $this->te('Unterlizenz hinzufügen') ?>
                        </a>
                    </div>
                    <?php endif; ?>
                    </div>
                </section>
                <?php elseif ($can_manage && empty($e['parent_id'])): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
                            <?= $this->te('Unterlizenzen') ?>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                        <p class="lvs-empty"><?= $this->te('Noch keine Unterlizenzen.') ?></p>
                        <div style="margin-top:0.875rem;">
                            <a href="/licenses/create?parent_id=<?= (int)$e['id'] ?>" class="btn btn-sm btn-default">
                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="13" height="13"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                                <?= $this->te('Unterlizenz hinzufügen') ?>
                            </a>
                        </div>
                    </div>
                </section>
                <?php endif; ?>

                <!-- Verknüpfte Verträge -->
                <?php if ($contractsOn): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h2 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
                            <?= $this->te('Verknüpfte Verträge') ?>
                            <?php if (!empty($e['contracts'])): ?>
                            <span style="background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:9999px;font-size:0.7rem;font-weight:600;padding:0.1rem 0.4rem;"><?= count($e['contracts']) ?></span>
                            <?php endif; ?>
                        </h2>
                    </div>
                    <div class="detail-card-body">
                    <?php if (empty($e['contracts'])): ?>
                    <p class="lvs-empty"><?= $this->te('Kein Vertrag verknüpft.') ?></p>
                    <?php else: ?>
                    <div style="display:flex;flex-direction:column;gap:0.5rem;">
                        <?php foreach ($e['contracts'] as $con): ?>
                        <a href="/contracts/<?= (int)$con['id'] ?>" class="lvs-contract-row">
                            <span style="font-size:1.1rem;flex-shrink:0;">📄</span>
                            <div class="lvs-contract-info">
                                <span class="lvs-contract-name"><?= $this->e($con['title']) ?></span>
                                <span class="lvs-contract-meta">
                                    <?php if (!empty($con['company_name'])): ?>🏢 <?= $this->e($con['company_name']) ?><?php endif; ?>
                                    <?php if (!empty($con['end_date'])): ?> &middot; bis <?= date('d.m.Y', strtotime($con['end_date'])) ?><?php endif; ?>
                                </span>
                            </div>
                            <svg viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2" width="14" height="14"><polyline points="9 18 15 12 9 6"/></svg>
                        </a>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    </div>
                </section>
                <?php endif; ?>

            </main>

            <!-- ── SIDEBAR ──────────────────────────────────────── -->
            <aside class="lvs-sidebar">

                <!-- Übergeordnete Lizenz -->
                <?php if ($parent): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h3 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
                            <?= $this->te('Gruppe') ?>
                        </h3>
                    </div>
                    <div class="detail-card-body">
                        <a href="/licenses/<?= (int)$parent['id'] ?>" style="display:flex;align-items:center;gap:0.5rem;text-decoration:none;color:var(--blue-600);font-size:0.875rem;font-weight:500;">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><polyline points="15 18 9 12 15 6"/></svg>
                            <?= $this->e($parent['name']) ?>
                        </a>
                        <?php if (!empty($parent['vendor'])): ?>
                        <div style="font-size:0.75rem;color:var(--gray-400);margin-top:0.25rem;padding-left:1.35rem;"><?= $this->e($parent['vendor']) ?></div>
                        <?php endif; ?>
                    </div>
                </section>
                <?php endif; ?>

                <!-- Status & Typ -->
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h3 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><circle cx="12" cy="12" r="10"/></svg>
                            <?= $this->te('Status') ?>
                        </h3>
                    </div>
                    <div class="detail-card-body">
                        <dl class="lvs-dl">
                            <dt><?= $this->te('Status') ?></dt>
                            <dd><span class="status-pill <?= $stCfg['cls'] ?>"><?= $stCfg['label'] ?></span></dd>
                            <dt><?= $this->te('Typ') ?></dt>
                            <dd>
                                <span style="display:inline-block;padding:0.1rem 0.5rem;border-radius:3px;background:<?= $tc ?>18;color:<?= $tc ?>;font-size:0.78rem;font-weight:600;">
                                    <?= $this->e($type_labels[$e['type']] ?? $e['type']) ?>
                                </span>
                            </dd>
                        </dl>
                    </div>
                </section>

                <!-- Lizenzdetails -->
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h3 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
                            <?= $this->te('Lizenzdetails') ?>
                        </h3>
                    </div>
                    <div class="detail-card-body">
                    <dl class="lvs-dl">
                        <?php if (!empty($e['vendor'])): ?>
                        <dt><?= $this->te('Hersteller') ?></dt>
                        <dd><?= $this->e($e['vendor']) ?></dd>
                        <?php endif; ?>

                        <?php if (!empty($e['product'])): ?>
                        <dt><?= $this->te('Produkt') ?></dt>
                        <dd><?= $this->e($e['product']) ?></dd>
                        <?php endif; ?>

                        <?php if (!empty($e['valid_from'])): ?>
                        <dt><?= $this->te('Gültig ab') ?></dt>
                        <dd><?= date('d.m.Y', strtotime($e['valid_from'])) ?></dd>
                        <?php endif; ?>

                        <dt><?= $this->te('Gültig bis') ?></dt>
                        <dd>
                            <?php if (!empty($e['valid_until'])): ?>
                                <?= date('d.m.Y', strtotime($e['valid_until'])) ?>
                            <?php else: ?>
                                <span style="color:var(--gray-400);"><?= $this->te('Unbefristet') ?></span>
                            <?php endif; ?>
                        </dd>

                        <?php if ($seatsTot !== null || $seatsUsed > 0): ?>
                        <dt><?= $this->te('Seats') ?></dt>
                        <dd>
                            <?= $seatsUsed ?><?= $seatsTot !== null ? ' / ' . $seatsTot : '' ?>
                            <?php if ($seatsTot !== null): ?>
                            <div class="lvs-seat-track">
                                <div class="lvs-seat-fill" style="width:<?= $seatPct ?>%;background:<?= $seatPct >= 100 ? 'var(--red-500)' : ($seatPct >= 80 ? 'var(--amber-500)' : 'var(--blue-500)') ?>;"></div>
                            </div>
                            <?php endif; ?>
                        </dd>
                        <?php endif; ?>

                        <?php if (!empty($e['cost_amount'])): ?>
                        <dt><?= $this->te('Kosten') ?></dt>
                        <dd><?= $this->money((float)$e['cost_amount'], 2) ?><?= !empty($e['cost_interval']) ? ' / ' . ($cost_labels[$e['cost_interval']] ?? $e['cost_interval']) : '' ?></dd>
                        <?php endif; ?>

                        <dt><?= $this->te('Erstellt') ?></dt>
                        <dd><?= date('d.m.Y', strtotime($e['created_at'])) ?> von <?= $this->e($e['created_by_name'] ?? '–') ?></dd>
                    </dl>
                    </div>
                </section>

                <!-- Lizenzschlüssel -->
                <?php if (!empty($e['license_key'])): ?>
                <section class="detail-card">
                    <div class="detail-card-header">
                        <h3 class="detail-card-title">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
                            <?= $this->te('Lizenzschlüssel') ?>
                        </h3>
                    </div>
                    <div class="detail-card-body">
                        <div class="lvs-key-box">
                            <span class="lvs-key-masked" id="keyText"><?= $this->e($e['license_key']) ?></span>
                        </div>
                        <div class="lvs-key-actions">
                            <button type="button" class="lvs-key-btn" id="revealKeyBtn"><?= $this->te('&#128065; Anzeigen') ?></button>
                            <button type="button" class="lvs-key-btn" id="copyKeyBtn" data-key="<?= $this->e($e['license_key']) ?>"><?= $this->te('&#128203; Kopieren') ?></button>
                        </div>
                    </div>
                </section>
                <?php endif; ?>

            </aside>
        </div>
    </div>
</div>

<script>
(function() {
    var revealBtn = document.getElementById('revealKeyBtn');
    var keyText   = document.getElementById('keyText');
    if (revealBtn && keyText) {
        revealBtn.addEventListener('click', function() {
            var masked = keyText.classList.contains('lvs-key-masked');
            keyText.classList.toggle('lvs-key-masked', !masked);
            revealBtn.innerHTML = masked ? '&#128584; Verbergen' : '&#128065; Anzeigen';
        });
    }
    var copyBtn = document.getElementById('copyKeyBtn');
    if (copyBtn) {
        copyBtn.addEventListener('click', function() {
            if (navigator.clipboard) {
                navigator.clipboard.writeText(copyBtn.getAttribute('data-key')).then(function() {
                    copyBtn.innerHTML = '&#10003; Kopiert!';
                    setTimeout(function() { copyBtn.innerHTML = '&#128203; Kopieren'; }, 2000);
                });
            }
        });
    }
})();
</script>

<?= $this->partial('confirm_modal') ?>

<script>
// Zuordnung einer Lizenz: Person und Gerät haben getrennte Suchwege. Die
// Personensuche bringt ihr eigenes Vorschlagsfeld mit; für Geräte genügt eine
// Liste, die beim Tippen nachgeladen wird.
(function () {
    var typ    = document.getElementById('lic-subject-type');
    var ziel   = document.getElementById('lic-subject-id');
    if (!typ || !ziel) { return; }

    var personWrap = document.getElementById('lic-person-wrap');
    var assetWrap  = document.getElementById('lic-asset-wrap');
    var assetInput = document.getElementById('lic-asset-search');
    var assetList  = document.getElementById('lic-asset-list');
    var assetHint  = document.getElementById('lic-asset-hint');
    var gefunden   = {};   // Beschriftung -> Kennung

    function umschalten() {
        var istGeraet = typ.value === 'asset';
        personWrap.style.display = istGeraet ? 'none' : '';
        assetWrap.style.display  = istGeraet ? '' : 'none';
        // Bei einem Wechsel darf keine Kennung des anderen Typs stehen bleiben.
        ziel.value = '';
        assetInput.value = '';
        document.getElementById('lic-person-search').value = '';
    }
    typ.addEventListener('change', umschalten);

    var wartet;
    assetInput.addEventListener('input', function () {
        ziel.value = gefunden[assetInput.value] || '';
        if (ziel.value !== '') { assetHint.textContent = 'Gerät gewählt.'; return; }

        assetHint.textContent = 'Mindestens zwei Zeichen eingeben, dann aus der Liste wählen.';
        clearTimeout(wartet);
        if (assetInput.value.trim().length < 2) { return; }
        wartet = setTimeout(function () {
            fetch('/api/licenses/assets?q=' + encodeURIComponent(assetInput.value.trim()), {
                headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }
            })
                .then(function (r) { return r.ok ? r.json() : { results: [] }; })
                .then(function (d) {
                    assetList.innerHTML = '';
                    gefunden = {};
                    (d.results || []).forEach(function (a) {
                        var text = (a.name || a.inventory_number || ('#' + a.id));
                        if (a.inventory_number && a.name) { text += ' (' + a.inventory_number + ')'; }
                        gefunden[text] = a.id;
                        var o = document.createElement('option');
                        o.value = text;
                        assetList.appendChild(o);
                    });
                    if ((d.results || []).length === 0) { assetHint.textContent = 'Kein Gerät gefunden.'; }
                })
                .catch(function () { assetHint.textContent = 'Die Suche ist gerade nicht erreichbar.'; });
        }, 250);
    });
})();
</script>