Quelltext des aktuellen Standes
<?php
/**
* Einzelne Lizenz-Tabellenzeile (aus templates/licenses/index.php ausgelagert).
*
* Erwartet im Scope: $e (Lizenz), $statusCfg, $typeColors, $type_labels,
* $can_manage. Optional: $virtualChild (bool) – rendert die Zeile als
* Kind eines virtuellen Oberpunkts (z. B. Entra-Gruppe).
*/
$sc = $statusCfg[$e['status']] ?? $statusCfg['active'];
$seatsUsed = (int)$e['seats_used'];
$seatsTot = $e['seats'] !== null ? (int)$e['seats'] : null;
$seatPct = ($seatsTot > 0) ? min(100, (int)round($seatsUsed * 100 / $seatsTot)) : 0;
$seatCls = $seatPct >= 100 ? 'full' : ($seatPct >= 80 ? 'warn' : '');
$days = !empty($e['valid_until']) ? (int)ceil((strtotime($e['valid_until']) - time()) / 86400) : null;
$dateColor = $days !== null ? ($days < 0 ? '#dc2626' : ($days <= 30 ? '#d97706' : '#374151')) : '#9ca3af';
$isPerpetual = ($e['type'] === 'perpetual');
$isArchived = ($e['status'] === 'archived');
$virtualChild = !empty($virtualChild);
$isChild = !empty($e['parent_id']) || $virtualChild;
$childCount = (int)($e['children_count'] ?? 0);
// Sortierschlüssel (für klickbare Spalten-Sortierung im Frontend)
$sortMonthly = 0.0;
if (!empty($e['cost_amount']) && !empty($e['cost_interval'])) {
$amt = (float)$e['cost_amount'];
$sortMonthly = match ($e['cost_interval']) {
'monthly' => $amt,
'quarterly' => $amt / 3,
'yearly' => $amt / 12,
default => 0.0, // one_time zählt nicht als laufende Kosten
};
}
$sortValid = !empty($e['valid_until']) ? (int)strtotime($e['valid_until']) : PHP_INT_MAX; // ohne Datum/unbegrenzt ans Ende
?>
<tr class="lv-row clickable<?= $isArchived ? ' lv-row-archived' : '' ?><?= $isChild ? ' lv-row-child' : '' ?><?= $virtualChild ? ' lv-entra-child' : '' ?>"
data-id="<?= (int)$e['id'] ?>" data-parent="<?= (int)($e['parent_id'] ?? 0) ?>"
data-s-name="<?= $this->e(mb_strtolower($e['name'] ?? '')) ?>"
data-s-status="<?= $this->e(mb_strtolower($sc['label'])) ?>"
data-s-type="<?= $this->e(mb_strtolower($type_labels[$e['type']] ?? $e['type'])) ?>"
data-s-seats="<?= $seatsUsed ?>"
data-s-cost="<?= $sortMonthly ?>"
data-s-valid="<?= $sortValid ?>"
onclick="window.location='/licenses/<?= (int)$e['id'] ?>'" tabindex="0"
onkeydown="if(event.key==='Enter')window.location='/licenses/<?= (int)$e['id'] ?>'">
<td>
<span class="status-pill <?= $sc['pill'] ?>">
<span class="status-dot" style="background:<?= $sc['dot'] ?>;"></span>
<?= $this->e($sc['label']) ?>
</span>
</td>
<td>
<?php if ($isChild): ?>
<span class="lv-child-indent">└</span>
<?php endif; ?>
<a href="/licenses/<?= (int)$e['id'] ?>" class="lv-link"><?= $this->e($e['name']) ?></a>
<?php if ($isPerpetual): ?>
<span class="lv-perpetual-badge" title="<?= $this->te('Gekaufte Dauerlizenz') ?>"><?= $this->te('∞ Dauerlizenz') ?></span>
<?php endif; ?>
<?php if ($childCount > 0): ?>
<span class="lv-children-badge" title="<?= $childCount ?> Unterlizenz<?= $childCount !== 1 ? 'en' : '' ?>">📁 <?= $childCount ?></span>
<?php endif; ?>
<?php $extSrc = $e['external_source'] ?? 'manual'; ?>
<?php if ($extSrc === 'entra' && !$virtualChild): ?>
<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.1rem 0.4rem;margin-left:0.25rem;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('☁ Entra') ?>
</span>
<?php endif; ?>
<?php if (!empty($e['vendor']) || !empty($e['product'])): ?>
<br><small class="lv-muted"><?= $isChild ? '<span style="margin-left:1.2rem;"></span>' : '' ?><?= $this->e(implode(' · ', array_filter([$e['vendor'] ?? '', $e['product'] ?? '']))) ?></small>
<?php endif; ?>
</td>
<td>
<?php $tc = $typeColors[$e['type']] ?? '#6b7280'; ?>
<span class="status-pill" style="background:<?= $tc ?>18;color:<?= $tc ?>;border:1px solid <?= $tc ?>35;">
<?= $this->e($type_labels[$e['type']] ?? $e['type']) ?>
</span>
</td>
<td>
<?php if ($seatsTot !== null): ?>
<div class="lv-seat-bar">
<div class="lv-seat-track">
<div class="lv-seat-fill <?= $seatCls ?>" style="width:<?= $seatPct ?>%"></div>
</div>
<small class="lv-muted lv-mono" style="white-space:nowrap;"><?= $seatsUsed ?>/<?= $seatsTot ?></small>
</div>
<?php elseif ($seatsUsed > 0): ?>
<small class="lv-muted"><?= $seatsUsed ?> genutzt</small>
<?php else: ?>
<span class="lv-muted">–</span>
<?php endif; ?>
</td>
<td class="lv-cost">
<?php if (!empty($e['cost_amount'])): ?>
<?= $this->money((float)$e['cost_amount'], 2) ?>
<?php if (!empty($e['cost_interval'])): ?>
<span class="lv-cost-interval"><?= ['monthly'=>'/Monat','quarterly'=>'/Quartal','yearly'=>'/Jahr','one_time'=>'einm.'][$e['cost_interval']] ?? '' ?></span>
<?php endif; ?>
<?php else: ?>
<span class="lv-muted">–</span>
<?php endif; ?>
</td>
<td class="lv-dates">
<?php if (!empty($e['valid_until'])): ?>
<span style="color:<?= $dateColor ?>;font-weight:<?= $days !== null && $days <= 30 ? '600' : '400' ?>;">
<?= date('d.m.Y', strtotime($e['valid_until'])) ?>
</span>
<?php if ($days !== null && $days <= 90): ?>
<br><small style="color:<?= $dateColor ?>;"><?= $days < 0 ? abs($days) . 'd abgelaufen' : 'in ' . $days . 'd' ?></small>
<?php endif; ?>
<?php elseif ($isPerpetual): ?>
<span class="lv-perpetual-valid"><?= $this->te('∞ unbegrenzt') ?></span>
<?php else: ?>
<span class="lv-muted">–</span>
<?php endif; ?>
</td>
<?php if ($can_manage): ?>
<td style="text-align:right;white-space:nowrap;" onclick="event.stopPropagation()">
<a href="/licenses/<?= (int)$e['id'] ?>/edit" class="btn btn-default btn-xs" title="<?= $this->te('Bearbeiten') ?>">✎</a>
<?php if (in_array($e['status'], ['expired','cancelled'], true)): ?>
<form action="/licenses/<?= (int)$e['id'] ?>/archive" method="POST" style="display:inline;">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<button type="button" class="btn btn-default btn-xs" title="<?= $this->te('Archivieren') ?>"
onclick="itConfirmSubmit(this, 'Lizenz archivieren?', 'Die Lizenz „<?= $this->e(addslashes($e['name'])) ?>“ wird ins Archiv verschoben.', 'Archivieren')">🗄</button>
</form>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>