OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

index.php

26 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => $current_route,
    'full_width'    => true,
]);

$monthNames = ['','Januar','Februar','März','April','Mai','Juni',
               'Juli','August','September','Oktober','November','Dezember'];
$dayNames   = ['Mo','Di','Mi','Do','Fr','Sa','So'];

$today     = date('Y-m-d');
$todayDay  = (int)date('j');
$todayMon  = (int)date('n');
$todayYear = (int)date('Y');
?>
<style>
a { text-decoration: none; }
a:hover { text-decoration: none; }
/* ld- = loan devices module */
.ld-wrap { padding: 0; }

/* Calendar nav */
.ld-cal-nav {
    background: var(--gray-50);
    border: 1px solid var(--gray-200);
    border-bottom: none;
    border-radius: 0.25rem 0.25rem 0 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1.25rem;
}
.ld-cal-nav-arrow {
    display: inline-flex; align-items: center; gap: 0.3rem;
    background: var(--color-bg); color: var(--gray-700);
    border: 1px solid var(--gray-200); border-radius: 0.4rem; padding: 0.4rem 0.85rem;
    font-size: 0.82rem; font-weight: 600; cursor: pointer; text-decoration: none;
    transition: background 0.15s;
}
.ld-cal-nav-arrow:hover { background: var(--gray-100); }
.ld-cal-month-title { font-size: 1.1rem; font-weight: 700; color: var(--gray-900); }

/* Body */

/* Calendar grid */
.ld-calendar {
    border: 1px solid var(--gray-200);
    border-radius: 0.25rem;
    overflow: hidden;
}
.ld-cal-header {
    display: grid; grid-template-columns: repeat(7, 1fr);
    background: var(--gray-50); border-bottom: 1px solid var(--gray-200);
}
.ld-cal-header-cell {
    text-align: center; padding: 0.5rem;
    font-size: 0.75rem; font-weight: 700; color: var(--gray-500);
    border-right: 1px solid var(--gray-200);
}
.ld-cal-header-cell:last-child { border-right: none; }

/* Week row */
.ld-week { border-bottom: 1px solid var(--gray-200); }
.ld-week:last-child { border-bottom: none; }
.ld-week-days {
    display: grid; grid-template-columns: repeat(7, 1fr);
}
.ld-cal-cell {
    min-height: 36px; padding: 0.3rem 0.4rem 0;
    border-right: 1px solid var(--gray-200);
}
.ld-cal-cell:last-child { border-right: none; }
.ld-cal-cell.empty { background: var(--gray-50); }
.ld-cal-cell.today { background: var(--amber-50); }
.ld-cal-day-num {
    font-size: 0.78rem; font-weight: 700; color: var(--gray-700);
    display: inline-flex; width: 1.5rem; height: 1.5rem;
    align-items: center; justify-content: center; border-radius: 50%;
}
.ld-cal-cell.today .ld-cal-day-num { background: var(--amber-600); color: var(--color-on-accent); }

