Quelltext des aktuellen Standes
<?php
$this->layout('layouts/main', [
'page_title' => $page_title,
'current_route' => $current_route,
'full_width' => true,
]);
$statusLabels = [
'available' => [$this->te('Verfügbar'), 'status-pill--success'],
'lent' => [$this->te('Ausgeliehen'), 'status-pill--danger'],
'reserved' => [$this->te('Reserviert'), 'status-pill--warning'],
'maintenance' => [$this->te('Wartung'), 'status-pill--purple'],
'retired' => [$this->te('Ausgemustert'), 'status-pill--neutral'],
];
?>
<style>
a { text-decoration: none; }
a:hover { text-decoration: none; }
.ldv-wrap { padding: 0; }
.ldv-tab-bar { display:flex; gap:0.25rem; padding:0 1rem; border-bottom:2px solid var(--gray-200); background:var(--color-bg); }
.ldv-tab { padding:0.6rem 1rem; font-size:0.85rem; font-weight:600; color:var(--gray-500); text-decoration:none;
border-bottom:2px solid transparent; margin-bottom:-2px; transition:color 0.15s,border-color 0.15s; }
.ldv-tab:hover { color:var(--color-primary); }
.ldv-tab.active { color:var(--color-primary); border-bottom-color:var(--color-primary); }
/* Body */
/* Device grid */
.ldv-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:1.25rem; }
.ldv-card {
background:var(--color-bg);
border:1px solid var(--gray-200);
border-radius:0.25rem;
overflow:hidden;
transition:box-shadow 0.15s, transform 0.15s;
text-decoration:none;
color:inherit;
display:flex; flex-direction:column;
position:relative;
}
.ldv-card:hover { box-shadow:0 4px 20px rgba(0,0,0,0.1); }
.ldv-card-title { color:var(--gray-900); display:block; }
/* Stretched-Link: ganze Karte klickbar, aber als echter, fokussierbarer Link */
.ldv-card-titlelink::after { content:''; position:absolute; inset:0; z-index:1; }
.ldv-card-titlelink:focus-visible { outline:2px solid var(--color-primary); outline-offset:2px; }
.ldv-card-img {
width:100%; height:160px; object-fit:cover; background:var(--gray-100);
display:flex; align-items:center; justify-content:center;
}
.ldv-card-img img { width:100%; height:100%; object-fit:cover; }
.ldv-card-img-placeholder { color:var(--gray-300); }
.ldv-card-body { padding:1rem; flex:1; display:flex; flex-direction:column; gap:0.35rem; }
.ldv-card-title { font-size:0.95rem; font-weight:700; color:var(--gray-900); }
.ldv-card-type { font-size:0.78rem; color:var(--gray-500); }
.ldv-card-serial { font-size:0.75rem; color:var(--gray-400); font-family:monospace; }
.ldv-pending-badge {
display:inline-flex; align-items:center; gap:0.35rem; margin-top:0.5rem;
font-size:0.72rem; font-weight:700; padding:0.2rem 0.55rem; border-radius:9999px;
background:var(--blue-50); color:var(--blue-700); border:1px solid var(--blue-200); text-decoration:none;
}
.ldv-pending-badge:hover { background:var(--blue-100); }
.ldv-card-footer { padding:0.65rem 1rem; border-top:1px solid var(--gray-100); display:flex; align-items:center; justify-content:space-between; }
.ldv-card-actions { display:flex; gap:0.4rem; }
/* Empty */
.ldv-empty { text-align:center; padding:4rem 1rem; color:var(--gray-400); }
.ldv-empty svg { display:block; margin:0 auto 1rem; opacity:0.35; }
@media(max-width:768px){
.filter-card-body form { flex-direction:column; gap:0.5rem; }
.filter-card-body input, .filter-card-body select, .filter-card-body button { width:100%; box-sizing:border-box; }
.ldv-tab-bar { padding:0 1rem; overflow-x:auto; white-space:nowrap; }
.ldv-grid { grid-template-columns:1fr; }
}
@media(max-width:480px){
.ldv-card-actions { flex-direction:column; }
}
</style>
<div class="ldv-wrap">
<!-- Content Header -->
<div class="content-header">
<div>
<h1><?= $this->te('Leihgeräte') ?></h1>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
<li class="breadcrumb-item"><a href="/loans"><?= $this->te('Leihgeräte') ?></a></li>
<li class="breadcrumb-item active"><?= $this->te('Geräteliste') ?></li>
</ol>
</div>
<div class="content-header-actions">
<a href="/loans" class="btn btn-default"><?= $this->te('Kalender') ?></a>
<a href="/loans/lendings" class="btn btn-default"><?= $this->te('Ausleihen') ?></a>
<?php if ($can_manage): ?>
<a href="/loans/devices/create" class="btn btn-warning"><?= $this->te('Neues Gerät') ?></a>
<?php endif; ?>
</div>
</div>
<!-- KPI Small Boxes -->
<div class="page-body" style="padding-bottom:0;">
<div class="kpi-row">
<div class="kpi-col">
<div class="small-box bg-info">
<div class="inner"><h3><?= (int)$summary['total'] ?></h3><p><?= $this->te('Gesamt') ?></p></div>
<div class="icon"><i class="fas fa-laptop"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-success">
<div class="inner"><h3><?= (int)$summary['available'] ?></h3><p><?= $this->te('Verfügbar') ?></p></div>
<div class="icon"><i class="fas fa-check"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box <?= (int)$summary['lent'] > 0 ? 'bg-warning' : 'bg-secondary' ?>">
<div class="inner"><h3><?= (int)$summary['lent'] ?></h3><p><?= $this->te('Ausgeliehen') ?></p></div>
<div class="icon"><i class="fas fa-share-square"></i></div>
</div>
</div>
<div class="kpi-col">
<div class="small-box bg-secondary">
<div class="inner"><h3><?= (int)$summary['reserved'] ?></h3><p><?= $this->te('Reserviert') ?></p></div>
<div class="icon"><i class="fas fa-clock"></i></div>
</div>
</div>
</div>
</div>
<!-- Filter -->
<div class="page-body" style="padding-bottom:0;padding-top:0;">
<div class="card card-outline card-primary mb-0">
<div class="filter-card-body">
<form method="GET" action="/loans/devices" style="display:flex;gap:0.75rem;flex-wrap:wrap;width:100%;align-items:center;">
<input type="search" name="q" value="<?= $this->e($search) ?>" placeholder="<?= $this->te('Gerät suchen…') ?>" class="form-control form-control-sm" style="min-width:200px;width:auto;" autocomplete="off">
<select name="type" class="form-control form-control-sm" style="width:auto;" onchange="this.form.submit()">
<option value=""><?= $this->te('Alle Typen') ?></option>
<?php foreach ($types as $t): ?>
<option value="<?= $this->e($t['type']) ?>" <?= $type_filter === $t['type'] ? 'selected' : '' ?>><?= $this->e($t['type']) ?></option>
<?php endforeach; ?>
</select>
<select name="status" class="form-control form-control-sm" style="width:auto;" onchange="this.form.submit()">
<option value=""><?= $this->te('Alle Status') ?></option>
<?php foreach ($statusLabels as $val => [$label, $col]): ?>
<option value="<?= $val ?>" <?= $status_filter === $val ? 'selected' : '' ?>><?= $label ?></option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-sm btn-primary"><?= $this->te('Suchen') ?></button>
<?php if ($search || $status_filter || $type_filter): ?>
<a href="/loans/devices" class="btn btn-sm btn-default"><?= $this->te('Zurücksetzen') ?></a>
<?php endif; ?>
<span style="margin-left:auto;font-size:0.82rem;color:var(--gray-500);"><?= count($devices) ?> Gerät<?= count($devices) !== 1 ? 'e' : '' ?></span>
</form>
</div>
</div>
</div>
<!-- Body -->
<div class="page-body" style="padding-top:0.75rem;">
<?php if (empty($devices)): ?>
<div class="ldv-empty">
<svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
<p style="font-weight:600;margin-bottom:0.5rem;"><?= $this->te('Keine Geräte gefunden') ?></p>
<?php if ($can_manage): ?>
<a href="/loans/devices/create" style="color:var(--amber-600);font-weight:600;font-size:0.85rem;"><?= $this->te('Erstes Gerät anlegen →') ?></a>
<?php endif; ?>
</div>
<?php else: ?>
<div class="ldv-grid">
<?php foreach ($devices as $device):
[$slabel, $scol] = $statusLabels[$device['status']] ?? ['Unbekannt','status-pill--neutral'];
?>
<div class="ldv-card">
<div class="ldv-card-img">
<?php if (!empty($device['image_path'])): ?>
<img src="<?= $this->e($device['image_path']) ?>" alt="<?= $this->e($device['title']) ?>">
<?php else: ?>
<svg class="ldv-card-img-placeholder" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
<?php endif; ?>
</div>
<div class="ldv-card-body">
<a href="/loans/devices/<?= $device['id'] ?>" class="ldv-card-title ldv-card-titlelink"><?= $this->e($device['title']) ?></a>
<?php if ($device['type']): ?><div class="ldv-card-type"><?= $this->e($device['type']) ?></div><?php endif; ?>
<?php if ($device['serial_number']): ?><div class="ldv-card-serial">S/N: <?= $this->e($device['serial_number']) ?></div><?php endif; ?>
<?php if (!empty($device['pending_count'])): ?>
<a href="/loans" class="ldv-pending-badge" title="<?= $this->te('Offene Self-Service-Anfrage(n) – warten auf Freigabe') ?>">
<i class="fas fa-hourglass-half"></i>
<?= (int)$device['pending_count'] ?> Anfrage<?= (int)$device['pending_count'] !== 1 ? 'n' : '' ?> offen
</a>
<?php endif; ?>
</div>
<div class="ldv-card-footer">
<span class="status-pill <?= $scol ?>"><?= $slabel ?></span>
<?php if ($can_issue && $device['status'] === 'available'): ?>
<a href="/loans/issue/<?= $device['id'] ?>" class="btn btn-sm btn-primary" style="position:relative;z-index:2;"><?= $this->te('Ausgeben') ?></a>
<?php elseif ($can_issue && $device['status'] === 'reserved' && !empty($device['current_lending_id'])): ?>
<a href="/loans/lendings/<?= (int)$device['current_lending_id'] ?>" class="btn btn-sm btn-default" style="position:relative;z-index:2;"><?= $this->te('Herausgeben') ?></a>
<?php elseif ($can_issue && $device['status'] === 'lent' && !empty($device['current_lending_id'])): ?>
<a href="/loans/lendings/<?= (int)$device['current_lending_id'] ?>/return" class="btn btn-sm btn-default" style="position:relative;z-index:2;"><?= $this->te('Zurücknehmen') ?></a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>