TIF_NGANJUK_E41220461/resources/views/admin/manajemen-prestasi/index.blade.php

1888 lines
73 KiB
PHP

@extends('layouts.app')
@section('title', 'Manajemen Prestasi')
@push('styles')
<style>
/* Manajemen Prestasi Container */
.manajemen-prestasi-container {
max-width: 1400px;
margin: 0 auto;
}
/* Page Header */
.page-header {
padding: 20px 0;
}
.page-title {
font-family: 'Poppins', sans-serif;
font-weight: 700;
font-size: 2rem;
color: var(--dark);
margin-bottom: 0.5rem;
}
.page-subtitle {
color: #6c757d;
font-size: 1.1rem;
margin: 0;
}
/* Statistics Cards */
.stat-card {
background: white;
border-radius: var(--border-radius);
padding: 25px;
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 20px;
transition: var(--transition);
border-left: 4px solid transparent;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
}
.stat-card.primary {
border-left-color: var(--primary);
}
.stat-card.success {
border-left-color: #28a745;
}
.stat-card.warning {
border-left-color: #ffc107;
}
.stat-card.info {
border-left-color: #17a2b8;
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: white;
}
.stat-card.primary .stat-icon {
background: var(--gradient);
}
.stat-card.success .stat-icon {
background: linear-gradient(135deg, #28a745, #20c997);
}
.stat-card.warning .stat-icon {
background: linear-gradient(135deg, #ffc107, #fd7e14);
}
.stat-card.info .stat-icon {
background: linear-gradient(135deg, #17a2b8, #6f42c1);
}
.stat-content {
flex: 1;
}
.stat-value {
font-family: 'Poppins', sans-serif;
font-weight: 700;
font-size: 2rem;
color: var(--dark);
margin: 0;
line-height: 1;
}
.stat-label {
color: #6c757d;
font-size: 0.9rem;
margin: 5px 0 0 0;
font-weight: 500;
}
/* Prestasi Grid Card */
.prestasi-grid-card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
transition: var(--transition);
border: 1px solid rgba(0, 0, 0, 0.05);
}
.prestasi-grid-card:hover {
box-shadow: var(--shadow-lg);
}
.prestasi-grid-card .card-header {
padding: 25px 30px 0;
border: none;
background: none;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
}
.prestasi-grid-card .card-body {
padding: 25px 30px 30px;
}
.header-content {
display: flex;
align-items: center;
flex: 1;
}
.header-icon {
font-size: 1.8rem;
color: var(--primary);
margin-right: 15px;
width: 50px;
height: 50px;
background: linear-gradient(135deg, var(--primary-light), var(--primary));
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.card-title {
font-family: 'Poppins', sans-serif;
font-weight: 600;
font-size: 1.3rem;
color: var(--dark);
margin: 0;
}
.card-subtitle {
color: #6c757d;
font-size: 0.9rem;
margin: 5px 0 0 0;
}
/* Header Actions */
.header-actions {
display: flex;
align-items: center;
gap: 15px;
}
/* View Toggle */
.view-toggle {
display: flex;
gap: 5px;
background: #f8f9fa;
padding: 5px;
border-radius: 8px;
}
.view-btn {
width: 40px;
height: 40px;
border: none;
background: transparent;
border-radius: 6px;
color: #6c757d;
transition: var(--transition);
display: flex;
align-items: center;
justify-content: center;
}
.view-btn:hover {
background: white;
color: var(--primary);
}
.view-btn.active {
background: var(--primary);
color: white;
}
/* Search Box */
.search-box {
position: relative;
min-width: 250px;
}
.search-box i {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: #6c757d;
}
.search-box input {
width: 100%;
padding: 12px 15px 12px 45px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 0.95rem;
transition: var(--transition);
}
.search-box input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 0.2rem rgba(0, 102, 204, 0.1);
outline: none;
}
/* Prestasi Grid */
.prestasi-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 25px;
}
.prestasi-card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
transition: var(--transition);
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.prestasi-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.prestasi-image {
position: relative;
height: 220px;
overflow: hidden;
}
.prestasi-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: var(--transition);
}
.prestasi-card:hover .prestasi-image img {
transform: scale(1.05);
}
.prestasi-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 0;
transition: var(--transition);
overflow-y: auto;
padding: 20px;
text-align: center;
color: white;
z-index: 2;
}
.prestasi-card:hover .prestasi-overlay {
opacity: 1;
}
.prestasi-overlay::-webkit-scrollbar {
width: 6px;
}
.prestasi-overlay::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
.prestasi-overlay::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
}
.prestasi-overlay::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.prestasi-badges {
position: absolute;
top: 10px;
left: 10px;
display: flex;
gap: 5px;
flex-direction: column;
}
.badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-urutan {
background: rgba(0, 0, 0, 0.8);
color: white;
}
.badge-active {
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
}
.badge-inactive {
background: #6c757d;
color: white;
}
/* Prestasi Content */
.prestasi-content {
padding: 20px;
}
.prestasi-title {
font-weight: 600;
color: var(--dark);
margin: 0 0 15px 0;
font-size: 1.1rem;
line-height: 1.4;
height: 3em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.prestasi-info {
margin-bottom: 15px;
}
.info-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-size: 0.9rem;
}
.info-item i {
width: 16px;
color: var(--primary);
text-align: center;
}
.info-item span {
color: #6c757d;
}
.prestasi-actions {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #e9ecef;
padding-top: 15px;
}
/* Form Switch */
.form-check.form-switch {
display: flex;
align-items: center;
gap: 10px;
margin: 0;
}
.form-check-input {
width: 3em;
height: 1.5em;
margin: 0;
}
.form-check-label {
font-size: 0.9rem;
color: var(--dark);
font-weight: 500;
}
/* Action Buttons */
.action-buttons {
display: flex;
gap: 8px;
}
.action-buttons .btn {
width: 36px;
height: 36px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
border: 2px solid;
transition: var(--transition);
}
.btn-edit {
color: var(--primary);
border-color: var(--primary) !important;
background: transparent;
}
.btn-edit:hover {
background: var(--primary);
color: white;
transform: translateY(-2px);
}
.btn-view {
color: #17a2b8;
border-color: #17a2b8 !important;
background: transparent;
}
.btn-view:hover {
background: #17a2b8;
color: white;
transform: translateY(-2px);
}
.btn-delete {
color: #dc3545;
border-color: #dc3545 !important;
background: transparent;
}
.btn-delete:hover {
background: #dc3545;
color: white;
transform: translateY(-2px);
}
/* Prestasi List View */
.prestasi-list .prestasi-thumb {
width: 80px;
height: 60px;
border-radius: 8px;
overflow: hidden;
}
.prestasi-list .prestasi-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.prestasi-list .prestasi-info-list h6 {
margin: 0;
font-weight: 600;
font-size: 0.95rem;
}
.student-info {
font-size: 0.9rem;
}
.achievement-info {
font-size: 0.9rem;
}
.urutan-badge {
background: var(--primary);
color: white;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 600;
}
/* Empty State */
.empty-state {
padding: 60px 20px;
text-align: center;
}
.empty-state h4 {
color: var(--dark);
margin-bottom: 10px;
font-weight: 600;
}
.empty-state p {
color: #6c757d;
max-width: 400px;
margin: 0 auto;
}
/* Modal Styles */
.modal-content {
border: none;
border-radius: var(--border-radius);
box-shadow: var(--shadow-xl);
}
.modal-header {
border-bottom: 1px solid #e9ecef;
padding: 25px 30px 20px;
}
.modal-title {
font-family: 'Poppins', sans-serif;
font-weight: 600;
color: var(--dark);
display: flex;
align-items: center;
}
.modal-body {
padding: 25px 30px;
}
.modal-footer {
border-top: 1px solid #e9ecef;
padding: 20px 30px 25px;
}
/* Prestasi Upload Area */
.prestasi-upload-area {
position: relative;
}
.upload-wrapper {
border: 2px dashed #dee2e6;
border-radius: var(--border-radius);
padding: 40px 20px;
text-align: center;
cursor: pointer;
transition: var(--transition);
background: #f8f9fa;
}
.upload-wrapper:hover {
border-color: var(--primary);
background: #f0f4ff;
}
.upload-wrapper.dragover {
border-color: var(--primary);
background: #e3f2fd;
}
.upload-placeholder i {
font-size: 3rem;
color: #6c757d;
margin-bottom: 15px;
}
.upload-placeholder p {
font-weight: 600;
color: var(--dark);
margin: 0 0 10px 0;
}
.upload-placeholder small {
color: #6c757d;
line-height: 1.4;
}
.upload-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
/* Image Preview */
.image-preview {
position: relative;
border: 2px solid #e9ecef;
border-radius: var(--border-radius);
overflow: hidden;
margin-top: 15px;
}
.image-preview img {
width: 100%;
height: 200px;
object-fit: cover;
}
.btn-remove-preview {
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
border: none;
background: rgba(220, 53, 69, 0.9);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
transition: var(--transition);
}
.btn-remove-preview:hover {
background: #dc3545;
transform: scale(1.1);
}
/* Current Image Preview */
.current-image-preview {
border: 2px solid #e9ecef;
border-radius: var(--border-radius);
overflow: hidden;
}
.current-image-preview img {
width: 100%;
height: 150px;
object-fit: cover;
}
/* Prestasi Detail View */
.prestasi-detail-image img {
width: 100%;
height: 300px;
object-fit: cover;
}
.prestasi-detail-info {
padding: 20px 0;
}
.detail-item {
margin-bottom: 15px;
}
.detail-label {
font-weight: 600;
color: var(--dark);
display: block;
margin-bottom: 5px;
}
.detail-content {
color: #6c757d;
margin: 0;
}
/* Form Styles */
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
font-weight: 600;
color: var(--dark);
margin-bottom: 8px;
display: block;
}
.form-control {
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 12px 15px;
transition: var(--transition);
font-size: 0.95rem;
}
.form-control:focus {
border-color: var(--primary);
box-shadow: 0 0 0 0.2rem rgba(0, 102, 204, 0.1);
}
textarea.form-control {
resize: vertical;
min-height: 100px;
}
.form-text {
font-size: 0.85rem;
}
/* Buttons */
.btn {
padding: 12px 25px;
border-radius: 8px;
font-weight: 500;
transition: var(--transition);
border: none;
}
.btn-primary {
background: var(--gradient);
border: none;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 102, 204, 0.3);
}
.btn-secondary {
background: #6c757d;
border: none;
}
.btn-secondary:hover {
background: #5a6268;
transform: translateY(-2px);
}
/* Loading State */
.btn-loading {
position: relative;
color: transparent !important;
}
.btn-loading::after {
content: '';
position: absolute;
width: 20px;
height: 20px;
top: 50%;
left: 50%;
margin-left: -10px;
margin-top: -10px;
border: 2px solid #ffffff;
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Responsive Design */
@media (max-width: 991.98px) {
.page-header {
padding: 15px 0;
}
.page-title {
font-size: 1.7rem;
}
.stat-card {
padding: 20px;
}
.stat-value {
font-size: 1.7rem;
}
.prestasi-grid {
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.prestasi-grid-card .card-header {
padding: 20px 25px 0;
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.header-content {
width: 100%;
}
.header-actions {
width: 100%;
justify-content: space-between;
}
.search-box {
min-width: 200px;
}
}
@media (max-width: 767.98px) {
.prestasi-grid {
grid-template-columns: 1fr;
}
.modal-body {
padding: 20px 25px;
}
.modal-header,
.modal-footer {
padding: 20px 25px;
}
.prestasi-list .table-responsive {
font-size: 0.9rem;
}
.header-actions {
flex-direction: column;
gap: 10px;
align-items: stretch;
}
.search-box {
min-width: 100%;
}
.prestasi-detail-info {
padding: 15px 0 0 0;
}
}
@media (max-width: 575.98px) {
.manajemen-prestasi-container {
padding: 0 15px;
}
.page-title {
font-size: 1.5rem;
}
.stat-card {
flex-direction: column;
text-align: center;
gap: 15px;
}
.upload-placeholder {
padding: 30px 15px;
}
.upload-placeholder i {
font-size: 2.5rem;
}
.modal-body .row {
flex-direction: column;
}
.modal-body .col-md-6 {
width: 100%;
}
.action-buttons {
justify-content: center;
}
.prestasi-title {
height: auto;
-webkit-line-clamp: 3;
line-clamp: 3;
}
}
</style>
@endpush
@section('content')
<link rel="stylesheet" href="{{ asset('css/manajemen-prestasi.css') }}">
<div class="manajemen-prestasi-container">
<div class="page-header mb-4">
<div class="row align-items-center">
<div class="col">
<h1 class="page-title">Manajemen Prestasi</h1>
<p class="page-subtitle">Kelola data prestasi siswa SMK Muhammadiyah 1 Berbek</p>
</div>
<div class="col-auto">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#tambahPrestasiModal">
<i class="fas fa-plus me-2"></i>Tambah Prestasi
</button>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-3">
<div class="stat-card primary">
<div class="stat-icon">
<i class="fas fa-trophy"></i>
</div>
<div class="stat-content">
<h3 class="stat-value">{{ $prestasis->count() }}</h3>
<p class="stat-label">Total Prestasi</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-card success">
<div class="stat-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-content">
<h3 class="stat-value">{{ $prestasis->where('status', true)->count() }}</h3>
<p class="stat-label">Prestasi Aktif</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-card warning">
<div class="stat-icon">
<i class="fas fa-clock"></i>
</div>
<div class="stat-content">
<h3 class="stat-value">{{ $prestasis->where('status', false)->count() }}</h3>
<p class="stat-label">Prestasi Nonaktif</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="stat-card info">
<div class="stat-icon">
<i class="fas fa-sort-amount-down"></i>
</div>
<div class="stat-content">
<h3 class="stat-value">{{ $prestasis->max('urutan') ?? 0 }}</h3>
<p class="stat-label">Urutan Tertinggi</p>
</div>
</div>
</div>
</div>
<div class="prestasi-grid-card">
<div class="card-header">
<div class="header-content">
<i class="fas fa-award header-icon"></i>
<div>
<h3 class="card-title">Daftar Prestasi Siswa</h3>
<p class="card-subtitle">Kelola urutan dan tampilan prestasi di halaman utama</p>
</div>
</div>
<div class="header-actions">
<div class="view-toggle">
<button class="view-btn active" data-view="grid">
<i class="fas fa-th-large"></i>
</button>
<button class="view-btn" data-view="list">
<i class="fas fa-list"></i>
</button>
</div>
<div class="search-box">
<i class="fas fa-search"></i>
<input type="text" id="searchInput" placeholder="Cari prestasi...">
</div>
</div>
</div>
<div class="card-body">
@if ($prestasis->isEmpty())
<div class="empty-state">
<i class="fas fa-trophy fa-4x text-muted mb-4"></i>
<h4>Belum ada data prestasi</h4>
<p class="text-muted">Tambahkan data prestasi pertama untuk ditampilkan di website.</p>
<button class="btn btn-primary mt-3" data-bs-toggle="modal" data-bs-target="#tambahPrestasiModal">
<i class="fas fa-plus me-2"></i>Tambah Prestasi Pertama
</button>
</div>
@else
<div class="prestasi-grid" id="prestasiGridView">
@foreach ($prestasis as $item)
<div class="prestasi-card" data-prestasi-id="{{ $item->id }}">
<div class="prestasi-image">
<img src="{{ $item->foto_prestasi_url }}" class="card-img-top"
alt="{{ $item->nama_prestasi }}"
onerror="this.onerror=null;this.src='{{ asset('images/default-img.png') }}'">
<div class="prestasi-overlay">
<div class="action-buttons">
<button class="btn btn-sm btn-edit" onclick="editPrestasi({{ $item->id }})"
title="Edit Prestasi">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-view" onclick="viewPrestasi({{ $item->id }})"
title="Lihat Detail">
<i class="fas fa-eye"></i>
</button>
<button class="btn btn-sm btn-delete"
onclick="deletePrestasi({{ $item->id }})" title="Hapus Prestasi">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="prestasi-badges">
<span class="badge badge-urutan">#{{ $item->urutan }}</span>
@if ($item->status)
<span class="badge badge-active">Aktif</span>
@else
<span class="badge badge-inactive">Nonaktif</span>
@endif
</div>
</div>
<div class="prestasi-content">
<h6 class="prestasi-title">{{ $item->nama_prestasi }}</h6>
<div class="student-info">
<div class="info-item">
<i class="fas fa-user"></i>
<span>{{ $item->nama_siswa }}</span>
</div>
<div class="info-item">
<i class="fas fa-graduation-cap"></i>
<span>{{ $item->jurusan->nama_jurusan ?? '-' }}</span>
</div>
<div class="info-item">
<i class="fas fa-calendar-alt"></i>
<span>Tahun {{ $item->tahun_prestasi }}</span>
</div>
</div>
<div class="prestasi-actions">
<div class="form-check form-switch">
<input class="form-check-input status-toggle" type="checkbox"
data-prestasi-id="{{ $item->id }}"
{{ $item->status ? 'checked' : '' }}>
<label class="form-check-label">Status Aktif</label>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div class="prestasi-list d-none" id="prestasiListView">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Foto</th>
<th>Nama Siswa & Jurusan</th>
<th>Nama Prestasi</th>
<th>Peringkat & Tahun</th>
<th>Status</th>
<th>Urutan</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($prestasis as $item)
<tr data-prestasi-id="{{ $item->id }}">
<td>
<div class="prestasi-img-wrapper">
<img src="{{ $item->foto_prestasi_url }}" class="prestasi-img"
alt="{{ $item->nama_prestasi }}"
onerror="this.onerror=null;this.src='{{ asset('images/default-img.png') }}'">
</div>
</td>
<td>
<div class="student-info">
<strong>{{ $item->nama_siswa }}</strong>
<br>
<small
class="text-muted">{{ $item->jurusan->nama_jurusan ?? '-' }}</small>
</div>
</td>
<td>
<div class="prestasi-name">{{ $item->nama_prestasi }}</div>
</td>
<td>
<div class="achievement-info">
<span class="badge bg-warning">{{ $item->peringkat }}</span>
<br>
<small class="text-muted">{{ $item->tahun_prestasi }}</small>
</div>
</td>
<td>
<div class="form-check form-switch">
<input class="form-check-input status-toggle" type="checkbox"
data-prestasi-id="{{ $item->id }}"
{{ $item->status ? 'checked' : '' }}>
</div>
</td>
<td>
<span class="urutan-badge">#{{ $item->urutan }}</span>
</td>
<td>
<div class="action-buttons">
<button class="btn btn-sm btn-edit"
onclick="editPrestasi({{ $item->id }})"
title="Edit Prestasi">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-view"
onclick="viewPrestasi({{ $item->id }})" title="Lihat Detail">
<i class="fas fa-eye"></i>
</button>
<button class="btn btn-sm btn-delete"
onclick="deletePrestasi({{ $item->id }})"
title="Hapus Prestasi">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
</div>
</div>
</div>
<!-- Modal Tambah Prestasi -->
<div class="modal fade" id="tambahPrestasiModal" tabindex="-1" aria-labelledby="tambahPrestasiModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="tambahPrestasiModalLabel">
<i class="fas fa-plus me-2"></i>Tambah Prestasi Siswa
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="tambahPrestasiForm" enctype="multipart/form-data">
@csrf
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="tambahNamaSiswa" class="form-label">Nama Siswa *</label>
<input type="text" class="form-control" id="tambahNamaSiswa" name="nama_siswa"
placeholder="Masukkan nama siswa" required>
<div class="invalid-feedback" id="tambahNamaSiswa_error"></div>
</div>
<div class="form-group">
<label for="tambahJurusan" class="form-label">Jurusan *</label>
<select class="form-select" id="tambahJurusan" name="jurusan_id" required>
<option value="">Pilih Jurusan</option>
@foreach ($jurusans as $j)
<option value="{{ $j->id }}">{{ $j->nama_jurusan }}</option>
@endforeach
</select>
<div class="invalid-feedback" id="tambahJurusan_id_error"></div>
</div>
<div class="form-group">
<label for="tambahNamaPrestasi" class="form-label">Nama Prestasi *</label>
<input type="text" class="form-control" id="tambahNamaPrestasi"
name="nama_prestasi" placeholder="Contoh: Juara 1 LKS Tingkat Provinsi" required>
<div class="invalid-feedback" id="tambahNamaPrestasi_error"></div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="tambahPeringkat" class="form-label">Peringkat *</label>
<input type="text" class="form-control" id="tambahPeringkat"
name="peringkat" placeholder="Contoh: Juara 1" required>
<div class="invalid-feedback" id="tambahPeringkat_error"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="tambahTahunPrestasi" class="form-label">Tahun *</label>
<input type="number" class="form-control" id="tambahTahunPrestasi"
name="tahun_prestasi" value="{{ date('Y') }}" min="2000"
max="{{ date('Y') + 1 }}" required>
<div class="invalid-feedback" id="tambahTahunPrestasi_error"></div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="tambahFotoPrestasi" class="form-label">Foto Prestasi *</label>
<div class="prestasi-upload-area">
<div class="upload-wrapper" id="tambahUploadWrapper">
<div class="upload-placeholder" id="tambahUploadPreview">
<i class="fas fa-cloud-upload-alt"></i>
<p>Klik atau drag foto di sini</p>
<small>Maksimal 5MB (JPG, PNG, WEBP)</small>
</div>
<input type="file" name="foto_prestasi" id="tambahFotoPrestasi"
class="upload-input" accept="image/*" required>
</div>
<div class="image-preview d-none" id="tambahImagePreview">
<img src="" alt="Preview" id="tambahPreviewImage">
<button type="button" class="btn-remove-preview"
onclick="removePreview('tambah')">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="invalid-feedback" id="tambahFotoPrestasi_error"></div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="tambahUrutan" class="form-label">Urutan Tampil</label>
<input type="number" class="form-control" id="tambahUrutan" name="urutan"
value="{{ $prestasi->max('urutan') + 1 }}" min="0" readonly>
<div class="invalid-feedback" id="tambahUrutan_error"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Status</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="tambahStatus"
name="status" checked>
<label class="form-check-label" for="tambahStatus">Aktif</label>
</div>
</div>
</div>
</div>
</div>
</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" id="tambahPrestasiBtn">
<i class="fas fa-save me-2"></i>Simpan Prestasi
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Edit Prestasi -->
<div class="modal fade" id="editPrestasiModal" tabindex="-1" aria-labelledby="editPrestasiModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editPrestasiModalLabel">
<i class="fas fa-edit me-2"></i>Edit Prestasi Siswa
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="editPrestasiForm" enctype="multipart/form-data">
@csrf
@method('PUT')
<input type="hidden" id="editPrestasiId" name="id">
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="editNamaSiswa" class="form-label">Nama Siswa *</label>
<input type="text" class="form-control" id="editNamaSiswa" name="nama_siswa"
placeholder="Masukkan nama siswa" required>
<div class="invalid-feedback" id="editNamaSiswa_error"></div>
</div>
<div class="form-group">
<label for="editJurusan" class="form-label">Jurusan *</label>
<select class="form-select" id="editJurusan" name="jurusan_id" required>
<option value="">Pilih Jurusan</option>
@foreach ($jurusans as $j)
<option value="{{ $j->id }}">{{ $j->nama_jurusan }}</option>
@endforeach
</select>
<div class="invalid-feedback" id="editJurusan_id_error"></div>
</div>
<div class="form-group">
<label for="editNamaPrestasi" class="form-label">Nama Prestasi *</label>
<input type="text" class="form-control" id="editNamaPrestasi"
name="nama_prestasi" placeholder="Contoh: Juara 1 LKS Tingkat Provinsi" required>
<div class="invalid-feedback" id="editNamaPrestasi_error"></div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="editPeringkat" class="form-label">Peringkat *</label>
<input type="text" class="form-control" id="editPeringkat"
name="peringkat" placeholder="Contoh: Juara 1" required>
<div class="invalid-feedback" id="editPeringkat_error"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="editTahunPrestasi" class="form-label">Tahun *</label>
<input type="number" class="form-control" id="editTahunPrestasi"
name="tahun_prestasi" min="2000" max="{{ date('Y') + 1 }}" required>
<div class="invalid-feedback" id="editTahunPrestasi_error"></div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Foto Saat Ini</label>
<div class="current-image-preview">
<img src="" alt="Current Prestasi" id="editCurrentImage"
onerror="this.onerror=null;this.src='{{ asset('images/default-img.png') }}'">
</div>
<div class="prestasi-upload-area mt-3">
<div class="upload-wrapper" id="editUploadWrapper">
<div class="upload-placeholder" id="editUploadPreview">
<i class="fas fa-camera"></i>
<p>Klik untuk ubah foto</p>
</div>
<input type="file" name="foto_prestasi" id="editFotoPrestasi"
class="upload-input" accept="image/*">
</div>
<div class="image-preview d-none" id="editImagePreview">
<img src="" alt="Preview" id="editPreviewImage">
<button type="button" class="btn-remove-preview"
onclick="removePreview('edit')">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<small class="text-muted">Biarkan kosong jika tidak ingin mengubah foto</small>
<div class="invalid-feedback" id="editFotoPrestasi_error"></div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="editUrutan" class="form-label">Urutan Tampil</label>
<input type="number" class="form-control" id="editUrutan" name="urutan"
min="0" readonly>
<div class="invalid-feedback" id="editUrutan_error"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Status</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="editStatus"
name="status" value="1">
<label class="form-check-label" for="editStatus">Aktif</label>
</div>
</div>
</div>
</div>
</div>
</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" id="editPrestasiBtn">
<i class="fas fa-save me-2"></i>Simpan Perubahan
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal View Prestasi -->
<div class="modal fade" id="viewPrestasiModal" tabindex="-1" aria-labelledby="viewPrestasiModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="viewPrestasiModalLabel">
<i class="fas fa-eye me-2"></i>Detail Prestasi Siswa
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-5">
<div class="view-image-container">
<img src="" alt="Prestasi Detail" id="viewPrestasiImage"
class="img-fluid rounded shadow-sm"
onerror="this.onerror=null;this.src='{{ asset('images/default-img.png') }}'">
</div>
</div>
<div class="col-md-7">
<div class="view-details">
<h4 id="viewPrestasiTitle" class="mb-3"></h4>
<div class="detail-item">
<label class="detail-label">Nama Siswa:</label>
<p id="viewPrestasiSiswa" class="detail-content"></p>
</div>
<div class="detail-item">
<label class="detail-label">Jurusan:</label>
<p id="viewPrestasiJurusan" class="detail-content"></p>
</div>
<div class="row">
<div class="col-6">
<div class="detail-item">
<label class="detail-label">Peringkat:</label>
<p id="viewPrestasiPeringkat" class="detail-content"></p>
</div>
</div>
<div class="col-6">
<div class="detail-item">
<label class="detail-label">Tahun:</label>
<p id="viewPrestasiTahun" class="detail-content"></p>
</div>
</div>
</div>
<div class="detail-item">
<label class="detail-label">Urutan Tampil:</label>
<p id="viewPrestasiUrutan" class="detail-content"></p>
</div>
<div class="row mt-3">
<div class="col-6">
<small class="text-muted d-block">Dibuat pada:</small>
<small id="viewPrestasiCreated" class="fw-bold"></small>
</div>
<div class="col-6">
<small class="text-muted d-block">Terakhir diupdate:</small>
<small id="viewPrestasiUpdated" class="fw-bold"></small>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
initManajemenPrestasi();
});
function initManajemenPrestasi() {
initUploadPreview();
initFormSubmissions();
initViewToggle();
initStatusToggles();
initSearch();
}
function initUploadPreview() {
const tambahInput = document.getElementById('tambahFotoPrestasi');
const tambahUploadWrapper = document.getElementById('tambahUploadWrapper');
const tambahImagePreview = document.getElementById('tambahImagePreview');
const tambahPreviewImage = document.getElementById('tambahPreviewImage');
if (tambahInput) {
tambahInput.addEventListener('change', function() {
handleFileSelect(this, tambahUploadWrapper, tambahImagePreview, tambahPreviewImage);
});
}
const editInput = document.getElementById('editFotoPrestasi');
const editUploadWrapper = document.getElementById('editUploadWrapper');
const editImagePreview = document.getElementById('editImagePreview');
const editPreviewImage = document.getElementById('editPreviewImage');
if (editInput) {
editInput.addEventListener('change', function() {
handleFileSelect(this, editUploadWrapper, editImagePreview, editPreviewImage);
});
}
}
function handleFileSelect(input, wrapper, preview, img) {
const file = input.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
img.src = e.target.result;
wrapper.classList.add('d-none');
preview.classList.remove('d-none');
}
reader.readAsDataURL(file);
}
}
function removePreview(type) {
const wrapper = document.getElementById(`${type}UploadWrapper`);
const preview = document.getElementById(`${type}ImagePreview`);
const input = document.getElementById(`${type}FotoPrestasi`);
if (wrapper) wrapper.classList.remove('d-none');
if (preview) preview.classList.add('d-none');
if (input) input.value = '';
}
function initViewToggle() {
const viewBtns = document.querySelectorAll('.view-btn');
const gridView = document.getElementById('prestasiGridView');
const listView = document.getElementById('prestasiListView');
viewBtns.forEach(btn => {
btn.addEventListener('click', function() {
const view = this.getAttribute('data-view');
viewBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
if (view === 'grid') {
gridView.classList.remove('d-none');
listView.classList.add('d-none');
} else {
gridView.classList.add('d-none');
listView.classList.remove('d-none');
}
});
});
}
function initStatusToggles() {
const toggles = document.querySelectorAll('.status-toggle');
toggles.forEach(toggle => {
toggle.addEventListener('change', function() {
const id = this.getAttribute('data-prestasi-id');
const status = this.checked;
updateStatus(id, status);
});
});
}
function initSearch() {
const searchInput = document.getElementById('searchInput');
if (!searchInput) return;
searchInput.addEventListener('input', function() {
const term = this.value.toLowerCase();
const cards = document.querySelectorAll('.prestasi-card');
const rows = document.querySelectorAll('tbody tr');
cards.forEach(card => {
const text = card.textContent.toLowerCase();
card.style.display = text.includes(term) ? '' : 'none';
});
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(term) ? '' : 'none';
});
});
}
function initFormSubmissions() {
const tambahForm = document.getElementById('tambahPrestasiForm');
if (tambahForm) {
tambahForm.addEventListener('submit', function(e) {
e.preventDefault();
storePrestasi();
});
}
const editForm = document.getElementById('editPrestasiForm');
if (editForm) {
editForm.addEventListener('submit', function(e) {
e.preventDefault();
updatePrestasi();
});
}
}
function storePrestasi() {
const form = document.getElementById('tambahPrestasiForm');
const formData = new FormData(form);
const submitBtn = document.getElementById('tambahPrestasiBtn');
clearErrors('tambah');
submitBtn.classList.add('btn-loading');
submitBtn.disabled = true;
fetch('{{ route('admin.manajemen-prestasi.store') }}', {
method: 'POST',
body: formData,
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
timer: 2000,
showConfirmButton: false
}).then(() => {
window.location.reload();
});
} else {
showErrors(data.errors, 'tambah');
if (data.message && !data.errors) {
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: data.message
});
}
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan sistem.'
});
})
.finally(() => {
submitBtn.classList.remove('btn-loading');
submitBtn.disabled = false;
});
}
function editPrestasi(prestasiId) {
const editPrestasiBtn = document.getElementById('editPrestasiBtn');
editPrestasiBtn.classList.add('btn-loading');
editPrestasiBtn.disabled = true;
fetch(`/admin/manajemen-prestasi/${prestasiId}`, {
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const prestasi = data.prestasi;
document.getElementById('editPrestasiId').value = prestasi.id;
document.getElementById('editNamaSiswa').value = prestasi.nama_siswa;
document.getElementById('editJurusan').value = prestasi.jurusan_id;
document.getElementById('editNamaPrestasi').value = prestasi.nama_prestasi;
document.getElementById('editPeringkat').value = prestasi.peringkat;
document.getElementById('editTahunPrestasi').value = prestasi.tahun_prestasi;
document.getElementById('editUrutan').value = prestasi.urutan;
document.getElementById('editStatus').checked = prestasi.status;
document.getElementById('editCurrentImage').src = prestasi.foto_prestasi_url;
removePreview('edit');
const modal = new bootstrap.Modal(document.getElementById('editPrestasiModal'));
modal.show();
} else {
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Gagal memuat data prestasi.'
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan saat memuat data prestasi.'
});
})
.finally(() => {
editPrestasiBtn.classList.remove('btn-loading');
editPrestasiBtn.disabled = false;
});
}
function viewPrestasi(prestasiId) {
fetch(`/admin/manajemen-prestasi/${prestasiId}`, {
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const prestasi = data.prestasi;
document.getElementById('viewPrestasiImage').src = prestasi.foto_prestasi_url;
document.getElementById('viewPrestasiTitle').textContent = prestasi.nama_prestasi;
document.getElementById('viewPrestasiSiswa').textContent = prestasi.nama_siswa;
document.getElementById('viewPrestasiJurusan').textContent = prestasi.nama_jurusan;
document.getElementById('viewPrestasiPeringkat').textContent = prestasi.peringkat;
document.getElementById('viewPrestasiTahun').textContent = prestasi.tahun_prestasi;
document.getElementById('viewPrestasiUrutan').textContent = '#' + prestasi.urutan;
document.getElementById('viewPrestasiCreated').textContent = prestasi.created_at;
document.getElementById('viewPrestasiUpdated').textContent = prestasi.updated_at;
const modal = new bootstrap.Modal(document.getElementById('viewPrestasiModal'));
modal.show();
} else {
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Gagal memuat data prestasi.'
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan saat memuat data prestasi.'
});
});
}
function updatePrestasi() {
const form = document.getElementById('editPrestasiForm');
const formData = new FormData(form);
const id = document.getElementById('editPrestasiId').value;
const submitBtn = document.getElementById('editPrestasiBtn');
clearErrors('edit');
submitBtn.classList.add('btn-loading');
submitBtn.disabled = true;
fetch(`/admin/manajemen-prestasi/${id}`, {
method: 'POST',
body: formData,
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'X-HTTP-Method-Override': 'PUT',
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
timer: 2000,
showConfirmButton: false
}).then(() => {
window.location.reload();
});
} else {
showErrors(data.errors, 'edit');
if (data.message && !data.errors) {
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: data.message
});
}
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan saat memperbarui prestasi.'
});
})
.finally(() => {
submitBtn.classList.remove('btn-loading');
submitBtn.disabled = false;
});
}
function updateStatus(id, status) {
fetch(`/admin/manajemen-prestasi/${id}/status`, {
method: 'PATCH',
body: JSON.stringify({
status: status
}),
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (!data.success) {
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: data.message
});
window.location.reload();
}
})
.catch(error => {
console.error('Error:', error);
window.location.reload();
});
}
function deletePrestasi(id) {
Swal.fire({
title: 'Hapus Prestasi?',
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
fetch(`/admin/manajemen-prestasi/${id}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
'content'),
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
timer: 2000,
showConfirmButton: false
}).then(() => {
window.location.reload();
});
} else {
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: data.message
});
}
});
}
});
}
function showErrors(errors, type) {
for (const key in errors) {
// Map validation fields to element IDs
let field = key;
if (key === 'jurusan_id') field = 'jurusan';
if (key === 'foto_prestasi') field = 'fotoPrestasi';
if (key === 'nama_siswa') field = 'namaSiswa';
if (key === 'nama_prestasi') field = 'namaPrestasi';
if (key === 'tahun_prestasi') field = 'tahunPrestasi';
const id = type + field.charAt(0).toUpperCase() + field.slice(1);
const input = document.getElementById(id);
const errorEl = document.getElementById(id + '_error');
if (input) input.classList.add('is-invalid');
if (errorEl) errorEl.textContent = errors[key][0];
}
}
function clearErrors(type) {
const inputs = document.querySelectorAll(
`#${type}PrestasiForm .form-control, #${type}PrestasiForm .form-select`);
const errors = document.querySelectorAll(`#${type}PrestasiForm .invalid-feedback`);
inputs.forEach(input => input.classList.remove('is-invalid'));
errors.forEach(error => error.textContent = '');
}
window.editPrestasi = editPrestasi;
window.viewPrestasi = viewPrestasi;
window.deletePrestasi = deletePrestasi;
window.removePreview = removePreview;
</script>
@endsection