/* Bar lanes */
.ld-week-bars {
    display: grid; grid-template-columns: repeat(7, 1fr);
    padding: 2px 0 4px; gap: 2px 0;
    min-height: 4px;
}
.ld-bar {
    height: 20px; overflow: hidden;
    display: flex; align-items: center;
}
.ld-bar a {
    display: block; width: 100%; height: 100%;
    font-size: 0.67rem; font-weight: 600; color: var(--color-on-accent);
    padding: 0 0.4rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    border-radius: 3px; line-height: 20px;
}
.ld-bar a:hover { opacity: 0.85; }
/* Bar shape based on position */
.ld-bar.bar-start a { border-radius: 3px 0 0 3px; padding-right: 0; }
.ld-bar.bar-mid   a { border-radius: 0; padding: 0; font-size:0; }
.ld-bar.bar-end   a { border-radius: 0 3px 3px 0; padding-left: 0; font-size:0; }
.ld-bar.bar-single a { border-radius: 3px; }
/* Continues from previous week – no left rounding */
.ld-bar.bar-cont-l a { border-radius: 0 3px 3px 0; }
.ld-bar.bar-cont-l.bar-end a { border-radius: 0 3px 3px 0; }
/* Reserved bars */
.ld-bar.bar-reserved a { opacity: 0.75; background: repeating-linear-gradient(90deg, transparent, transparent 3px, rgba(255,255,255,0.3) 3px, rgba(255,255,255,0.3) 4px); }
/* Offene Anfragen (pending) – gestrichelt, transparent, noch nicht bestätigt */
.ld-bar.bar-pending a { opacity: 0.55; background: repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(255,255,255,0.45) 4px, rgba(255,255,255,0.45) 6px); }
.ld-bar.bar-pending { outline: 1px dashed rgba(255,255,255,0.85); outline-offset: -2px; border-radius: 3px; }
/* Überfällige Ausleihen */
.ld-bar.bar-overdue a { background: var(--red-600) !important; animation: overdue-pulse 2s ease-in-out infinite; }
@keyframes overdue-pulse { 0%,100% { opacity:1; } 50% { opacity:0.7; } }
@media (prefers-reduced-motion: reduce) { .ld-bar.bar-overdue a, [class*="overdue-pulse"] { animation: none !important; } }

/* Legend */
.ld-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1.25rem;
    padding: 1rem 1.25rem;
    background: var(--gray-50);
    border-radius: 0.25rem;
    border: 1px solid var(--gray-200);
}
.ld-legend-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.78rem;
    color: var(--gray-700);
    text-decoration: none;
}
.ld-legend-dot {
    width: 10px; height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}
.ld-legend-device { font-weight: 600; }
.ld-legend-sep { color: var(--gray-300); }

/* Tab bar */
.ld-tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 2px solid var(--gray-200);
    margin-bottom: 1.75rem;
}
.ld-tab {
    padding: 0.6rem 1.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;
}
.ld-tab:hover { color: var(--amber-600); }
.ld-tab.active { color: var(--amber-600); border-bottom-color: var(--amber-600); }

@media (max-width: 768px) {
    /* Kalender auf Mobile: horizontales Scrollen statt Quetschen */
    .ld-calendar-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .ld-calendar { min-width: 320px; }
    .ld-cal-cell { min-height: 48px; }
    .ld-bar a { font-size: 0.58rem; }
    .ld-today-grid { grid-template-columns: 1fr !important; }
    .ld-tabs { overflow-x: auto; white-space: nowrap; padding-bottom: 2px; }
}
@media (max-width: 480px) {
    .ld-cal-nav-arrow { padding: 0.35rem 0.6rem; font-size: 0.78rem; }
    .ld-month-title { font-size: 0.95rem; }
    .ld-legend { flex-direction: column; gap: 0.5rem; }
}
</style>

