777 lines
34 KiB
PHP
777 lines
34 KiB
PHP
|
||
|
||
<?php $__env->startSection('content'); ?>
|
||
<style>
|
||
/* ── Filter Periode ── */
|
||
.period-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
|
||
.period-btn {
|
||
padding:6px 14px; border-radius:20px; border:1px solid #e2e8f0;
|
||
background:#fff; cursor:pointer; font-size:0.85rem; transition:all .2s;
|
||
}
|
||
.period-btn:hover, .period-btn.active {
|
||
background:var(--primary-color); color:#fff; border-color:var(--primary-color);
|
||
}
|
||
.custom-range { display:none; align-items:center; gap:8px; }
|
||
.custom-range.show { display:flex; }
|
||
.custom-range input[type=date] {
|
||
padding:5px 10px; border:1px solid #e2e8f0; border-radius:6px; font-size:0.85rem;
|
||
}
|
||
|
||
/* ── KPI Strip ── */
|
||
.kpi-strip {
|
||
display:grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap:12px;
|
||
margin-bottom:18px;
|
||
}
|
||
.kpi-card {
|
||
background:#fff; border-radius:10px; padding:14px 16px;
|
||
box-shadow:0 2px 8px rgba(0,0,0,.06);
|
||
border-top:3px solid transparent;
|
||
}
|
||
.kpi-card.kpi-hadir { border-color:#10B981; }
|
||
.kpi-card.kpi-alpa { border-color:#EF4444; }
|
||
.kpi-card.kpi-avg { border-color:#3B82F6; }
|
||
.kpi-card.kpi-warning{ border-color:#F59E0B; }
|
||
.kpi-card.kpi-info { border-color:#6366F1; }
|
||
.kpi-label { font-size:0.78rem; color:#6B7280; margin-bottom:4px; }
|
||
.kpi-value { font-size:1.45rem; font-weight:700; color:var(--primary-dark); line-height:1.1; }
|
||
.kpi-context { font-size:0.75rem; color:#9CA3AF; margin-top:4px; }
|
||
.kpi-trend { font-size:0.78rem; margin-top:5px; display:flex; align-items:center; gap:4px; }
|
||
.kpi-trend.up { color:#10B981; }
|
||
.kpi-trend.down { color:#EF4444; }
|
||
.kpi-trend.neutral { color:#6B7280; }
|
||
|
||
/* ── Dua kolom ── */
|
||
.two-col { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; }
|
||
.three-col { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
|
||
@media(max-width:900px){
|
||
.kpi-strip { grid-template-columns:repeat(2,1fr); }
|
||
.two-col { grid-template-columns:1fr; }
|
||
.three-col { grid-template-columns:1fr; }
|
||
}
|
||
|
||
/* ── Cards ── */
|
||
.panel {
|
||
background:#fff; border-radius:10px; padding:16px;
|
||
box-shadow:0 2px 8px rgba(0,0,0,.06);
|
||
}
|
||
.panel-title {
|
||
font-size:.93rem; font-weight:600; margin:0 0 12px;
|
||
display:flex; align-items:center; gap:8px;
|
||
}
|
||
.panel-title a { font-size:.78rem; margin-left:auto; color:var(--primary-color); font-weight:400; }
|
||
|
||
/* ── List item kegiatan ── */
|
||
.kg-item {
|
||
display:flex; align-items:center; gap:10px;
|
||
padding:8px 0; border-bottom:1px solid #f1f5f9; font-size:.84rem;
|
||
}
|
||
.kg-item:last-child { border-bottom:none; }
|
||
.kg-name { flex:1; font-weight:500; }
|
||
.kg-context { font-size:.76rem; color:#6B7280; }
|
||
.kg-persen {
|
||
font-weight:700; font-size:.9rem; min-width:52px; text-align:right;
|
||
}
|
||
|
||
/* ── Anomali ── */
|
||
.alert-row {
|
||
display:flex; align-items:flex-start; gap:10px;
|
||
padding:10px 12px; border-radius:8px; margin-bottom:6px;
|
||
font-size:.85rem; border-left:4px solid transparent;
|
||
}
|
||
.alert-row:last-child { margin-bottom:0; }
|
||
.alert-row.a-danger { background:#FEF2F2; border-color:#EF4444; color:#991B1B; }
|
||
.alert-row.a-warning { background:#FFFBEB; border-color:#F59E0B; color:#92400E; }
|
||
.alert-row.a-info { background:#EFF6FF; border-color:#3B82F6; color:#1E40AF; }
|
||
.alert-row.a-success { background:#ECFDF5; border-color:#10B981; color:#065F46; }
|
||
.alert-text { flex:1; }
|
||
.alert-title { font-weight:600; font-size:.86rem; }
|
||
.alert-desc { font-size:.8rem; opacity:.85; margin-top:2px; }
|
||
|
||
/* ── Kelas chart legend ── */
|
||
.kelas-legend { display:flex; gap:16px; flex-wrap:wrap; margin-top:10px; font-size:.78rem; }
|
||
.kelas-legend-item { display:flex; align-items:center; gap:5px; color:#6B7280; }
|
||
.kelas-legend-dot { width:10px; height:10px; border-radius:50%; flex-shrink:0; }
|
||
|
||
/* ── Santri perlu perhatian ── */
|
||
.spp-item {
|
||
display:flex; align-items:center; gap:10px;
|
||
padding:7px 0; border-bottom:1px solid #f1f5f9; font-size:.84rem;
|
||
}
|
||
.spp-item:last-child { border-bottom:none; }
|
||
.spp-rank { width:22px; text-align:center; color:#9CA3AF; font-size:.78rem; }
|
||
.spp-name { flex:1; font-weight:500; }
|
||
.spp-pct { font-weight:700; color:#EF4444; }
|
||
.spp-ctx { font-size:.75rem; color:#9CA3AF; }
|
||
|
||
/* ── Trend chart ── */
|
||
.chart-wrap { background:#fff; border-radius:10px; padding:16px; box-shadow:0 2px 8px rgba(0,0,0,.06); margin-bottom:16px; }
|
||
.chart-wrap h4 { margin:0 0 10px; font-size:.93rem; color:var(--primary-dark); }
|
||
</style>
|
||
|
||
|
||
<div class="page-header">
|
||
<h2><i class="fas fa-chart-line"></i> Laporan Kegiatan</h2>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="content-box" style="margin-bottom:16px;">
|
||
<form method="GET" id="periodForm" action="<?php echo e(route('admin.laporan-kegiatan.index')); ?>">
|
||
<div class="period-bar">
|
||
<span style="font-size:.85rem;color:var(--text-light);">
|
||
<i class="fas fa-calendar-alt"></i> Periode:
|
||
</span>
|
||
<?php $__currentLoopData = ['hari_ini'=>'Hari Ini','minggu_ini'=>'Minggu Ini','bulan_ini'=>'Bulan Ini','semester_ini'=>'Semester','custom'=>'Custom']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key=>$label): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
||
<button type="button"
|
||
class="period-btn <?php echo e($periode===$key?'active':''); ?>"
|
||
onclick="setPeriode('<?php echo e($key); ?>')"><?php echo e($label); ?></button>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
||
<div class="custom-range <?php echo e($periode==='custom'?'show':''); ?>" id="customRange">
|
||
<input type="date" name="tanggal_dari"
|
||
value="<?php echo e(request('tanggal_dari', $startDate->format('Y-m-d'))); ?>">
|
||
<span>–</span>
|
||
<input type="date" name="tanggal_sampai"
|
||
value="<?php echo e(request('tanggal_sampai', $endDate->format('Y-m-d'))); ?>">
|
||
</div>
|
||
<input type="hidden" name="periode" id="periodeInput" value="<?php echo e($periode); ?>">
|
||
<button type="submit" class="btn btn-primary btn-sm">
|
||
<i class="fas fa-sync-alt"></i> Terapkan
|
||
</button>
|
||
</div>
|
||
</form>
|
||
<p style="margin:8px 0 0;font-size:.82rem;color:var(--text-light);">
|
||
<i class="fas fa-info-circle"></i> Data: <strong><?php echo e($periodeLabel); ?></strong>
|
||
</p>
|
||
</div>
|
||
|
||
|
||
<div class="kpi-strip">
|
||
|
||
|
||
<div class="kpi-card kpi-avg">
|
||
<div class="kpi-label">Rata-rata Kehadiran</div>
|
||
<div class="kpi-value"><?php echo e($kpi['avg_kehadiran']); ?>%</div>
|
||
<div class="kpi-context">
|
||
<?php echo e(number_format($kpi['total_hadir'])); ?> hadir dari
|
||
<?php echo e(number_format($kpi['total_absensi'])); ?> tercatat
|
||
</div>
|
||
<?php $diffAvg = $kpiComparison['avg_kehadiran']; ?>
|
||
<div class="kpi-trend <?php echo e($diffAvg > 0 ? 'up' : ($diffAvg < 0 ? 'down' : 'neutral')); ?>">
|
||
<i class="fas fa-arrow-<?php echo e($diffAvg >= 0 ? 'up' : 'down'); ?>"></i>
|
||
<?php echo e(abs($diffAvg)); ?>% vs periode sebelumnya
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="kpi-card kpi-hadir">
|
||
<div class="kpi-label">Hadir Efektif</div>
|
||
<div class="kpi-value"><?php echo e(number_format($kpi['total_hadir'])); ?></div>
|
||
<div class="kpi-context">
|
||
termasuk <?php echo e($kpi['total_terlambat']); ?> terlambat
|
||
— dari <?php echo e(number_format($kpi['total_absensi'])); ?> sesi tercatat
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="kpi-card kpi-alpa">
|
||
<div class="kpi-label">Total Alpa</div>
|
||
<div class="kpi-value"><?php echo e(number_format($kpi['total_alpa'])); ?></div>
|
||
<div class="kpi-context">
|
||
<?php $alpaRate = $kpi['total_absensi'] > 0 ? round($kpi['total_alpa']/$kpi['total_absensi']*100,1) : 0; ?>
|
||
<?php echo e($alpaRate); ?>% dari <?php echo e(number_format($kpi['total_absensi'])); ?> tercatat
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="kpi-card kpi-warning">
|
||
<div class="kpi-label">Santri Perlu Perhatian</div>
|
||
<div class="kpi-value"><?php echo e($kpi['santri_perlu_perhatian']); ?></div>
|
||
<div class="kpi-context">
|
||
dari <?php echo e($kpi['total_santri_aktif']); ?> santri aktif
|
||
(kehadiran <70%)
|
||
</div>
|
||
<?php $diffSpp = $kpiComparison['santri_perlu_perhatian']; ?>
|
||
<div class="kpi-trend <?php echo e($diffSpp <= 0 ? 'up' : 'down'); ?>">
|
||
<i class="fas fa-arrow-<?php echo e($diffSpp <= 0 ? 'down' : 'up'); ?>"></i>
|
||
<?php echo e(abs($diffSpp)); ?> vs periode sebelumnya
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="kpi-card kpi-info">
|
||
<div class="kpi-label">Kehadiran Terbaik</div>
|
||
<div class="kpi-value" style="font-size:1rem;">
|
||
<?php echo e($kpi['kegiatan_terbaik']['nama']); ?>
|
||
|
||
</div>
|
||
<div class="kpi-context">
|
||
<?php echo e($kpi['kegiatan_terbaik']['persen']); ?>%
|
||
(<?php echo e($kpi['kegiatan_terbaik']['hadir']); ?> hadir dari <?php echo e($kpi['kegiatan_terbaik']['total']); ?> tercatat)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<?php
|
||
$buckets = $distribusiSantri['buckets'];
|
||
$totalDengan = $distribusiSantri['total'];
|
||
?>
|
||
|
||
<div class="chart-wrap" style="margin-bottom:16px;">
|
||
<div style="display:flex;align-items:flex-start;gap:24px;flex-wrap:wrap;">
|
||
|
||
|
||
<div style="position:relative;width:200px;height:200px;flex-shrink:0;">
|
||
<canvas id="distribusiChart"></canvas>
|
||
|
||
<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;">
|
||
<div style="font-size:1.6rem;font-weight:700;color:var(--primary-dark);line-height:1;">
|
||
<?php echo e($totalDengan); ?>
|
||
|
||
</div>
|
||
<div style="font-size:.72rem;color:#6B7280;margin-top:2px;">santri<br>tercatat</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div style="flex:1;min-width:220px;">
|
||
<h4 style="margin:0 0 6px;font-size:.93rem;color:var(--primary-dark);">
|
||
<i class="fas fa-users"></i> Kondisi Kehadiran Santri
|
||
</h4>
|
||
|
||
|
||
<?php $__currentLoopData = $buckets; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $b): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;">
|
||
<div style="width:12px;height:12px;border-radius:50%;background:<?php echo e($b['color']); ?>;flex-shrink:0;"></div>
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:3px;">
|
||
<span style="font-size:.83rem;font-weight:600;color:var(--primary-dark);">
|
||
<?php echo e($b['label']); ?>
|
||
|
||
<?php if($b['label']==='Sangat Baik'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(≥95%)</span>
|
||
<?php elseif($b['label']==='Baik'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(85–94%)</span>
|
||
<?php elseif($b['label']==='Cukup'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(70–84%)</span>
|
||
<?php else: ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(<70%)</span>
|
||
<?php endif; ?>
|
||
</span>
|
||
<span style="font-size:.83rem;font-weight:700;color:<?php echo e($b['color']); ?>;">
|
||
<?php echo e($b['count']); ?> santri
|
||
<span style="font-weight:400;color:#9CA3AF;">(<?php echo e($b['persen']); ?>%)</span>
|
||
</span>
|
||
</div>
|
||
<div style="background:#f1f5f9;border-radius:4px;height:6px;overflow:hidden;">
|
||
<div style="width:<?php echo e($b['persen']); ?>%;height:100%;background:<?php echo e($b['color']); ?>;border-radius:4px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
||
|
||
<?php $tanpaData = $kpi['total_santri_aktif'] - $totalDengan; ?>
|
||
<?php if($tanpaData > 0): ?>
|
||
<p style="margin:6px 0 0;font-size:.76rem;color:#9CA3AF;">
|
||
<i class="fas fa-info-circle"></i>
|
||
<?php echo e($tanpaData); ?> santri aktif belum punya data absensi di periode ini.
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="panel" style="margin-bottom:16px;">
|
||
<div class="panel-title" style="color:#EF4444;">
|
||
<i class="fas fa-bell"></i> Anomali & Perlu Tindak Lanjut
|
||
</div>
|
||
<?php if(!empty($patterns) && count($patterns) > 0): ?>
|
||
<?php $__currentLoopData = $patterns; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $p): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
||
<div class="alert-row a-<?php echo e($p['type']); ?>">
|
||
<?php if($p['type']==='danger'): ?>
|
||
<i class="fas fa-exclamation-circle" style="margin-top:2px;"></i>
|
||
<?php elseif($p['type']==='warning'): ?>
|
||
<i class="fas fa-exclamation-triangle" style="margin-top:2px;"></i>
|
||
<?php else: ?>
|
||
<i class="fas fa-info-circle" style="margin-top:2px;"></i>
|
||
<?php endif; ?>
|
||
<div class="alert-text">
|
||
<div class="alert-title"><?php echo e($p['title']); ?></div>
|
||
<div class="alert-desc"><?php echo e($p['description']); ?></div>
|
||
</div>
|
||
<?php if(!empty($p['action_url'])): ?>
|
||
<a href="<?php echo e($p['action_url']); ?>"
|
||
class="btn btn-sm btn-<?php echo e($p['type']==='danger'?'danger':($p['type']==='warning'?'warning':'info')); ?>"
|
||
style="white-space:nowrap;">
|
||
<?php echo e($p['action_text'] ?? 'Lihat'); ?>
|
||
|
||
</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
||
<?php else: ?>
|
||
<div class="alert-row a-success">
|
||
<i class="fas fa-check-circle"></i>
|
||
<div class="alert-text">
|
||
<div class="alert-title">Tidak ada anomali</div>
|
||
<div class="alert-desc">Pola kehadiran dalam kondisi normal.</div>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
|
||
<?php
|
||
$trendLabels = $trendData['labels'] ?? [];
|
||
$trendDs = $trendData['datasets'] ?? [];
|
||
$avgLine = [];
|
||
foreach ($trendLabels as $i => $_) {
|
||
$sum=0; $cnt=0;
|
||
foreach ($trendDs as $ds) {
|
||
if (isset($ds['data'][$i]) && $ds['data'][$i] !== null) { $sum+=$ds['data'][$i]; $cnt++; }
|
||
}
|
||
$avgLine[] = $cnt > 0 ? round($sum/$cnt,1) : null;
|
||
}
|
||
?>
|
||
|
||
<div class="chart-wrap">
|
||
<h4><i class="fas fa-chart-line"></i> Trend Kehadiran Rata-rata</h4>
|
||
<p style="margin:0 0 10px;font-size:.8rem;color:#6B7280;">
|
||
Angka = % hadir efektif (Terlambat dihitung Hadir). Hover untuk detail.
|
||
</p>
|
||
<div style="position:relative;height:200px;">
|
||
<canvas id="trendChart"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="three-col">
|
||
|
||
|
||
<div class="panel">
|
||
<div class="panel-title" style="color:#10B981;">
|
||
<i class="fas fa-trophy"></i> Kehadiran Terbaik
|
||
</div>
|
||
<?php $__empty_1 = true; $__currentLoopData = $topKegiatan; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $kg): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
|
||
<div class="kg-item">
|
||
|
||
<span style="color:#9CA3AF;font-size:.75rem;width:16px;flex-shrink:0;text-align:center;">
|
||
<?php echo e($i + 1); ?>
|
||
|
||
</span>
|
||
|
||
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
|
||
<div class="kg-name"
|
||
style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.84rem;font-weight:600;">
|
||
<?php echo e($kg['nama_kegiatan']); ?>
|
||
|
||
</div>
|
||
<span style="font-weight:700;color:#10B981;font-size:.88rem;flex-shrink:0;">
|
||
<?php echo e($kg['persen']); ?>%
|
||
</span>
|
||
</div>
|
||
|
||
|
||
<div style="background:#f1f5f9;border-radius:4px;height:6px;overflow:hidden;margin-bottom:4px;">
|
||
<div style="width:<?php echo e($kg['persen']); ?>%;height:100%;background:#10B981;border-radius:4px;transition:width .5s;"></div>
|
||
</div>
|
||
|
||
|
||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||
<span class="kg-context">
|
||
<?php echo e($kg['nama_kategori'] ?? '-'); ?> · <?php echo e($kg['hari'] ?? '-'); ?>
|
||
|
||
</span>
|
||
<span style="font-size:.74rem;color:#10B981;">
|
||
<?php echo e($kg['hadir']); ?>/<?php echo e($kg['total']); ?> sesi
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
|
||
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:16px 0;">
|
||
Belum ada data absensi di periode ini.
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
|
||
<div class="panel">
|
||
<div class="panel-title" style="color:#EF4444;">
|
||
<i class="fas fa-arrow-trend-down"></i> Kehadiran Terendah
|
||
</div>
|
||
<?php $__empty_1 = true; $__currentLoopData = $bottomKegiatan; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $kg): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
|
||
<?php
|
||
$warna = $kg['persen'] < 70 ? '#EF4444' : '#F59E0B';
|
||
$bgBar = $kg['persen'] < 70 ? '#FEF2F2' : '#FFFBEB';
|
||
$kgId = $kg['id'];
|
||
?>
|
||
<div class="kg-item">
|
||
<span style="color:#9CA3AF;font-size:.75rem;width:16px;flex-shrink:0;text-align:center;">
|
||
<?php echo e($i + 1); ?>
|
||
|
||
</span>
|
||
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
|
||
<a href="<?php echo e(route('admin.riwayat-kegiatan.show', $kgId)); ?>"
|
||
style="text-decoration:none;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.84rem;font-weight:600;flex:1;min-width:0;">
|
||
<?php echo e($kg['nama_kegiatan']); ?>
|
||
|
||
</a>
|
||
<span style="font-weight:700;color:<?php echo e($warna); ?>;font-size:.88rem;flex-shrink:0;">
|
||
<?php echo e($kg['persen']); ?>%
|
||
</span>
|
||
</div>
|
||
|
||
|
||
<div style="background:<?php echo e($bgBar); ?>;border-radius:4px;height:6px;overflow:hidden;margin-bottom:4px;">
|
||
<div style="width:<?php echo e($kg['persen']); ?>%;height:100%;background:<?php echo e($warna); ?>;border-radius:4px;transition:width .5s;"></div>
|
||
</div>
|
||
|
||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||
<span class="kg-context">
|
||
<?php echo e($kg['nama_kategori'] ?? '-'); ?> · <?php echo e($kg['hari'] ?? '-'); ?>
|
||
|
||
</span>
|
||
<span style="font-size:.74rem;color:<?php echo e($warna); ?>;">
|
||
<?php echo e($kg['hadir']); ?>/<?php echo e($kg['total']); ?> sesi
|
||
<?php if(($kg['alpa'] ?? 0) > 0): ?>
|
||
· <strong><?php echo e($kg['alpa']); ?>× alpa</strong>
|
||
<?php endif; ?>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
|
||
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:16px 0;">
|
||
Belum ada data absensi di periode ini.
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
|
||
<div class="panel">
|
||
<div class="panel-title" style="color:#F59E0B;">
|
||
<i class="fas fa-user-clock"></i> Santri Perlu Perhatian
|
||
<a href="<?php echo e(route('admin.laporan-kegiatan.santri-perlu-perhatian', request()->query())); ?>">
|
||
Lihat Semua →
|
||
</a>
|
||
</div>
|
||
<p style="font-size:.78rem;color:#6B7280;margin:0 0 10px;">
|
||
<?php echo e($kpi['santri_perlu_perhatian']); ?> dari <?php echo e($kpi['total_santri_aktif']); ?> santri aktif
|
||
dengan kehadiran <70%
|
||
</p>
|
||
<?php $__empty_1 = true; $__currentLoopData = $santriPerluPerhatianList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
|
||
<div class="spp-item">
|
||
<span class="spp-rank"><?php echo e($i + 1); ?></span>
|
||
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
|
||
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $s->id_santri)); ?>"
|
||
class="spp-name"
|
||
style="text-decoration:none;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;">
|
||
<?php echo e($s->nama_lengkap); ?>
|
||
|
||
</a>
|
||
<span class="spp-pct" style="flex-shrink:0;"><?php echo e($s->persen); ?>%</span>
|
||
</div>
|
||
|
||
|
||
<div style="background:#FEF2F2;border-radius:4px;height:6px;overflow:hidden;margin-bottom:3px;">
|
||
<div style="width:<?php echo e($s->persen); ?>%;height:100%;background:#EF4444;border-radius:4px;transition:width .5s;"></div>
|
||
</div>
|
||
|
||
<div class="spp-ctx">
|
||
<?php echo e($s->hadir); ?>/<?php echo e($s->total); ?> sesi · <?php echo e($s->alpa); ?>× alpa
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
|
||
<div style="padding:12px;background:#ECFDF5;border-radius:8px;color:#065F46;
|
||
font-size:.85rem;display:flex;align-items:center;gap:8px;margin-top:4px;">
|
||
<i class="fas fa-check-circle"></i> Semua santri kehadiran ≥70%. Alhamdulillah!
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<?php
|
||
/* Flatten semua kelas dari semua kelompok untuk chart */
|
||
$chartKelasLabels = [];
|
||
$chartKelasData = [];
|
||
$chartKelasColors = [];
|
||
$chartKelasContext = []; // untuk tooltip: "hadir / total"
|
||
$chartKelasGroups = []; // label kelompok per kelas
|
||
|
||
foreach ($kelasRingkasan as $kelompok) {
|
||
foreach ($kelompok['kelas'] as $k) {
|
||
$chartKelasLabels[] = $k['nama_kelas'];
|
||
$chartKelasData[] = $k['persen'];
|
||
$chartKelasColors[] = $k['persen'] >= 85 ? '#10B981' : ($k['persen'] >= 70 ? '#F59E0B' : '#EF4444');
|
||
$chartKelasContext[] = $k['hadir'] . ' hadir dari ' . $k['total'] . ' tercatat';
|
||
$chartKelasGroups[] = $kelompok['nama_kelompok'];
|
||
}
|
||
}
|
||
$chartHeight = max(180, count($chartKelasLabels) * 34 + 40);
|
||
?>
|
||
|
||
<div class="chart-wrap" style="margin-bottom:16px;">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;flex-wrap:wrap;gap:8px;">
|
||
<h4 style="margin:0;">
|
||
<i class="fas fa-school"></i> Kehadiran Per Kelas
|
||
</h4>
|
||
<a href="<?php echo e(route('admin.riwayat-kegiatan.index')); ?>"
|
||
style="font-size:.78rem;color:var(--primary-color);">
|
||
Detail di Riwayat Kegiatan →
|
||
</a>
|
||
</div>
|
||
<p style="margin:0 0 12px;font-size:.78rem;color:#6B7280;">
|
||
% hadir efektif — Terlambat dihitung Hadir. Hover bar untuk melihat angka lengkap.
|
||
</p>
|
||
|
||
<?php if(count($chartKelasLabels) > 0): ?>
|
||
<div style="position:relative;height:<?php echo e($chartHeight); ?>px;">
|
||
<canvas id="kelasChart"></canvas>
|
||
</div>
|
||
<div class="kelas-legend">
|
||
<div class="kelas-legend-item">
|
||
<div class="kelas-legend-dot" style="background:#10B981;"></div> ≥85% Baik
|
||
</div>
|
||
<div class="kelas-legend-item">
|
||
<div class="kelas-legend-dot" style="background:#F59E0B;"></div> 70–84% Cukup
|
||
</div>
|
||
<div class="kelas-legend-item">
|
||
<div class="kelas-legend-dot" style="background:#EF4444;"></div> <70% Perlu Perhatian
|
||
</div>
|
||
</div>
|
||
<?php else: ?>
|
||
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:20px 0;">
|
||
Belum ada data kelas untuk periode ini.
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||
<script>
|
||
function setPeriode(p) {
|
||
document.getElementById('periodeInput').value = p;
|
||
document.querySelectorAll('.period-btn').forEach(b => b.classList.remove('active'));
|
||
event.target.classList.add('active');
|
||
const cr = document.getElementById('customRange');
|
||
if (p === 'custom') { cr.classList.add('show'); }
|
||
else { cr.classList.remove('show'); document.getElementById('periodForm').submit(); }
|
||
}
|
||
|
||
// ── Distribusi Santri Donut Chart ──
|
||
const distribusiBuckets = <?php echo json_encode($buckets, 15, 512) ?>;
|
||
new Chart(document.getElementById('distribusiChart'), {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: distribusiBuckets.map(b => b.label),
|
||
datasets: [{
|
||
data: distribusiBuckets.map(b => b.count),
|
||
backgroundColor: distribusiBuckets.map(b => b.color),
|
||
borderWidth: 2,
|
||
borderColor: '#fff',
|
||
hoverOffset: 8,
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
cutout: '68%',
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
callbacks: {
|
||
label: (ctx) => {
|
||
const b = distribusiBuckets[ctx.dataIndex];
|
||
return [
|
||
` ${b.count} santri (${b.persen}%)`,
|
||
` dari <?php echo e($totalDengan); ?> santri tercatat`,
|
||
];
|
||
}
|
||
},
|
||
backgroundColor: 'rgba(17,24,39,.9)',
|
||
titleFont: { size: 13, weight: '600' },
|
||
bodyFont: { size: 12 },
|
||
padding: 10,
|
||
cornerRadius: 8,
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
|
||
const labels = <?php echo json_encode($trendLabels, 15, 512) ?>;
|
||
const avgLine = <?php echo json_encode($avgLine, 15, 512) ?>;
|
||
|
||
new Chart(document.getElementById('trendChart'), {
|
||
type: 'line',
|
||
data: {
|
||
labels,
|
||
datasets: [{
|
||
label: 'Rata-rata Kehadiran',
|
||
data: avgLine,
|
||
borderColor: '#10B981',
|
||
backgroundColor: 'rgba(16,185,129,.1)',
|
||
tension: 0.4, fill: true,
|
||
pointRadius: 4, pointHoverRadius: 6,
|
||
spanGaps: true,
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true, maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
callbacks: {
|
||
label: ctx => {
|
||
const v = ctx.parsed.y;
|
||
return v !== null ? `${v}% kehadiran rata-rata` : 'Tidak ada data';
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
min: 0, max: 100,
|
||
ticks: { callback: v => v + '%' },
|
||
grid: { color: '#f1f5f9' }
|
||
},
|
||
x: { grid: { display: false } }
|
||
}
|
||
}
|
||
});
|
||
|
||
// ── Horizontal Bar Chart: Kehadiran Per Kelas ──
|
||
<?php if(count($chartKelasLabels) > 0): ?>
|
||
const kelasLabels = <?php echo json_encode($chartKelasLabels, 15, 512) ?>;
|
||
const kelasData = <?php echo json_encode($chartKelasData, 15, 512) ?>;
|
||
const kelasColors = <?php echo json_encode($chartKelasColors, 15, 512) ?>;
|
||
const kelasContext = <?php echo json_encode($chartKelasContext, 15, 512) ?>;
|
||
const kelasGroups = <?php echo json_encode($chartKelasGroups, 15, 512) ?>;
|
||
|
||
new Chart(document.getElementById('kelasChart'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: kelasLabels,
|
||
datasets: [{
|
||
label: '% Kehadiran',
|
||
data: kelasData,
|
||
backgroundColor: kelasColors,
|
||
borderRadius: 5,
|
||
borderSkipped: false,
|
||
barThickness: 20,
|
||
}]
|
||
},
|
||
options: {
|
||
indexAxis: 'y', // horizontal
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
callbacks: {
|
||
title: (items) => {
|
||
const i = items[0].dataIndex;
|
||
return `${kelasLabels[i]} — ${kelasGroups[i]}`;
|
||
},
|
||
label: (ctx) => {
|
||
const i = ctx.dataIndex;
|
||
return [
|
||
`Kehadiran: ${ctx.parsed.x}%`,
|
||
kelasContext[i],
|
||
];
|
||
},
|
||
labelColor: (ctx) => ({
|
||
borderColor: kelasColors[ctx.dataIndex],
|
||
backgroundColor: kelasColors[ctx.dataIndex],
|
||
borderRadius: 3,
|
||
}),
|
||
},
|
||
backgroundColor: 'rgba(17,24,39,.9)',
|
||
titleFont: { size: 13, weight: '600' },
|
||
bodyFont: { size: 12 },
|
||
padding: 10,
|
||
cornerRadius: 8,
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
min: 0, max: 100,
|
||
ticks: { callback: v => v + '%', font: { size: 11 } },
|
||
grid: { color: '#f1f5f9' },
|
||
// Garis referensi 70% dan 85%
|
||
},
|
||
y: {
|
||
ticks: { font: { size: 11 } },
|
||
grid: { display: false },
|
||
}
|
||
},
|
||
// Plugin untuk garis referensi 70% dan 85%
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
callbacks: {
|
||
title: (items) => {
|
||
const i = items[0].dataIndex;
|
||
return `${kelasLabels[i]} — ${kelasGroups[i]}`;
|
||
},
|
||
label: (ctx) => {
|
||
const i = ctx.dataIndex;
|
||
return [
|
||
`Kehadiran: ${ctx.parsed.x}%`,
|
||
kelasContext[i],
|
||
];
|
||
},
|
||
},
|
||
backgroundColor: 'rgba(17,24,39,.9)',
|
||
titleFont: { size: 13, weight: '600' },
|
||
bodyFont: { size: 12 },
|
||
padding: 10,
|
||
cornerRadius: 8,
|
||
},
|
||
annotation: undefined,
|
||
},
|
||
},
|
||
plugins: [{
|
||
// Plugin custom: garis vertikal referensi 70% dan 85%
|
||
id: 'refLines',
|
||
afterDraw(chart) {
|
||
const { ctx, chartArea: { top, bottom }, scales: { x } } = chart;
|
||
[{ v: 70, color: '#F59E0B', label: '70%' }, { v: 85, color: '#10B981', label: '85%' }].forEach(ref => {
|
||
const xPx = x.getPixelForValue(ref.v);
|
||
ctx.save();
|
||
ctx.beginPath();
|
||
ctx.setLineDash([4, 4]);
|
||
ctx.moveTo(xPx, top);
|
||
ctx.lineTo(xPx, bottom);
|
||
ctx.strokeStyle = ref.color;
|
||
ctx.lineWidth = 1.5;
|
||
ctx.globalAlpha = 0.6;
|
||
ctx.stroke();
|
||
ctx.globalAlpha = 1;
|
||
ctx.fillStyle = ref.color;
|
||
ctx.font = '10px sans-serif';
|
||
ctx.fillText(ref.label, xPx + 3, top + 10);
|
||
ctx.restore();
|
||
});
|
||
}
|
||
}]
|
||
});
|
||
<?php endif; ?>
|
||
</script>
|
||
|
||
<style>
|
||
@media print {
|
||
.period-bar, .btn, button { display:none !important; }
|
||
.panel, .chart-wrap { box-shadow:none !important; border:1px solid #e2e8f0; }
|
||
}
|
||
</style>
|
||
<?php $__env->stopSection(); ?>
|
||
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/laporan/index.blade.php ENDPATH**/ ?>
|