sistem_rekomendasi/resources/views/admin/alumni/alumni.blade.php

369 lines
12 KiB
PHP

@extends('admin.layout')
@section('content')
<link rel="stylesheet" href="{{ asset('css/fitur-admin.css') }}">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<div class="dashboard-section">
<div class="master-page-header">
<div>
<h4 class="master-page-title">Manajemen Data Alumni</h4>
<p class="master-page-subtitle">Kelola data historis lulusan untuk kebutuhan dataset sistem prediksi.</p>
</div>
<a href="{{ route('admin.alumni.create') }}" class="btn-add-modern">
<i class="fas fa-plus"></i>
Tambah Data Alumni
</a>
</div>
<div class="table-container-modern shadow-sm">
<div class="table-toolbar">
<div class="search-wrapper-modern">
<i class="fas fa-search"></i>
<input type="text" id="searchInput" class="search-input-modern" placeholder="Cari nama alumni, perguruan tinggi, atau jurusan...">
</div>
<div class="per-page-wrapper">
<span>Tampilkan</span>
<select id="perPageSelect" class="per-page-select">
<option value="7" selected>7 data</option>
<option value="5">5 data</option>
</select>
</div>
</div>
<div class="table-responsive">
<table class="table table-modern mb-0">
<thead>
<tr>
<th class="text-center" width="70">No</th>
<th class="text-center" width="120">ID Alumni</th>
<th>Nama Alumni</th>
<th>Perguruan Tinggi</th>
<th class="text-center">Jurusan Diambil</th>
<th class="text-center" width="150">Opsi</th>
</tr>
</thead>
<tbody id="alumni-table-body">
<tr>
<td colspan="6" class="loading-state-modern">
<i class="fas fa-spinner fa-spin me-2"></i> Memuat data alumni...
</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination-container">
<div class="text-muted small" id="pagination-info">
Menampilkan 0 sampai 0 dari 0 data
</div>
<div class="d-flex gap-1 flex-wrap" id="pagination-controls"></div>
</div>
</div>
</div>
<script>
function getAppBaseUrl() {
const origin = window.location.origin;
const segments = window.location.pathname.split('/').filter(Boolean);
const adminIndex = segments.indexOf('admin');
if (adminIndex > 0) {
return origin + '/' + segments.slice(0, adminIndex).join('/');
}
return origin;
}
const APP_BASE_URL = getAppBaseUrl();
const API_ALUMNI_URL = `${APP_BASE_URL}/api/admin/alumni`;
const LOGIN_URL = `${APP_BASE_URL}/login`;
const ALUMNI_EDIT_BASE_URL = `${APP_BASE_URL}/admin/alumni`;
let currentPage = 1;
let currentSearch = '';
let perPage = 7;
let searchTimer = null;
function getToken() {
return localStorage.getItem('access_token') ||
localStorage.getItem('token') ||
sessionStorage.getItem('access_token') ||
sessionStorage.getItem('token') ||
'';
}
function escapeHtml(value) {
if (value === null || value === undefined || value === '') return '-';
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#039;');
}
function getInitial(name) {
const cleanName = name || 'A';
return cleanName.charAt(0).toUpperCase();
}
async function parseJsonResponse(response) {
const text = await response.text();
try {
return text ? JSON.parse(text) : {};
} catch (error) {
return {
success: false,
message: text || response.statusText || 'Respon server tidak valid.'
};
}
}
function showLoading() {
document.getElementById('alumni-table-body').innerHTML = `
<tr>
<td colspan="6" class="loading-state-modern">
<i class="fas fa-spinner fa-spin me-2"></i> Memuat data alumni...
</td>
</tr>
`;
}
async function fetchAlumni(page = 1) {
const token = getToken();
const tbody = document.getElementById('alumni-table-body');
if (!token) {
window.location.href = LOGIN_URL;
return;
}
currentPage = page;
showLoading();
const params = new URLSearchParams({
page: page,
per_page: perPage
});
if (currentSearch !== '') {
params.append('search', currentSearch);
}
try {
const response = await fetch(`${API_ALUMNI_URL}?${params.toString()}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Accept': 'application/json'
}
});
const result = await parseJsonResponse(response);
if (!response.ok || result.success === false) {
throw new Error(result.message || 'Gagal memuat data alumni.');
}
const data = result.data || [];
tbody.innerHTML = '';
if (data.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="6" class="empty-state-modern">
<i class="fas fa-folder-open"></i>
Tidak ada data alumni yang ditemukan.
</td>
</tr>
`;
updatePaginationInfo(null);
renderPagination({ current_page: 1, last_page: 1, total: 0 });
return;
}
data.forEach((item, index) => {
const editUrl = `${ALUMNI_EDIT_BASE_URL}/${encodeURIComponent(item.id)}/edit`;
tbody.innerHTML += `
<tr>
<td class="text-center text-muted fw-bold">${(result.from || 1) + index}</td>
<td class="text-center fw-bold text-muted">${escapeHtml(item.id_alumni)}</td>
<td>
<div class="table-user-cell">
<div class="avatar-circle blue">${getInitial(item.nama_alumni)}</div>
<div>
<div class="fw-bold text-dark">${escapeHtml(item.nama_alumni)}</div>
<div class="text-cell-muted">Data alumni</div>
</div>
</div>
</td>
<td class="text-muted fw-semibold">${escapeHtml(item.perguruan_tinggi)}</td>
<td class="text-center">
<span class="badge-soft badge-kelas">${escapeHtml(item.jurusan_diambil)}</span>
</td>
<td class="text-center">
<div class="opsi-wrapper">
<a href="${editUrl}" class="btn-action btn-edit" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" onclick="deleteAlumni(${item.id})" class="btn-action btn-delete" title="Hapus">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
`;
});
updatePaginationInfo(result);
renderPagination(result);
} catch (error) {
tbody.innerHTML = `
<tr>
<td colspan="6" class="empty-state-modern text-danger">
<i class="fas fa-exclamation-circle"></i>
${escapeHtml(error.message || 'Gagal memuat data alumni.')}
</td>
</tr>
`;
}
}
function updatePaginationInfo(data) {
const info = document.getElementById('pagination-info');
if (!data || !data.total) {
info.innerText = 'Menampilkan 0 sampai 0 dari 0 data';
return;
}
info.innerText = `Menampilkan ${data.from || 0} sampai ${data.to || 0} dari ${data.total || 0} data`;
}
function renderPagination(data) {
const controls = document.getElementById('pagination-controls');
controls.innerHTML = '';
const current = Number(data.current_page || 1);
const last = Number(data.last_page || 1);
const total = Number(data.total || 0);
if (total === 0) return;
const createButton = (label, page, disabled = false, active = false) => {
const btn = document.createElement('button');
btn.className = `page-link-custom ${active ? 'active' : ''}`;
btn.innerHTML = label;
btn.disabled = disabled;
btn.onclick = () => {
if (!disabled && !active) fetchAlumni(page);
};
return btn;
};
controls.appendChild(createButton('<i class="fas fa-chevron-left"></i>', current - 1, current === 1));
let pages = [];
if (last <= 5) {
for (let i = 1; i <= last; i++) pages.push(i);
} else {
pages.push(1);
if (current > 3) pages.push('...');
const start = Math.max(2, current - 1);
const end = Math.min(last - 1, current + 1);
for (let i = start; i <= end; i++) pages.push(i);
if (current < last - 2) pages.push('...');
pages.push(last);
}
pages.forEach(page => {
if (page === '...') {
controls.appendChild(createButton('...', current, true));
} else {
controls.appendChild(createButton(page, page, false, page === current));
}
});
controls.appendChild(createButton('<i class="fas fa-chevron-right"></i>', current + 1, current === last));
}
async function deleteAlumni(id) {
const confirmResult = await Swal.fire({
title: 'Hapus data?',
text: 'Data alumni akan dihapus permanen!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ef4444',
cancelButtonColor: '#64748b',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
});
if (!confirmResult.isConfirmed) return;
const token = getToken();
try {
const response = await fetch(`${API_ALUMNI_URL}/${encodeURIComponent(id)}`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer ' + token,
'Accept': 'application/json'
}
});
const result = await parseJsonResponse(response);
if (response.ok && result.success !== false) {
Swal.fire('Berhasil!', result.message || 'Data alumni berhasil dihapus.', 'success');
fetchAlumni(currentPage);
} else {
Swal.fire('Gagal!', result.message || 'Data alumni gagal dihapus.', 'error');
}
} catch (error) {
Swal.fire('Error!', error.message || 'Koneksi ke server gagal.', 'error');
}
}
document.addEventListener('DOMContentLoaded', function () {
fetchAlumni(1);
document.getElementById('searchInput').addEventListener('input', function () {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
currentSearch = this.value.trim();
fetchAlumni(1);
}, 350);
});
document.getElementById('perPageSelect').addEventListener('change', function () {
perPage = this.value;
fetchAlumni(1);
});
});
</script>
@endsection