<div class="ld-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 active"><?= $this->te('Leihgeräte') ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <a href="/loans/lendings" class="btn btn-default"><?= $this->te('Ausleihen') ?></a>
            <a href="/loans/devices" class="btn btn-default"><?= $this->te('Geräte') ?></a>
            <a href="/loans/statistics" class="btn btn-default"><?= $this->te('Auswertung') ?></a>
            <?php if ($can_issue): ?>
            <a href="/loans/issue" class="btn btn-warning"><?= $this->te('Ausgeben') ?></a>
            <?php endif; ?>
        </div>
    </div>

    <!-- KPI Small Boxes -->
    <div class="page-body">
        <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>
            <?php if ((int)($summary['unclaimed'] ?? 0) > 0): ?>
            <div class="kpi-col">
                <a href="/loans/lendings?unclaimed=1">
                    <div class="small-box bg-warning">
                        <div class="inner"><h3><?= (int)$summary['unclaimed'] ?></h3><p><?= $this->te('Nicht abgeholt') ?></p></div>
                        <div class="icon"><i class="fas fa-hourglass-half"></i></div>
                    </div>
                </a>
            </div>
            <?php endif; ?>
        </div>
        <?php if (!empty($pending_requests)): ?>
        <!-- Offene Self-Service-Anfragen -->
        <div style="margin:0 0 1.25rem;border:1px solid var(--blue-200);background:var(--blue-50);border-radius:0.25rem;overflow:hidden;">
            <div style="display:flex;align-items:center;gap:0.5rem;padding:0.6rem 1rem;background:var(--blue-100);border-bottom:1px solid var(--blue-200);">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2.5"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
                <span style="font-weight:700;color:var(--blue-800);font-size:0.88rem;"><?= count($pending_requests) ?> offene Anfrage<?= count($pending_requests) !== 1 ? 'n' : '' ?> – warten auf Bestätigung</span>
            </div>
            <?php foreach ($pending_requests as $i => $pr): ?>
            <a href="/loans/lendings/<?= (int)$pr['id'] ?>"
               style="display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:0.55rem 1rem;color:var(--gray-700);<?= $i < count($pending_requests) - 1 ? 'border-bottom:1px solid var(--blue-100);' : '' ?>">
                <span style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
                    <strong style="color:var(--gray-900);"><?= $this->e($pr['device_title']) ?></strong>
                    <span style="color:var(--gray-500);"> · <?= $this->e($pr['borrower_name']) ?></span>
                </span>
                <span style="flex-shrink:0;font-size:0.8rem;color:var(--gray-500);">
                    <?= date('d.m.Y', strtotime($pr['planned_start'])) ?> – <?= date('d.m.Y', strtotime($pr['planned_end'])) ?>
                </span>
            </a>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>

        <?php if (!empty($overdue_lendings)): ?>
        <!-- Überfällige Ausleihen (monatsunabhängig) -->
        <div style="margin:0 0 1.25rem;border:1px solid var(--red-200);background:var(--red-50);border-radius:0.25rem;overflow:hidden;">
            <div style="display:flex;align-items:center;gap:0.5rem;padding:0.6rem 1rem;background:var(--red-100);border-bottom:1px solid var(--red-200);">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
                <span style="font-weight:700;color:var(--red-800);font-size:0.88rem;"><?= count($overdue_lendings) ?> überfällige Ausleihe<?= count($overdue_lendings) !== 1 ? 'n' : '' ?></span>
            </div>
            <?php foreach ($overdue_lendings as $i => $ol): ?>
            <a href="/loans/lendings/<?= (int)$ol['id'] ?>"
               style="display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:0.55rem 1rem;color:var(--gray-700);<?= $i < count($overdue_lendings) - 1 ? 'border-bottom:1px solid var(--red-100);' : '' ?>">
                <span style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
                    <strong style="color:var(--gray-900);"><?= $this->e($ol['device_title']) ?></strong>
                    <span style="color:var(--gray-500);"> · <?= $this->e($ol['borrower_name']) ?></span>
                </span>
                <span style="flex-shrink:0;font-size:0.8rem;font-weight:700;color:var(--red-600);">
                    <?= (int)$ol['days_overdue'] ?> Tag<?= (int)$ol['days_overdue'] !== 1 ? 'e' : '' ?> überfällig
                    <span style="color:var(--gray-400);font-weight:400;">(fällig <?= date('d.m.Y', strtotime($ol['planned_end'])) ?>)</span>
                </span>
            </a>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>

        <?php if (!empty($today_activity['pickups']) || !empty($today_activity['returns'])): ?>
        <!-- Heute-Aktivität -->
        <div class="ld-today-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin:0 0 1.25rem;">
            <div style="border:1px solid var(--amber-200);background:var(--amber-50);border-radius:0.25rem;overflow:hidden;">
                <div style="padding:0.55rem 1rem;font-weight:700;color:var(--amber-800);font-size:0.83rem;border-bottom:1px solid var(--amber-200);background:var(--amber-100);">
                    <?= $this->t('Heute abzuholen') ?> (<?= count($today_activity['pickups']) ?>)
                </div>
                <?php if (empty($today_activity['pickups'])): ?>
                    <div style="padding:0.75rem 1rem;color:var(--gray-400);font-size:0.82rem;"><?= $this->te('Keine offenen Abholungen.') ?></div>
                <?php else: foreach ($today_activity['pickups'] as $p): ?>
                    <a href="/loans/lendings/<?= (int)$p['id'] ?>" style="display:flex;justify-content:space-between;gap:0.75rem;padding:0.5rem 1rem;color:var(--gray-700);border-bottom:1px solid var(--amber-100);">
                        <span style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><strong style="color:var(--gray-900);"><?= $this->e($p['device_title']) ?></strong> · <?= $this->e($p['borrower_name']) ?></span>
                        <span style="flex-shrink:0;color:var(--gray-400);font-size:0.8rem;">ab <?= date('d.m.', strtotime($p['planned_start'])) ?></span>
                    </a>
                <?php endforeach; endif; ?>
            </div>
            <div style="border:1px solid var(--green-200);background:var(--green-50);border-radius:0.25rem;overflow:hidden;">
                <div style="padding:0.55rem 1rem;font-weight:700;color:var(--green-700);font-size:0.83rem;border-bottom:1px solid var(--green-200);background:var(--green-100);">
                    <?= $this->t('Heute zurück') ?> (<?= count($today_activity['returns']) ?>)
                </div>
                <?php if (empty($today_activity['returns'])): ?>
                    <div style="padding:0.75rem 1rem;color:var(--gray-400);font-size:0.82rem;"><?= $this->te('Keine Rückgaben heute.') ?></div>
                <?php else: foreach ($today_activity['returns'] as $r): ?>
                    <a href="/loans/lendings/<?= (int)$r['id'] ?>" style="display:flex;justify-content:space-between;gap:0.75rem;padding:0.5rem 1rem;color:var(--gray-700);border-bottom:1px solid var(--green-100);">
                        <span style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"><strong style="color:var(--gray-900);"><?= $this->e($r['device_title']) ?></strong> · <?= $this->e($r['borrower_name']) ?></span>
                        <span style="flex-shrink:0;color:var(--green-600);font-size:0.8rem;font-weight:600;"><?= $this->te('fällig heute') ?></span>
                    </a>
                <?php endforeach; endif; ?>
            </div>
        </div>
        <?php endif; ?>

        <!-- Month nav -->
        <div class="ld-cal-nav">
            <a href="/loans?year=<?= $prevYear ?>&month=<?= $prevMonth ?>" class="ld-cal-nav-arrow">
                &#8592; <?= $monthNames[$prevMonth] ?>
            </a>
            <div style="display:flex;align-items:center;gap:0.75rem;">
                <span class="ld-cal-month-title"><?= $monthNames[$month] ?> <?= $year ?></span>
                <?php if (!($month === $todayMon && $year === $todayYear)): ?>
                    <a href="/loans" class="ld-cal-nav-arrow" style="padding:0.3rem 0.7rem;"><?= $this->te('Heute') ?></a>
                <?php endif; ?>
            </div>
            <a href="/loans?year=<?= $nextYear ?>&month=<?= $nextMonth ?>" class="ld-cal-nav-arrow">
                <?= $monthNames[$nextMonth] ?> &#8594;
            </a>
        </div>

        <!-- Calendar -->
        <?php
        // Build weeks: array of 7-element arrays (null = empty, int = day number)
        $calCells = array_fill(0, $startDow - 1, null);
        for ($d = 1; $d <= $daysInMonth; $d++) $calCells[] = $d;
        while (count($calCells) % 7 !== 0) $calCells[] = null;
        $calWeeks = array_chunk($calCells, 7);

        // Precompute clamped day range per lending
        foreach ($lendings as &$l) {
            $ls = date('Y-m', strtotime($l['planned_start']));
            $le = date('Y-m', strtotime($l['planned_end']));
            $curMon = sprintf('%04d-%02d', $year, $month);
            $l['_ds'] = ($ls < $curMon) ? 1 : (int)date('j', strtotime($l['planned_start']));
            $l['_de'] = ($le > $curMon) ? $daysInMonth : (int)date('j', strtotime($l['planned_end']));
            // actual start/end in month?
            $l['_actual_start'] = ($ls === $curMon);
            $l['_actual_end']   = ($le === $curMon);
        }
        unset($l);

        // Assign bars to lanes per week (greedy lane packing)
        $weekBars = [];
        foreach ($calWeeks as $wi => $weekDays) {
            $wDays = array_filter($weekDays, fn($d) => $d !== null);
            if (empty($wDays)) { $weekBars[$wi] = []; continue; }
            $wFirst = min($wDays); $wLast = max($wDays);

            $bars = [];
            foreach ($lendings as $l) {
                if ($l['_de'] < $wFirst || $l['_ds'] > $wLast) continue;
                $barDay1 = max($l['_ds'], $wFirst);
                $barDay2 = min($l['_de'], $wLast);
                // find column (1-7) of barDay1 / barDay2 in this week
                $col1 = $col2 = null;
                foreach ($weekDays as $ci => $d) {
                    if ($d === $barDay1) $col1 = $ci + 1;
                    if ($d === $barDay2) $col2 = $ci + 1;
                }
                if ($col1 === null || $col2 === null) continue;
                $roundLeft  = $l['_actual_start'] && ($barDay1 === $l['_ds']);
                $roundRight = $l['_actual_end']   && ($barDay2 === $l['_de']);
                $bars[] = ['l'=>$l,'col1'=>$col1,'col2'=>$col2,'rl'=>$roundLeft,'rr'=>$roundRight];
            }
            // Lane assignment
            $laneRanges = [];
            foreach ($bars as &$bar) {
                $lane = 0;
                while (true) {
                    if (!isset($laneRanges[$lane])) break;
                    $ok = true;
                    foreach ($laneRanges[$lane] as $r) {
                        if ($bar['col1'] <= $r[1] && $bar['col2'] >= $r[0]) { $ok = false; break; }
                    }
                    if ($ok) break;
                    $lane++;
                }
                $laneRanges[$lane][] = [$bar['col1'], $bar['col2']];
                $bar['lane'] = $lane + 1; // grid-row is 1-based
            }
            unset($bar);
            $weekBars[$wi] = $bars;
        }
        ?>
        <div class="ld-calendar-wrap"><div class="ld-calendar">
            <!-- Header -->
            <div class="ld-cal-header">
                <?php foreach ($dayNames as $dn): ?>
                    <div class="ld-cal-header-cell"><?= $dn ?></div>
                <?php endforeach; ?>
            </div>

            <!-- Weeks -->
            <?php foreach ($calWeeks as $wi => $weekDays): ?>
            <div class="ld-week">
                <!-- Day number cells -->
                <div class="ld-week-days">
                    <?php foreach ($weekDays as $d):
                        $isToday = ($d === $todayDay && $month === $todayMon && $year === $todayYear);
                    ?>
                    <div class="ld-cal-cell <?= $d === null ? 'empty' : ($isToday ? 'today' : '') ?>">
                        <?php if ($d !== null): ?>
                            <span class="ld-cal-day-num"><?= $d ?></span>
                        <?php endif; ?>
                    </div>
                    <?php endforeach; ?>
                </div>
                <!-- Bar lanes -->
                <?php if (!empty($weekBars[$wi])): ?>
                <div class="ld-week-bars" style="grid-template-rows: repeat(<?= max(array_column($weekBars[$wi], 'lane')) ?>, 22px);">
                    <?php foreach ($weekBars[$wi] as $bar):
                        $l = $bar['l'];
                        $color = $deviceColors[$l['device_id']] ?? 'var(--gray-500)';
                        $isRes     = $l['status'] === 'reserved';
                        $isPending = $l['status'] === 'pending';
                        $isOverdue = ($l['status'] === 'active' && $l['planned_end'] < $today);
                        $span  = $bar['col2'] - $bar['col1'] + 1;
                        // Shape class
                        if ($span === 1) {
                            $shape = $bar['rl'] && $bar['rr'] ? 'bar-single' : ($bar['rl'] ? 'bar-start' : ($bar['rr'] ? 'bar-end' : 'bar-mid'));
                        } else {
                            $shape = ($bar['rl'] ? 'bar-start' : 'bar-cont-l');
                            if (!$bar['rr']) $shape .= ' bar-cont-r';
                        }
                        $barClasses = $shape;
                        if ($isRes)     $barClasses .= ' bar-reserved';
                        if ($isPending) $barClasses .= ' bar-pending';
                        if ($isOverdue) $barClasses .= ' bar-overdue';
                        $titlePrefix = $isPending ? 'Anfrage (Freigabe offen): ' : '';
                        $labelPrefix = $isPending ? '? ' : '';
                    ?>
                    <div class="ld-bar <?= $barClasses ?>"
                         style="grid-column:<?= $bar['col1'] ?>/<?= $bar['col2']+1 ?>; grid-row:<?= $bar['lane'] ?>; background:<?= $color ?>;">
                        <a href="/loans/lendings/<?= $l['id'] ?>"
                           style="background:<?= $color ?>;"
                           title="<?= $this->e($titlePrefix . $l['device_title']) ?> — <?= $this->e($l['borrower_name']) ?>">
                            <?= $this->e($labelPrefix . $l['device_title']) ?> · <?= $this->e($l['borrower_name']) ?>
                        </a>
                    </div>
                    <?php endforeach; ?>
                </div>
                <?php endif; ?>
            </div>
            <?php endforeach; ?>
        </div></div><!-- /ld-calendar-wrap -->

        <!-- Legend – immer anzeigen -->
        <div class="ld-legend">
            <span style="font-size:0.75rem;font-weight:700;color:var(--gray-500);text-transform:uppercase;letter-spacing:0.05em;align-self:center;"><?= $this->te('Legende:') ?></span>
            <?php
            $seenDevices = [];
            foreach ($lendings as $l) {
                if (isset($seenDevices[$l['device_id']])) continue;
                $seenDevices[$l['device_id']] = $l;
            }
            foreach ($seenDevices as $devId => $l):
                $col = $deviceColors[$devId] ?? 'var(--gray-500)';
            ?>
                <a href="/loans/devices/<?= $devId ?>" class="ld-legend-item">
                    <span class="ld-legend-dot" style="background:<?= $col ?>"></span>
                    <span class="ld-legend-device"><?= $this->e($l['device_title']) ?></span>
                    <span class="ld-legend-sep">·</span>
                    <span style="color:var(--gray-500)"><?= $this->e($l['device_type']) ?></span>
                </a>
            <?php endforeach; ?>
            <div style="margin-left:auto;display:flex;gap:0.75rem;align-items:center;">
                <span style="font-size:0.72rem;color:var(--gray-500);display:flex;align-items:center;gap:0.3rem;">
                    <span style="width:16px;height:10px;border-radius:2px;background:var(--gray-500);opacity:0.8;display:inline-block;"></span>
                    <?= $this->te('Aktiv') ?>
                </span>
                <span style="font-size:0.72rem;color:var(--gray-500);display:flex;align-items:center;gap:0.3rem;">
                    <span style="width:16px;height:10px;border-radius:2px;border:1.5px dashed var(--gray-500);display:inline-block;"></span>
                    <?= $this->te('Reserviert') ?>
                </span>
                <span style="font-size:0.72rem;color:var(--red-600);display:flex;align-items:center;gap:0.3rem;font-weight:600;">
                    <span style="width:16px;height:10px;border-radius:2px;background:var(--red-600);display:inline-block;"></span>
                    <?= $this->te('Überfällig') ?>
                </span>
            </div>
        </div>

        <?php if (empty($lendings)): ?>
        <div style="text-align:center;padding:3rem 1rem;color:var(--gray-400);">
            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="margin:0 auto 1rem;display:block;opacity:0.4"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
            <p style="font-size:0.95rem;font-weight:600;"><?= $this->te('Keine Ausleihen in diesem Monat') ?></p>
            <?php if ($can_issue): ?>
                <a href="/loans/issue" style="display:inline-block;margin-top:0.75rem;color:var(--amber-600);font-weight:600;font-size:0.85rem;"><?= $this->te('Gerät ausgeben &rarr;') ?></a>
            <?php endif; ?>
        </div>
        <?php endif; ?>
    </div>
</div>