MIF_E31230892/sim-pkpps/resources/views/admin/kegiatan/riwayat/detail-santri.blade.php

407 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{-- resources/views/admin/kegiatan/riwayat/detail-santri.blade.php --}}
@extends('layouts.app')
@section('content')
<style>
/* =====================================================
DETAIL SANTRI — Riwayat Kehadiran
===================================================== */
.ds-header { display:flex; justify-content:space-between; align-items:flex-start; gap:12px;
margin-bottom:18px; flex-wrap:wrap; }
.ds-header h2 { margin:0; font-size:1.3rem; color:var(--primary-dark); display:flex; align-items:center; gap:9px; }
/* Info santri */
.ds-info-box { background:#fff; border-radius:12px; padding:16px 20px; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); display:flex; justify-content:space-between;
align-items:center; flex-wrap:wrap; gap:12px; }
.ds-info-box h3 { margin:0 0 5px; color:var(--primary-color); font-size:1.1rem; }
.ds-info-meta { font-size:0.83rem; color:#64748b; display:flex; flex-wrap:wrap; gap:10px; }
.ds-info-meta span { display:inline-flex; align-items:center; gap:4px; }
/* KPI cards */
.ds-kpi-grid { display:grid; grid-template-columns:repeat(5,1fr); gap:10px; margin-bottom:14px; }
@media(max-width:900px) { .ds-kpi-grid { grid-template-columns:repeat(3,1fr); } }
@media(max-width:540px) { .ds-kpi-grid { grid-template-columns:repeat(2,1fr); } }
.ds-kpi { background:#fff; border-radius:10px; padding:12px 14px;
box-shadow:0 2px 8px rgba(0,0,0,0.06); display:flex; align-items:center; gap:10px; }
.ds-kpi .ico { width:36px; height:36px; border-radius:9px; display:flex; align-items:center;
justify-content:center; font-size:1.1rem; flex-shrink:0; }
.ds-kpi.hadir .ico { background:#d1fae5; color:#065f46; }
.ds-kpi.terlambat .ico { background:#fff3e0; color:#e65100; }
.ds-kpi.izin .ico { background:#fef3c7; color:#92400e; }
.ds-kpi.sakit .ico { background:#dbeafe; color:#1e40af; }
.ds-kpi.alpa .ico { background:#fee2e2; color:#991b1b; }
.ds-kpi .cnt { font-size:1.4rem; font-weight:800; color:var(--primary-dark); line-height:1; }
.ds-kpi .lbl { font-size:0.74rem; color:#94a3b8; margin-top:2px; }
/* Terlambat note */
.ds-terlambat-note { font-size:0.76rem; color:#6b7280; background:#fff7ed; border:1px solid #fed7aa;
border-radius:8px; padding:6px 12px; margin-bottom:14px;
display:flex; align-items:center; gap:6px; }
.ds-terlambat-note i { color:#ea580c; }
/* Tren chart box */
.ds-chart-box { background:#fff; border-radius:12px; padding:16px 18px; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); }
.ds-chart-box h4 { margin:0 0 14px; color:var(--primary-color); font-size:0.95rem;
display:flex; align-items:center; gap:7px; }
/* Riwayat lengkap dengan tabs */
.ds-riwayat-box { background:#fff; border-radius:12px; overflow:hidden; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); }
.ds-riwayat-head { padding:14px 18px 0; border-bottom:2px solid #f1f5f9; }
.ds-riwayat-head h4 { margin:0 0 12px; color:var(--primary-dark); font-size:0.95rem;
display:flex; align-items:center; gap:7px; }
/* Tabs */
.ds-tabs { display:flex; gap:0; overflow-x:auto; -webkit-overflow-scrolling:touch; }
.ds-tabs::-webkit-scrollbar { height:0; }
.ds-tab { padding:9px 16px; font-size:0.82rem; font-weight:600; border:none; background:transparent;
cursor:pointer; white-space:nowrap; color:#64748b; border-bottom:3px solid transparent;
transition:all 0.15s; text-decoration:none; display:inline-flex; align-items:center; gap:6px; }
.ds-tab:hover { color:var(--primary-color); background:#f0fdf4; }
.ds-tab.active { color:var(--primary-color); border-bottom-color:var(--primary-color); background:#f0fdf4; }
.ds-tab .tab-cnt { background:#e8f7f2; color:var(--primary-color); padding:1px 7px; border-radius:10px;
font-size:0.72rem; font-weight:700; }
.ds-tab.active .tab-cnt { background:var(--primary-color); color:#fff; }
.ds-tab .tab-hadir { background:#d1fae5; color:#065f46; padding:1px 7px; border-radius:10px;
font-size:0.7rem; font-weight:700; }
/* Per-page toolbar */
.ds-toolbar { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;
gap:8px; padding:10px 18px; background:#fafafa; border-bottom:1px solid #f1f5f9; }
.ds-toolbar-left { font-size:0.8rem; color:#64748b; }
.ds-toolbar-right { display:flex; align-items:center; gap:8px; font-size:0.8rem; color:#64748b; }
.ds-perpage-group { display:flex; gap:4px; }
.ds-pp-btn { padding:4px 10px; border:1.5px solid #e2e8f0; border-radius:6px; background:#fff;
font-size:0.78rem; font-weight:600; color:#64748b; cursor:pointer;
text-decoration:none; transition:all 0.15s; }
.ds-pp-btn:hover { border-color:var(--primary-color); color:var(--primary-color); }
.ds-pp-btn.active { background:var(--primary-color); color:#fff; border-color:var(--primary-color); }
/* Table */
.ds-table-wrap { overflow-x:auto; }
.ds-table { width:100%; border-collapse:collapse; min-width:540px; }
.ds-table thead th { padding:9px 14px; text-align:left; font-size:0.78rem; font-weight:600;
color:#64748b; background:#f8fafc; border-bottom:1px solid #e2e8f0; }
.ds-table tbody td { padding:9px 14px; font-size:0.83rem; border-bottom:1px solid #f8fafc; vertical-align:middle; }
.ds-table tbody tr:last-child td { border-bottom:none; }
.ds-table tbody tr:hover { background:#f8fafc; }
/* Status badges (inline fallback jika status_badge tidak ada) */
.s-hadir { background:#d1fae5; color:#065f46; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-terlambat { background:#fff3e0; color:#e65100; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-izin { background:#fef3c7; color:#92400e; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-sakit { background:#dbeafe; color:#1e40af; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-alpa { background:#fee2e2; color:#991b1b; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-pulang { background:#f3e8ff; color:#6b21a8; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
/* Button back */
.btn-ds-back { padding:8px 16px; background:#6b7280; color:#fff; border-radius:8px; font-size:0.82rem;
font-weight:600; text-decoration:none; display:inline-flex; align-items:center; gap:6px;
transition:background 0.18s; }
.btn-ds-back:hover { background:#4b5563; color:#fff; }
/* Empty */
.ds-empty { text-align:center; padding:30px; color:#94a3b8; }
.ds-empty i { font-size:2.5rem; display:block; margin-bottom:10px; opacity:0.35; }
</style>
@php
$totalHadirEfektif = $stats['_hadir_efektif'] ?? 0;
$totalHadir = $stats['Hadir'] ?? 0;
$totalTerlambat = $stats['Terlambat'] ?? 0;
$totalIzin = $stats['Izin'] ?? 0;
$totalSakit = $stats['Sakit'] ?? 0;
$totalAlpa = $stats['Alpa'] ?? 0;
$persenHadir = ($totalHadirEfektif + $totalIzin + $totalSakit + $totalAlpa) > 0
? round($totalHadirEfektif / ($totalHadirEfektif + $totalIzin + $totalSakit + $totalAlpa) * 100, 1)
: 0;
@endphp
{{-- Header --}}
<div class="ds-header">
<h2><i class="fas fa-user-clock" style="color:var(--primary-color);"></i>
Riwayat Kehadiran: {{ $santri->nama_lengkap }}
</h2>
<a href="{{ route('admin.riwayat-kegiatan.index') }}" class="btn-ds-back">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
{{-- Info Santri --}}
<div class="ds-info-box">
<div>
<h3>{{ $santri->nama_lengkap }}</h3>
<div class="ds-info-meta">
<span><i class="fas fa-id-card"></i> <strong>{{ $santri->id_santri }}</strong></span>
<span><i class="fas fa-school"></i> {{ $santri->kelas }}</span>
<span><i class="fas fa-circle" style="font-size:0.5rem; color:#22c55e;"></i>
<span class="badge badge-success" style="font-size:0.76rem;">{{ $santri->status }}</span>
</span>
@if($persenHadir > 0)
<span style="font-weight:700; color:{{ $persenHadir >= 85 ? '#059669' : ($persenHadir >= 70 ? '#d97706' : '#dc2626') }};">
<i class="fas fa-chart-pie"></i> Kehadiran {{ $persenHadir }}%
</span>
@endif
</div>
</div>
</div>
{{-- Catatan logika Terlambat --}}
@if($totalTerlambat > 0)
<div class="ds-terlambat-note">
<i class="fas fa-info-circle"></i>
<strong>{{ $totalTerlambat }}x Terlambat</strong> dihitung sebagai <strong>Hadir</strong> (bukan Alpa).
Total hadir efektif: <strong>{{ $totalHadirEfektif }}</strong> kali
({{ $totalHadir }} hadir tepat waktu + {{ $totalTerlambat }} terlambat).
</div>
@endif
{{-- KPI Cards --}}
<div class="ds-kpi-grid">
<div class="ds-kpi hadir">
<div class="ico"><i class="fas fa-check-circle"></i></div>
<div>
<div class="cnt">{{ $totalHadirEfektif }}</div>
<div class="lbl">Hadir Efektif</div>
@if($totalTerlambat > 0)
<div style="font-size:0.68rem; color:#94a3b8;">{{ $totalHadir }}+{{ $totalTerlambat }}tl</div>
@endif
</div>
</div>
<div class="ds-kpi terlambat">
<div class="ico"><i class="fas fa-clock"></i></div>
<div><div class="cnt">{{ $totalTerlambat }}</div><div class="lbl">Terlambat</div></div>
</div>
<div class="ds-kpi izin">
<div class="ico"><i class="fas fa-envelope"></i></div>
<div><div class="cnt">{{ $totalIzin }}</div><div class="lbl">Izin</div></div>
</div>
<div class="ds-kpi sakit">
<div class="ico"><i class="fas fa-heartbeat"></i></div>
<div><div class="cnt">{{ $totalSakit }}</div><div class="lbl">Sakit</div></div>
</div>
<div class="ds-kpi alpa">
<div class="ico"><i class="fas fa-times-circle"></i></div>
<div><div class="cnt">{{ $totalAlpa }}</div><div class="lbl">Alpa</div></div>
</div>
</div>
{{-- Tren 30 Hari --}}
@if($riwayat30Hari->count() > 0)
<div class="ds-chart-box">
<h4><i class="fas fa-chart-line"></i> Tren Kehadiran 30 Hari Terakhir
<span style="font-size:0.76rem; font-weight:400; color:#94a3b8; margin-left:4px;">
(Hadir + Terlambat)
</span>
</h4>
<canvas id="chartTren" style="max-height:220px;"></canvas>
</div>
@endif
{{-- ── RIWAYAT LENGKAP (Tabbed per Kategori) ──────────────────────────────── --}}
<div class="ds-riwayat-box">
<div class="ds-riwayat-head">
<h4><i class="fas fa-list-alt" style="color:var(--primary-color);"></i> Riwayat Lengkap per Kategori</h4>
@if($kategoriList->count() > 0)
{{-- Tab list --}}
<div class="ds-tabs" role="tablist">
@foreach($kategoriList as $kat)
@php
$tabParams = array_merge(request()->query(), [
'tab_kat' => $kat->kategori_id,
'per_page' => $perPage,
'page' => 1,
]);
$isActive = ($activeKategori == $kat->kategori_id);
$pctKat = $kat->total > 0 ? round($kat->hadir_efektif / $kat->total * 100) : 0;
@endphp
<a href="{{ route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri) }}?{{ http_build_query($tabParams) }}"
class="ds-tab {{ $isActive ? 'active' : '' }}"
role="tab">
<i class="fas fa-tag" style="font-size:0.72rem;"></i>
{{ $kat->nama_kategori }}
<span class="tab-cnt">{{ $kat->total }}</span>
@if($kat->hadir_efektif > 0)
<span class="tab-hadir" title="Hadir efektif">{{ $pctKat }}%</span>
@endif
</a>
@endforeach
</div>
@endif
</div>
@if($riwayats->count() > 0)
{{-- Toolbar: info + per-page --}}
<div class="ds-toolbar">
<div class="ds-toolbar-left">
@if(!$showAll)
Menampilkan
<strong>{{ $riwayats->firstItem() }}{{ $riwayats->lastItem() }}</strong>
dari <strong>{{ $riwayats->total() }}</strong> riwayat
@else
Menampilkan <strong>semua {{ $riwayats->total() }}</strong> riwayat
@endif
@php $activeKat = $kategoriList->firstWhere('kategori_id', $activeKategori); @endphp
@if($activeKat)
&mdash; <span style="color:var(--primary-color); font-weight:600;">{{ $activeKat->nama_kategori }}</span>
@endif
</div>
<div class="ds-toolbar-right">
Tampilkan:
<div class="ds-perpage-group">
@foreach([15, 50, 100] as $pp)
@php $ppParams = array_merge(request()->query(), ['per_page' => $pp, 'page' => 1]); @endphp
<a href="{{ route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri) }}?{{ http_build_query($ppParams) }}"
class="ds-pp-btn {{ (!$showAll && $perPage == $pp) ? 'active' : '' }}">
{{ $pp }}
</a>
@endforeach
{{-- Semua --}}
@php $allParams = array_merge(request()->query(), ['per_page' => 'all', 'page' => 1]); @endphp
<a href="{{ route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri) }}?{{ http_build_query($allParams) }}"
class="ds-pp-btn {{ $showAll ? 'active' : '' }}"
title="Tampilkan semua data sekaligus (bisa lambat jika data banyak)">
Semua
</a>
</div>
</div>
</div>
{{-- Table --}}
<div class="ds-table-wrap">
<table class="ds-table">
<thead>
<tr>
<th style="width:50px;">No</th>
<th style="width:110px;">Tanggal</th>
<th>Nama Kegiatan</th>
<th style="width:115px; text-align:center;">Status</th>
<th style="width:90px; text-align:center;">Waktu</th>
</tr>
</thead>
<tbody>
@foreach($riwayats as $idx => $riwayat)
<tr>
<td style="color:#94a3b8; font-size:0.78rem;">{{ $riwayats->firstItem() + $idx }}</td>
<td style="font-size:0.81rem; color:#64748b;">
{{ $riwayat->tanggal->format('d/m/Y') }}
<div style="font-size:0.71rem; color:#94a3b8;">
{{ $riwayat->tanggal->locale('id')->isoFormat('ddd') }}
</div>
</td>
<td>
<strong style="font-size:0.85rem;">{{ $riwayat->kegiatan->nama_kegiatan }}</strong>
<div style="font-size:0.74rem; color:#94a3b8; margin-top:1px;">
<i class="fas fa-clock"></i>
{{ date('H:i', strtotime($riwayat->kegiatan->waktu_mulai)) }}{{ date('H:i', strtotime($riwayat->kegiatan->waktu_selesai)) }}
</div>
</td>
<td style="text-align:center;">
@if(method_exists($riwayat, 'getStatusBadgeAttribute') || isset($riwayat->status_badge))
{!! $riwayat->status_badge !!}
@else
@php $st = $riwayat->status; @endphp
<span class="s-{{ strtolower($st) }}">{{ $st }}</span>
@endif
</td>
<td style="text-align:center; font-size:0.81rem; color:#64748b;">
{{ $riwayat->waktu_absen ? date('H:i', strtotime($riwayat->waktu_absen)) : '' }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- Pagination (disembunyikan jika mode Semua) --}}
@if(!$showAll && $riwayats->lastPage() > 1)
<div style="padding:12px 16px;">
{{ $riwayats->links() }}
</div>
@elseif($showAll)
<div style="padding:10px 16px; font-size:0.78rem; color:#94a3b8; background:#fafafa; border-top:1px solid #f1f5f9;">
<i class="fas fa-info-circle"></i> Semua <strong>{{ $riwayats->total() }}</strong> data ditampilkan.
&nbsp;
@php $backPaged = array_merge(request()->query(), ['per_page' => 15, 'page' => 1]); @endphp
<a href="{{ route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri) }}?{{ http_build_query($backPaged) }}"
style="color:var(--primary-color);">Kembali ke paginasi</a>
</div>
@endif
@elseif($kategoriList->count() > 0)
<div class="ds-empty">
<i class="fas fa-inbox"></i>
<p>Belum ada riwayat untuk kategori ini.</p>
</div>
@else
<div class="ds-empty">
<i class="fas fa-inbox"></i>
<p>Santri ini belum memiliki riwayat kehadiran apapun.</p>
</div>
@endif
</div>
{{-- ── CHART JS ─────────────────────────────────────────────────────────────── --}}
@if($riwayat30Hari->count() > 0)
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
(function() {
var ctx = document.getElementById('chartTren');
if (!ctx) return;
var labels = {!! json_encode($riwayat30Hari->pluck('tanggal')->map(fn($d) => date('d/m', strtotime($d)))) !!};
var hadir = {!! json_encode($riwayat30Hari->pluck('hadir')) !!};
var total = {!! json_encode($riwayat30Hari->pluck('total')) !!};
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Hadir (termasuk terlambat)',
data: hadir,
borderColor: '#6FBA9D',
backgroundColor: 'rgba(111,186,157,0.12)',
tension: 0.4,
fill: true,
pointRadius: 4,
pointBackgroundColor: '#6FBA9D',
},
{
label: 'Total Kegiatan',
data: total,
borderColor: '#cbd5e1',
backgroundColor: 'transparent',
borderDash: [5, 5],
tension: 0.4,
fill: false,
pointRadius: 0,
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 11 } } }
},
scales: {
y: { beginAtZero: true, ticks: { stepSize: 1 }, grid: { color: '#f1f5f9' } },
x: { grid: { display: false }, ticks: { font: { size: 10 } } }
}
}
});
})();
</script>
@endif
@endsection