TIFNJK_E41221880/resources/views/pages/absensi/absensi_mapel.blade.php

886 lines
50 KiB
PHP

@extends('pages.index')
@section('admin_content')
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.5/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.5/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.table-custom-header th {
background: linear-gradient(90deg, #365CF5 0%, #6a8ffd 100%) !important;
color: white !important;
font-weight: 600;
letter-spacing: 0.5px;
padding: 14px 12px !important;
border: none !important;
white-space: nowrap;
}
.table tbody tr { transition: background 0.2s; }
.table tbody tr:nth-child(even) { background: #f4f7ff !important; }
.table tbody tr:hover { background: #e3eafd !important; }
.table th, .table td { border: none !important; vertical-align: middle !important; }
.table { border-collapse: separate !important; border-spacing: 0 !important; font-size: 14px; }
.dataTables_paginate .paginate_button { background-color: transparent !important; border: none !important; color: #365CF5 !important; }
.dataTables_paginate .paginate_button:hover { background-color: #e3eafd !important; color: #365CF5 !important; border-radius: 6px !important; }
.dataTables_paginate .paginate_button.current { background: linear-gradient(90deg, #365CF5, #6a8ffd) !important; color: white !important; border-radius: 6px !important; }
.btn-edit-siswa {
font-weight: bold; background-color: transparent; color: #ffc107;
border: 2px solid #ffc107; padding: 8px 16px; display: flex;
align-items: center; gap: 8px; transition: all 0.3s ease; border-radius: 8px;
}
.btn-edit-siswa:hover { background-color: #ffc107; color: #212529; }
.summary-card {
border-radius: 16px; padding: 18px 24px; color: white;
display: flex; align-items: center; gap: 16px;
box-shadow: 0 4px 16px rgba(0,0,0,0.10); transition: transform 0.2s;
}
.summary-card:hover { transform: translateY(-3px); }
.summary-card .icon-wrap {
width: 52px; height: 52px; background: rgba(255,255,255,0.2);
border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.7rem;
}
.summary-card .label { font-size: 0.85rem; opacity: 0.85; }
.summary-card .count { font-size: 2rem; font-weight: 800; line-height: 1; }
.filter-label { font-size: 0.8rem; font-weight: 600; color: #6c757d; margin-bottom: 4px; }
div.dataTables_filter { margin-bottom: 1rem !important; margin-top: 0.5rem !important; }
@media (max-width: 767.98px) {
.btn-edit-siswa { font-size: 15px !important; padding: 10px 18px !important; width: 100% !important; }
}
</style>
<div class="container-fluid">
<div class="title-wrapper pt-30">
<div class="row align-items-start">
<div class="col-md-6">
<div class="title">
<h2 style="font-weight:500;">
Absensi Mapel
@if(isset($kelas)) - {{ $kelas->name }} @endif
</h2>
</div>
</div>
<div class="col-md-6">
<div class="breadcrumb-wrapper">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('dashboard.index') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ route('kelas.absensi') }}">Kelas</a></li>
<li class="breadcrumb-item active" aria-current="page">
Absensi Mapel @if(isset($kelas)) - {{ $kelas->name }} @endif
</li>
</ol>
</nav>
</div>
</div>
</div>
</div>
@php
$totalHadir = isset($absensi) ? $absensi->where('status','tepat_waktu')->count() : 0;
$totalTerlambat = isset($absensi) ? $absensi->where('status','terlambat')->count() : 0;
$totalLainnya = isset($absensi) ? $absensi->whereNotIn('status',['tepat_waktu','terlambat'])->count() : 0;
$totalSemua = isset($absensi) ? $absensi->count() : 0;
@endphp
<!-- Summary Cards -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-3">
<div class="summary-card" style="background:linear-gradient(135deg,#365CF5,#6a8ffd);">
<div class="icon-wrap"><i class="fas fa-users"></i></div>
<div><div class="label">Total</div><div class="count">{{ $totalSemua }}</div></div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="summary-card" style="background:linear-gradient(135deg,#22c55e,#4ade80);">
<div class="icon-wrap"><i class="fas fa-check-circle"></i></div>
<div><div class="label">Hadir</div><div class="count">{{ $totalHadir }}</div></div>
</div>
</div>
</div>
<!-- Card Wrapper -->
<div class="card border-0 shadow-sm rounded-4">
<div class="card-header bg-white border-0 rounded-top-4 d-flex flex-column flex-md-row justify-content-between align-items-md-center align-items-start py-3 px-4">
<h5 class="mb-2 mb-md-0 fw-bold text-primary">
<i class="fas fa-book me-2"></i>
Absensi Mapel
@if(isset($kelas))
<span class="badge bg-primary ms-2 rounded-pill">{{ $kelas->name }}</span>
@endif
</h5>
<div class="d-flex gap-2 flex-column flex-md-row w-100 w-md-auto justify-content-md-end mt-2 mt-md-0">
<button class="btn btn-success btn-sm" id="btn-export-excel" type="button" title="Export ke Excel">
<i class="fas fa-file-excel me-1"></i> Export Excel
</button>
<button class="btn btn-edit-siswa btn-sm" style="font-size:14px;padding:7px 14px;" id="btn-edit-siswa" type="button">
<i class="fas fa-edit"></i> Edit
</button>
</div>
</div>
<div class="card-body px-4 py-3">
<!-- Info Display -->
<div class="row mb-4 p-3 bg-light rounded-3" style="border-left: 4px solid #365CF5; background: linear-gradient(135deg, #f8f9fa 0%, #e3eafd 100%);">
<div class="col-md-4">
<div class="d-flex align-items-center gap-2">
<i class="fas fa-book text-primary" style="font-size: 1.2rem;"></i>
<div>
<span class="text-muted d-block" style="font-size: 0.75rem; font-weight: 600;">Pelajaran</span>
<span class="fw-bold text-dark" id="info-pelajaran" style="font-size: 1rem;">-</span>
</div>
</div>
</div>
<div class="col-md-4">
<div class="d-flex align-items-center gap-2">
<i class="fas fa-calendar-alt text-primary" style="font-size: 1.2rem;"></i>
<div>
<span class="text-muted d-block" style="font-size: 0.75rem; font-weight: 600;">Bulan/Tahun</span>
<span class="fw-bold text-dark" id="info-bulan-tahun" style="font-size: 1rem;">{{ now()->timezone('Asia/Jakarta')->format('F Y') }}</span>
</div>
</div>
</div>
<div class="col-md-4">
<div class="d-flex align-items-center gap-2">
<i class="fas fa-chalkboard-user text-primary" style="font-size: 1.2rem;"></i>
<div>
<span class="text-muted d-block" style="font-size: 0.75rem; font-weight: 600;">Kelas</span>
<span class="fw-bold text-dark" style="font-size: 1rem;">{{ $kelas->name ?? '-' }}</span>
</div>
</div>
</div>
</div>
<!-- Filter -->
<div class="row mb-4 g-2">
<div class="col-md-3">
<div class="filter-label"><i class="fas fa-book me-1"></i> Pelajaran</div>
<select id="filter-mapel" class="form-select rounded-3 shadow-sm">
<option value="">-- Pilih Pelajaran --</option>
@if(isset($mapelList) && count($mapelList))
@foreach($mapelList as $mapel)
<option value="{{ $mapel->id }}">{{ $mapel->name }}</option>
@endforeach
@endif
</select>
</div>
<div class="col-md-3">
<div class="filter-label"><i class="fas fa-calendar me-1"></i> Tanggal</div>
<input type="date" id="filter-tanggal" class="form-control rounded-3 shadow-sm">
</div>
<div class="col-md-3">
<div class="filter-label"><i class="fas fa-calendar-alt me-1"></i> Bulan</div>
<select id="filter-bulan" class="form-select rounded-3 shadow-sm">
<option value="">-- Pilih Bulan --</option>
@php
$bulanIndo = ['01'=>'Januari','02'=>'Februari','03'=>'Maret','04'=>'April','05'=>'Mei','06'=>'Juni','07'=>'Juli','08'=>'Agustus','09'=>'September','10'=>'Oktober','11'=>'November','12'=>'Desember'];
@endphp
@foreach($bulanIndo as $num => $nama)
<option value="{{ $num }}">{{ $nama }}</option>
@endforeach
</select>
</div>
<div class="col-md-3">
<div class="filter-label"><i class="fas fa-calendar-week me-1"></i> Tahun</div>
<select id="filter-tahun" class="form-select rounded-3 shadow-sm">
<option value="">-- Pilih Tahun --</option>
@for($y = date('Y')-5; $y <= date('Y')+1; $y++)
<option value="{{ $y }}">{{ $y }}</option>
@endfor
</select>
</div>
</div>
<div class="table-responsive rounded-3">
<table id="example" class="table table-hover align-middle w-100">
<thead class="table-custom-header">
<tr>
<th class="text-center" style="width:40px;"><input type="checkbox" id="select-all" /></th>
<th style="width:50px;">No</th>
<th>Nama Siswa</th>
<th>NISN</th>
<th>Tanggal Absensi</th>
<th>Jam Pertemuan</th>
<th>Coordinate</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody id="table-body" style="display: none;">
@php $rownum = 1; @endphp
@if(isset($absensi) && count($absensi))
@foreach($absensi as $idx => $item)
<tr data-absensi-index="{{ $idx }}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">{{ $rownum }}</td>
<td>
<span class="fw-semibold text-dark d-block">{{ $item->student->name ?? '-' }}</span>
</td>
<td>
<span class="fw-semibold">{{ $item->student->nisn ?? '-' }}</span>
</td>
<td class="text-center">
<span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">
{{ $item->created_at ? $item->created_at->format('H:i') : '-' }}
</span>
</td>
<td class="text-center">
<span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">
{{ $item->created_at ? $item->created_at->format('d/m/Y') : '-' }}
</span>
</td>
<td class="text-center">
@if($item->coordinate)
<a href="https://www.google.com/maps?q={{ $item->coordinate }}" target="_blank" class="text-decoration-none" title="Buka di Google Maps">
<span class="fw-semibold" style="font-size:0.85rem; color:#365CF5; cursor:pointer;">
<i class="fas fa-map-marker-alt me-1"></i>{{ $item->coordinate }}
</span>
</a>
@else
<span class="fw-semibold text-muted" style="font-size:0.85rem;">-</span>
@endif
</td>
<td>
@if($item->status == 'hadir')
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#dcfce7;color:#16a34a;font-size:0.9em;">
<i class="fas fa-check-circle me-1"></i> Hadir
</span>
@elseif($item->status == 'izin')
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fef9c3;color:#eab308;font-size:0.9em;">
<i class="fas fa-envelope me-1"></i> Izin
</span>
@elseif($item->status == 'sakit')
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#f0fdf4;color:#22c55e;font-size:0.9em;">
<i class="fas fa-medkit me-1"></i> Sakit
</span>
@elseif($item->status == 'alpha')
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fee2e2;color:#dc2626;font-size:0.9em;">
<i class="fas fa-times-circle me-1"></i> Alpha
</span>
@elseif($item->status == 'dispen')
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#e0e7ff;color:#6366f1;font-size:0.9em;">
<i class="fas fa-user-shield me-1"></i> Dispen
</span>
@else
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#f3f4f6;color:#6b7280;font-size:0.9em;">
{{ ucfirst($item->status) }}
</span>
@endif
</td>
</tr>
@php $rownum++; @endphp
@endforeach
@endif
@if(isset($belumAbsen) && count($belumAbsen))
@foreach($belumAbsen as $idx => $siswa)
<tr data-absensi-index="new_{{ $idx }}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">{{ $rownum }}</td>
<td>
<span class="fw-semibold text-dark d-block">{{ $siswa->name }}</span>
</td>
<td>
<span class="fw-semibold">{{ $siswa->nisn }}</span>
</td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td>
<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fff3cd;color:#b45309;font-size:0.9em;">
<i class="fas fa-minus-circle me-1"></i> Belum Absen
</span>
</td>
</tr>
@php $rownum++; @endphp
@endforeach
@endif
</tbody>
</table>
<div id="empty-message" class="alert alert-info text-center p-4" role="alert">
<i class="fas fa-info-circle me-2"></i> Silahkan pilih pelajaran terlebih dahulu untuk menampilkan data absensi
</div>
</div>
</div>
</div>
<!-- Modal Export Filter -->
<div class="modal fade" id="modalExportFilter" tabindex="-1" aria-labelledby="modalExportFilterLabel" aria-hidden="true" style="display:none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title" id="modalExportFilterLabel">
<i class="fas fa-file-excel me-2"></i> Export Absensi Mapel
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="formExportFilter">
<div class="modal-body">
<p class="text-muted mb-3">Pilih bulan dan tahun untuk export data absensi mapel</p>
<div class="mb-3">
<label for="export-bulan" class="form-label">Bulan</label>
<select class="form-select" id="export-bulan" name="bulan" required>
<option value="">-- Pilih Bulan --</option>
@php
$bulanIndo = ['01'=>'Januari','02'=>'Februari','03'=>'Maret','04'=>'April','05'=>'Mei','06'=>'Juni','07'=>'Juli','08'=>'Agustus','09'=>'September','10'=>'Oktober','11'=>'November','12'=>'Desember'];
$currentMonth = now()->timezone('Asia/Jakarta')->format('m');
@endphp
@foreach($bulanIndo as $num => $nama)
<option value="{{ $num }}" @selected($num === $currentMonth)>{{ $nama }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label for="export-tahun" class="form-label">Tahun</label>
<select class="form-select" id="export-tahun" name="tahun" required>
<option value="">-- Pilih Tahun --</option>
@php
$currentYear = now()->timezone('Asia/Jakarta')->format('Y');
@endphp
@for($y = date('Y')-5; $y <= date('Y')+1; $y++)
<option value="{{ $y }}" @selected($y == $currentYear)>{{ $y }}</option>
@endfor
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-success">
<i class="fas fa-download me-1"></i> Export Excel
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Edit Status -->
<div class="modal fade" id="modalEditStatus" tabindex="-1" aria-labelledby="modalEditStatusLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalEditStatusLabel">Edit Status Absensi</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="formEditStatus">
<div class="modal-body">
<input type="hidden" id="editAbsensiId" name="id">
<input type="hidden" id="editStudentId" name="id_student">
<input type="hidden" id="editClassId" name="id_class">
<input type="hidden" id="editMapelId" name="id_subject">
<div class="mb-3" id="scheduleField" style="display:none;">
<label for="editSchedule" class="form-label">Pilih Schedule <span class="text-danger">*</span></label>
<select class="form-select" id="editSchedule" name="id_schedule">
<option value="">-- Pilih Schedule --</option>
</select>
<small class="text-muted d-block mt-1">Pilih schedule untuk membuat record absensi baru</small>
</div>
<div class="mb-3">
<label for="editStatus" class="form-label">Status</label>
<select class="form-select" id="editStatus" name="status" required>
<option value="hadir">Hadir</option>
<option value="izin">Izin</option>
<option value="sakit">Sakit</option>
<option value="alpha">Alpha</option>
<option value="dispen">Dispen</option>
<option value="belum">Belum Absen</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary">Simpan</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
$(document).ready(function () {
var table;
var currentMapelId = null; // Track currently selected mapel
// Hide table body awalnya
$('#table-body').hide();
$('#empty-message').show();
$('#select-all').on('click', function () {
$('.row-checkbox').prop('checked', this.checked);
});
// Handle filter tanggal, bulan, tahun
function loadAbsensiWithFilters() {
const tanggal = $('#filter-tanggal').val();
const bulan = $('#filter-bulan').val();
const tahun = $('#filter-tahun').val();
const mapelId = $('#filter-mapel').val();
const classId = "{{ $kelas->id }}";
if (!mapelId) {
alert('Silahkan pilih pelajaran terlebih dahulu.');
return;
}
// Prioritas: tanggal > bulan+tahun > bulan > tahun
let url = "{{ url('/absensi-mapel/get-by-mapel') }}/" + classId + "/" + mapelId;
if (tanggal) {
// Jika tanggal spesifik, gunakan date filter
url = "{{ url('/absensi-mapel/get-by-mapel-date') }}/" + classId + "/" + mapelId + "/" + tanggal;
console.log('Filter dengan tanggal:', tanggal);
} else if (bulan && tahun) {
// Jika bulan+tahun, gunakan filter ini
url += "?bulan=" + bulan + "&tahun=" + tahun;
console.log('Filter dengan bulan+tahun:', bulan, tahun);
} else if (bulan) {
// Jika hanya bulan
url += "?bulan=" + bulan;
console.log('Filter dengan bulan:', bulan);
} else if (tahun) {
// Jika hanya tahun
url += "?tahun=" + tahun;
console.log('Filter dengan tahun:', tahun);
}
// Destroy DataTable dulu SEBELUM fetch
if ($.fn.DataTable.isDataTable('#example')) {
$('#example').DataTable().destroy();
}
// Fetch absensi data
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: function(response) {
console.log('API Response:', response);
// Update hidden data dengan response
window.currentAbsensi = response.absensi || [];
window.currentBelumAbsen = response.belumAbsen || [];
// Clear tbody COMPLETELY - hapus semua child
$('#table-body').empty();
let rownum = 1;
// Add existing absensi rows
if (response.absensi && response.absensi.length > 0) {
response.absensi.forEach(function(item, idx) {
let statusBadge = '';
if (item.status === 'hadir') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#dcfce7;color:#16a34a;font-size:0.9em;"><i class="fas fa-check-circle me-1"></i> Hadir</span>';
} else if (item.status === 'izin') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fef9c3;color:#eab308;font-size:0.9em;"><i class="fas fa-envelope me-1"></i> Izin</span>';
} else if (item.status === 'sakit') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#f0fdf4;color:#22c55e;font-size:0.9em;"><i class="fas fa-medkit me-1"></i> Sakit</span>';
} else if (item.status === 'alpha') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fee2e2;color:#dc2626;font-size:0.9em;"><i class="fas fa-times-circle me-1"></i> Alpha</span>';
} else if (item.status === 'dispen') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#e0e7ff;color:#6366f1;font-size:0.9em;"><i class="fas fa-user-shield me-1"></i> Dispen</span>';
} else {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold">' + item.status + '</span>';
}
const attendanceDate = item.created_at ? new Date(item.created_at).toLocaleDateString('id-ID', {day: '2-digit', month: '2-digit', year: 'numeric'}) : '-';
const attendanceTime = item.created_at ? new Date(item.created_at).toLocaleTimeString('id-ID', {hour: '2-digit', minute: '2-digit'}) : '-';
let coordinateCell = '';
if (item.coordinate) {
coordinateCell = `<a href="https://www.google.com/maps?q=${item.coordinate}" target="_blank" class="text-decoration-none" title="Buka di Google Maps"><span class="fw-semibold" style="font-size:0.85rem; color:#365CF5; cursor:pointer;"><i class="fas fa-map-marker-alt me-1"></i>${item.coordinate}</span></a>`;
} else {
coordinateCell = '<span class="fw-semibold text-muted" style="font-size:0.85rem;">-</span>';
}
const row = `
<tr data-absensi-index="${idx}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">${rownum}</td>
<td><span class="fw-semibold text-dark d-block">${item.student?.name || '-'}</span></td>
<td><span class="fw-semibold">${item.student?.nisn || '-'}</span></td>
<td class="text-center"><span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">${attendanceDate}</span></td>
<td class="text-center"><span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">${attendanceTime}</span></td>
<td class="text-center">${coordinateCell}</td>
<td>${statusBadge}</td>
</tr>
`;
$('#table-body').append(row);
rownum++;
});
}
// Add belum absen rows
if (response.belumAbsen && response.belumAbsen.length > 0) {
response.belumAbsen.forEach(function(siswa, idx) {
const row = `
<tr data-absensi-index="new_${idx}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">${rownum}</td>
<td><span class="fw-semibold text-dark d-block">${siswa.name}</span></td>
<td><span class="fw-semibold">${siswa.nisn}</span></td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td><span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fff3cd;color:#b45309;font-size:0.9em;"><i class="fas fa-minus-circle me-1"></i> Belum Absen</span></td>
</tr>
`;
$('#table-body').append(row);
rownum++;
});
}
$('#table-body').show();
$('#empty-message').hide();
// Reinitialize DataTable
table = $('#example').DataTable({
lengthChange: false,
language: {
search: "Cari:",
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
emptyTable: "Tidak ada data absensi mapel.",
paginate: { first:"Awal", last:"Akhir", next:"Berikutnya", previous:"Sebelumnya" }
},
pageLength: 10
});
},
error: function(xhr, status, error) {
console.error('AJAX Error:', error, xhr.responseText);
alert('Gagal memuat data absensi: ' + error);
}
});
}
// Handle filter tanggal change
$('#filter-tanggal').on('change', function() {
loadAbsensiWithFilters();
});
// Handle filter bulan change
$('#filter-bulan').on('change', function() {
loadAbsensiWithFilters();
});
// Handle filter tahun change
$('#filter-tahun').on('change', function() {
loadAbsensiWithFilters();
});
// Handle filter mapel
$('#filter-mapel').on('change', function() {
const mapelText = $(this).find('option:selected').text();
const mapelId = $(this).val();
const classId = "{{ $kelas->id }}";
currentMapelId = mapelId; // Store current mapel id
if (mapelId === '') {
$('#info-pelajaran').text('-');
$('#table-body').hide();
$('#empty-message').show();
// Destroy DataTable jika ada
if ($.fn.DataTable.isDataTable('#example')) {
$('#example').DataTable().destroy();
}
} else {
$('#info-pelajaran').text(mapelText);
// Fetch absensi data berdasarkan mapel
$.ajax({
url: "{{ url('/absensi-mapel/get-by-mapel') }}/" + classId + "/" + mapelId,
type: "GET",
dataType: "json",
success: function(response) {
// Update hidden data dengan response
window.currentAbsensi = response.absensi;
window.currentBelumAbsen = response.belumAbsen;
// Clear tbody
$('#table-body').html('');
let rownum = 1;
// Add existing absensi rows
if (response.absensi && response.absensi.length > 0) {
response.absensi.forEach(function(item, idx) {
let statusBadge = '';
if (item.status === 'hadir') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#dcfce7;color:#16a34a;font-size:0.9em;"><i class="fas fa-check-circle me-1"></i> Hadir</span>';
} else if (item.status === 'izin') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fef9c3;color:#eab308;font-size:0.9em;"><i class="fas fa-envelope me-1"></i> Izin</span>';
} else if (item.status === 'sakit') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#f0fdf4;color:#22c55e;font-size:0.9em;"><i class="fas fa-medkit me-1"></i> Sakit</span>';
} else if (item.status === 'alpha') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fee2e2;color:#dc2626;font-size:0.9em;"><i class="fas fa-times-circle me-1"></i> Alpha</span>';
} else if (item.status === 'dispen') {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#e0e7ff;color:#6366f1;font-size:0.9em;"><i class="fas fa-user-shield me-1"></i> Dispen</span>';
} else {
statusBadge = '<span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#f3f4f6;color:#6b7280;font-size:0.9em;">' + (item.status.charAt(0).toUpperCase() + item.status.slice(1)) + '</span>';
}
const attendanceDate = item.created_at ? new Date(item.created_at).toLocaleDateString('id-ID', {day: '2-digit', month: '2-digit', year: 'numeric'}) : '-';
const attendanceTime = item.created_at ? new Date(item.created_at).toLocaleTimeString('id-ID', {hour: '2-digit', minute: '2-digit'}) : '-';
let coordinateCell = '';
if (item.coordinate) {
coordinateCell = `<a href="https://www.google.com/maps?q=${item.coordinate}" target="_blank" class="text-decoration-none" title="Buka di Google Maps"><span class="fw-semibold" style="font-size:0.85rem; color:#365CF5; cursor:pointer;"><i class="fas fa-map-marker-alt me-1"></i>${item.coordinate}</span></a>`;
} else {
coordinateCell = `<span class="fw-semibold text-muted" style="font-size:0.85rem;">-</span>`;
}
const row = `
<tr data-absensi-index="${idx}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">${rownum}</td>
<td><span class="fw-semibold text-dark d-block">${item.student.name || '-'}</span></td>
<td><span class="fw-semibold">${item.student.nisn || '-'}</span></td>
<td class="text-center"><span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">${attendanceDate}</span></td>
<td class="text-center"><span class="badge rounded-pill px-3 py-2" style="background:#e3eafd;color:#365CF5;font-weight:600;">${attendanceTime}</span></td>
<td class="text-center">${coordinateCell}</td>
<td>${statusBadge}</td>
</tr>
`;
$('#table-body').append(row);
rownum++;
});
}
// Add belum absen rows
if (response.belumAbsen && response.belumAbsen.length > 0) {
response.belumAbsen.forEach(function(siswa, idx) {
const row = `
<tr data-absensi-index="new_${idx}">
<td class="text-center"><input type="checkbox" class="row-checkbox" /></td>
<td class="text-center fw-bold">${rownum}</td>
<td><span class="fw-semibold text-dark d-block">${siswa.name}</span></td>
<td><span class="fw-semibold">${siswa.nisn}</span></td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td class="text-center">-</td>
<td><span class="badge rounded-pill px-3 py-2 fw-bold" style="background:#fff3cd;color:#b45309;font-size:0.9em;"><i class="fas fa-minus-circle me-1"></i> Belum Absen</span></td>
</tr>
`;
$('#table-body').append(row);
rownum++;
});
}
$('#table-body').show();
$('#empty-message').hide();
// Reinitialize DataTable
if ($.fn.DataTable.isDataTable('#example')) {
$('#example').DataTable().destroy();
}
table = $('#example').DataTable({
lengthChange: false,
language: {
search: "Cari:",
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
emptyTable: "Tidak ada data absensi mapel.",
paginate: { first:"Awal", last:"Akhir", next:"Berikutnya", previous:"Sebelumnya" }
},
pageLength: 10
});
},
error: function() {
alert('Gagal mengambil data absensi.');
$('#table-body').hide();
$('#empty-message').show();
}
});
}
});
// Handle export button - format grid standar per periode
$('#btn-export-excel').on('click', function () {
const classId = "{{ $kelas->id ?? 0 }}";
const mapelId = $('#filter-mapel').val();
// Get current bulan dan tahun dari filter
let bulan = $('#filter-bulan').val() || '';
let tahun = $('#filter-tahun').val() || '';
// Jika kedua-duanya kosong, default ke bulan & tahun sekarang
if (!bulan && !tahun) {
bulan = new Date().getMonth() + 1;
bulan = String(bulan).padStart(2, '0');
tahun = new Date().getFullYear();
} else if (bulan && !tahun) {
// Jika hanya bulan yang diisi, default tahun ke tahun sekarang
tahun = new Date().getFullYear();
}
if (!mapelId) {
alert('Pilih pelajaran terlebih dahulu.');
return;
}
// Send POST request untuk export grid format
$.ajax({
url: `/absensi-mapel/export-excel/${classId}`,
type: 'POST',
data: {
mapel: mapelId,
bulan: bulan,
tahun: tahun,
_token: '{{ csrf_token() }}'
},
xhrFields: {
responseType: 'blob'
},
success: function(data, status, xhr) {
// Get filename dari Content-Disposition header
const disposition = xhr.getResponseHeader('Content-Disposition');
let filename = 'absensi_mapel.xlsx';
if (disposition && disposition.indexOf('attachment') !== -1) {
const matches = disposition.match(/filename[^;=\n]*=(["\']?)([^"\';\n]*)\1/);
if (matches && matches[2]) {
filename = matches[2];
}
}
// Download file
const link = document.createElement('a');
const url = window.URL.createObjectURL(data);
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
},
error: function() {
alert('Gagal export data.');
}
});
});
// Inisialisasi modal Bootstrap
var modalEditStatus = new bootstrap.Modal(document.getElementById('modalEditStatus'));
$('#btn-edit-siswa').on('click', function () {
const checked = $('.row-checkbox:checked');
if (checked.length === 0) {
alert('Pilih data yang ingin diedit.');
} else if (checked.length > 1) {
alert('Pilih hanya satu data untuk diedit.');
} else {
const row = checked.closest('tr');
const absensiIndex = row.data('absensi-index');
let absensiId, status, studentId, classId, nama;
// Cek apakah ini record baru (belum absen) atau existing
if (typeof absensiIndex === 'string' && absensiIndex.startsWith('new_')) {
// Data dari belumAbsen
const belumAbsenList = window.currentBelumAbsen;
const belumAbsenIdx = parseInt(absensiIndex.split('_')[1]);
const siswaData = belumAbsenList[belumAbsenIdx];
absensiId = 'null';
status = 'belum';
studentId = siswaData?.id;
classId = "{{ $kelas->id }}";
nama = siswaData?.name;
// Tampilkan schedule field untuk data baru
$('#scheduleField').show();
loadScheduleOptions(classId, currentMapelId);
} else {
// Data dari absensi
const absensiList = window.currentAbsensi;
const absensiData = absensiList[absensiIndex];
absensiId = absensiData?.id;
status = absensiData?.status;
studentId = absensiData?.student?.id;
classId = absensiData?.class?.id;
nama = absensiData?.student?.name;
// Sembunyikan schedule field untuk data existing
$('#scheduleField').hide();
}
// Set values ke hidden inputs
$('#editAbsensiId').val(absensiId);
$('#editStudentId').val(studentId);
$('#editClassId').val(classId);
$('#editMapelId').val(currentMapelId);
$('#editStatus').val(status);
modalEditStatus.show();
}
});
// Function to load schedule options
function loadScheduleOptions(classId, subjectId) {
$.ajax({
url: "{{ url('/absensi-mapel/get-schedules') }}/" + classId + "/" + subjectId,
type: "GET",
dataType: "json",
success: function(response) {
$('#editSchedule').html('<option value="">-- Pilih Schedule --</option>');
if (response.success && response.schedules) {
response.schedules.forEach(function(schedule) {
$('#editSchedule').append(
'<option value="' + schedule.id + '">' + (schedule.day + ' - ' + schedule.time || 'Schedule ' + schedule.id) + '</option>'
);
});
}
},
error: function() {
alert('Gagal memuat schedule.');
}
});
}
$('#formEditStatus').on('submit', function (e) {
e.preventDefault();
const id = $('#editAbsensiId').val();
const status = $('#editStatus').val();
const studentId = $('#editStudentId').val();
const classId = $('#editClassId').val();
const mapelId = $('#editMapelId').val();
// Siapkan data
let data = {
status: status,
_token: '{{ csrf_token() }}'
};
// Jika create baru (id adalah 'null'), tambahkan id_student, id_class, id_subject, dan id_schedule
if (id === 'null') {
const scheduleId = $('#editSchedule').val();
if (!scheduleId) {
alert('Pilih schedule terlebih dahulu.');
return;
}
data.id_student = studentId;
data.id_class = classId;
data.id_subject = mapelId;
data.id_schedule = scheduleId;
}
$.ajax({
url: "{{ url('/pages/absensi-mapel/edit-status') }}/" + id,
type: "POST",
data: data,
success: function (res) {
if (res.success) {
modalEditStatus.hide();
location.reload();
} else {
alert('Gagal mengupdate status.');
}
},
error: function () {
alert('Terjadi kesalahan.');
}
});
});
});
</script>
@endsection