Quelltext des aktuellen Standes
<?php $this->layout('layouts/admin', [
'page_title' => 'Portal-Kennzahlen',
'current_admin_page' => 'portal',
'extra_css' => ['/assets/css/portal-admin.css'],
]);
$m = $metrics ?? [];
$nutzung = $m['nutzung'] ?? [];
$herkunft = $m['herkunft'] ?? [];
$hilfe = $m['selbsthilfe'] ?? [];
$verlauf = $m['verlauf'] ?? [];
$top = $m['top_artikel'] ?? [];
// Balkenbreite im Verlauf: größter Monatswert bestimmt den Maßstab.
$maxMonat = 0;
foreach ($verlauf as $v) {
$maxMonat = max($maxMonat, (int)$v['portal'] + (int)$v['mail'] + (int)$v['sonstige']);
}
$zahl = static fn (int $n): string => number_format($n, 0, ',', '.');
?>
<div class="settings-page pa-page">
<nav class="settings-tabs">
<a class="settings-tab" href="/admin/portal">
<span class="settings-tab-icon"><i class="fas fa-sliders-h"></i></span> <?= $this->te('Einstellungen') ?>
</a>
<a class="settings-tab active" href="/admin/portal/kennzahlen">
<span class="settings-tab-icon"><i class="fas fa-chart-line"></i></span> <?= $this->te('Kennzahlen') ?>
</a>
<a class="settings-tab" href="/admin/portal/confirmations">
<span class="settings-tab-icon"><i class="fas fa-clipboard-check"></i></span> <?= $this->te('Geräte-Bestätigungen') ?>
</a>
</nav>
<?php if (empty($enabled)): ?>
<p class="pa-note pa-note--warn">
<i class="fas fa-exclamation-triangle"></i>
<?= $this->te('Das Self-Service-Portal ist derzeit ausgeschaltet – die Zahlen unten bilden nur ab, was davor entstanden ist.') ?>
<a href="/admin/portal"><?= $this->te('Zu den Portal-Einstellungen') ?></a>
</p>
<?php endif; ?>
<!-- ── Nutzung ──────────────────────────────────────────────── -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📊</span>
<div>
<h2><?= $this->te('Nutzung') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Wie viele Personen das Portal benutzt haben. Gezählt wird, wer etwas getan hat – eine Meldung eröffnet, einen Artikel gelesen, ein Gerät bestätigt. Reines Blättern wird nicht aufgezeichnet und zählt deshalb nicht mit.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="subnav pa-filter-tabs">
<?php foreach (($zeitraeume ?? []) as $t => $label): ?>
<a class="subnav-tab <?= (int)$tage === (int)$t ? 'active' : '' ?>"
href="/admin/portal/kennzahlen?tage=<?= (int)$t ?>"><?= $this->e($label) ?></a>
<?php endforeach; ?>
</div>
<div class="kpi-row">
<div class="kpi-col">
<div class="small-box bg-info">
<div class="inner">
<h3><?= $zahl((int)($nutzung['aktiv'] ?? 0)) ?></h3>
<p><?= $this->te('Aktive Personen') ?></p>
</div>
<div class="icon"><i class="fas fa-users"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-primary">
<div class="inner">
<h3><?= $this->e(number_format((float)($nutzung['anteil'] ?? 0), 1, ',', '.')) ?> %</h3>
<p><?= $this->t('von {anzahl} Personen mit Zugang', ['anzahl' => $this->e($zahl((int)($nutzung['moeglich'] ?? 0)))]) ?></p>
</div>
<div class="icon"><i class="fas fa-user-check"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-success">
<div class="inner">
<h3><?= $zahl((int)($nutzung['meldungen'] ?? 0)) ?></h3>
<p><?= $this->te('Meldungen über das Portal') ?></p>
</div>
<div class="icon"><i class="fas fa-ticket-alt"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-warning">
<div class="inner">
<h3><?= $zahl((int)($nutzung['artikel'] ?? 0)) ?></h3>
<p><?= $this->te('Gelesene Hilfe-Artikel') ?></p>
</div>
<div class="icon"><i class="fas fa-book"></i></div>
</div>
</div>
</div>
<div class="pa-stats">
<span class="pa-stat"><?= $this->te('Geräte-Bestätigungen im Zeitraum') ?>
<strong><?= $zahl((int)($nutzung['bestaetigungen'] ?? 0)) ?></strong></span>
<span class="pa-stat"><?= $this->te('Ab') ?>
<strong><?= $this->e(date('d.m.Y', strtotime((string)($m['seit'] ?? 'today')))) ?></strong></span>
</div>
</div>
</section>
<!-- ── Herkunft der Meldungen ───────────────────────────────── -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📩</span>
<div>
<h2><?= $this->te('Woher die Meldungen kommen') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Der Anteil misst Portal gegen Mail – also die beiden Wege, auf denen Mitarbeitende selbst melden. Was die IT selbst erfasst, bleibt dabei außen vor: Es wäre ohnehin nicht über das Portal gekommen.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="kpi-row">
<div class="kpi-col">
<div class="small-box bg-success">
<div class="inner">
<h3><?= $this->e(number_format((float)($herkunft['anteil_portal'] ?? 0), 1, ',', '.')) ?> %</h3>
<p><?= $this->te('über das Portal statt per Mail') ?></p>
</div>
<div class="icon"><i class="fas fa-laptop"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-info">
<div class="inner">
<h3><?= $zahl((int)($herkunft['portal'] ?? 0)) ?></h3>
<p><?= $this->te('Portal') ?></p>
</div>
<div class="icon"><i class="fas fa-desktop"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-warning">
<div class="inner">
<h3><?= $zahl((int)($herkunft['mail'] ?? 0)) ?></h3>
<p><?= $this->t('E-Mail') ?></p>
</div>
<div class="icon"><i class="fas fa-envelope"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-secondary">
<div class="inner">
<h3><?= $zahl((int)($herkunft['erfasst'] ?? 0)) ?></h3>
<p><?= $this->te('von der IT erfasst') ?></p>
</div>
<div class="icon"><i class="fas fa-user-edit"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-purple">
<div class="inner">
<h3><?= $zahl((int)($herkunft['automatisch'] ?? 0)) ?></h3>
<p><?= $this->te('automatisch entstanden') ?></p>
</div>
<div class="icon"><i class="fas fa-robot"></i></div>
</div>
</div>
</div>
<?php
// Die einzelnen Wege dahinter – sonst bleibt „automatisch" eine
// Zahl, hinter der niemand nachsehen kann.
$jeQuelle = array_filter((array)($herkunft['je_quelle'] ?? []), static fn (int $n): bool => $n > 0);
?>
<?php if ($jeQuelle !== []): ?>
<div class="pa-stats">
<?php foreach ($jeQuelle as $sKey => $sAnzahl): ?>
<span class="pa-stat">
<a class="table-link" href="/tickets?source=<?= $this->e((string)$sKey) ?>"
title="<?= $this->te('Diese Meldungen in der Ticketliste zeigen') ?>">
<?= $this->e($ticket_sources[$sKey]['label'] ?? (string)$sKey) ?>
</a>
<strong><?= $zahl((int)$sAnzahl) ?></strong>
</span>
<?php endforeach; ?>
<?php if ((int)($herkunft['unbekannt'] ?? 0) > 0): ?>
<span class="pa-stat">
<a class="table-link" href="/tickets?source=unbekannt"><?= $this->te('Unbekannte Herkunft') ?></a>
<strong><?= $zahl((int)$herkunft['unbekannt']) ?></strong>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ((int)($herkunft['unbekannt'] ?? 0) > 0): ?>
<p class="pa-note pa-note--off">
<i class="fas fa-info-circle"></i>
<?= $this->t('Bei <strong>{anzahl}</strong> Meldungen des Zeitraums lässt sich die Herkunft nicht bestimmen. Sie stammen aus der Zeit, bevor OpenNIT sie am Ticket festgehalten hat, und trugen keines der Merkmale, aus denen sie sich nachträglich ableiten ließ. Sie zählen weder zum Portal noch zur Mail. Neu entstehende Meldungen tragen ihre Herkunft selbst.', ['anzahl' => $zahl((int)$herkunft['unbekannt'])]) ?>
</p>
<?php endif; ?>
<?php if ($maxMonat > 0): ?>
<h3 class="pa-subhead"><?= $this->te('Verlauf der letzten zwölf Monate') ?></h3>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Monat') ?></th>
<th scope="col" style="width:50%;"><?= $this->te('Verteilung') ?></th>
<th scope="col" style="text-align:right;"><?= $this->te('Portal') ?></th>
<th scope="col" style="text-align:right;"><?= $this->t('E-Mail') ?></th>
<th scope="col" style="text-align:right;"><?= $this->te('Sonstige') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($verlauf as $v):
$summe = (int)$v['portal'] + (int)$v['mail'] + (int)$v['sonstige'];
?>
<tr>
<td><?= $this->e(date('m/Y', (int)strtotime($v['monat'] . '-01'))) ?></td>
<td>
<div class="pa-bar" title="<?= $this->te('{anzahl} Meldungen', ['anzahl' => (int)$summe]) ?>">
<?php foreach ([['portal', 'pa-bar-portal'], ['mail', 'pa-bar-mail'], ['sonstige', 'pa-bar-rest']] as [$k, $cls]): ?>
<?php if ((int)$v[$k] > 0): ?>
<span class="<?= $cls ?>" style="width:<?= number_format((int)$v[$k] / $maxMonat * 100, 2, '.', '') ?>%;"></span>
<?php endif; ?>
<?php endforeach; ?>
</div>
</td>
<td style="text-align:right;"><?= $zahl((int)$v['portal']) ?></td>
<td style="text-align:right;"><?= $zahl((int)$v['mail']) ?></td>
<td style="text-align:right;"><?= $zahl((int)$v['sonstige']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</section>
<!-- ── Selbsthilfe ──────────────────────────────────────────── -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">💡</span>
<div>
<h2><?= $this->te('Selbsthilfe') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Während des Meldens schlägt das Portal passende Hilfe-Artikel vor. Wie oft das eine Meldung erübrigt hat, lässt sich nicht messen, sondern nur schätzen – die Regel dafür steht unter den Zahlen.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="kpi-row">
<div class="kpi-col">
<div class="small-box bg-info">
<div class="inner">
<h3><?= $zahl((int)($hilfe['aufrufe'] ?? 0)) ?></h3>
<p><?= $this->te('Artikel gelesen') ?></p>
</div>
<div class="icon"><i class="fas fa-book-open"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-warning">
<div class="inner">
<h3><?= $zahl((int)($hilfe['beim_melden'] ?? 0)) ?></h3>
<p><?= $this->te('davon aus einem Vorschlag beim Melden') ?></p>
</div>
<div class="icon"><i class="fas fa-lightbulb"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-success">
<div class="inner">
<h3><?= $zahl((int)($hilfe['eruebrigt'] ?? 0)) ?></h3>
<p><?= $this->te('Meldungen vermutlich erübrigt') ?></p>
</div>
<div class="icon"><i class="fas fa-check-circle"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-primary">
<div class="inner">
<h3><?= $this->e(number_format((float)($hilfe['quote'] ?? 0), 1, ',', '.')) ?> %</h3>
<p><?= $this->te('der Vorschläge führten zu keiner Meldung') ?></p>
</div>
<div class="icon"><i class="fas fa-percent"></i></div>
</div>
</div>
</div>
<p class="pa-note pa-note--off">
<i class="fas fa-info-circle"></i>
<span>
<?= $this->t('<strong>Die Regel:</strong> Gezählt wird ein Artikel, der während des Meldens vorgeschlagen und geöffnet wurde und auf den innerhalb von <strong>{minuten} Minuten</strong> keine Meldung derselben Person folgte. Wer länger braucht oder auf anderem Weg meldet, fällt dabei zugunsten der Selbsthilfe aus – die Zahl ist deshalb eher zu hoch als zu niedrig. Sie taugt für den Verlauf über die Zeit, nicht als absolute Aussage.', ['minuten' => (int)($hilfe['fenster'] ?? 30)]) ?>
</span>
</p>
<?php if (!empty($top)): ?>
<h3 class="pa-subhead"><?= $this->te('Meistgelesene Artikel') ?></h3>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Artikel') ?></th>
<th scope="col" style="text-align:right;"><?= $this->te('Aufrufe') ?></th>
<th scope="col" style="text-align:right;"><?= $this->te('davon beim Melden') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($top as $a): ?>
<tr>
<td>
<?php if ((int)$a['page_id'] > 0 && $a['title'] !== '(entfernt)'): ?>
<a class="table-link" href="/pages/<?= (int)$a['page_id'] ?>"><?= $this->e($a['title']) ?></a>
<?php else: ?>
<span class="text-muted"><?= $this->e($a['title']) ?></span>
<?php endif; ?>
</td>
<td style="text-align:right;"><?= $zahl((int)$a['aufrufe']) ?></td>
<td style="text-align:right;"><?= $zahl((int)$a['beim_melden']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-book-open"></i>
<p><?= $this->te('In diesem Zeitraum wurde noch kein Hilfe-Artikel gelesen.') ?></p>
</div>
<?php endif; ?>
</div>
</section>
</div>