This commit is contained in:
ell 2026-06-09 21:05:50 +07:00
parent 54a4a3a44b
commit 1f9a350251
15 changed files with 487 additions and 141 deletions

View File

@ -58,6 +58,7 @@ public function create()
$data_pelatih = Anggota::whereHas('roles', function($query) { $data_pelatih = Anggota::whereHas('roles', function($query) {
$query->where('nama_role', 'Pelatih'); $query->where('nama_role', 'Pelatih');
}) })
->with('kolatLatihan')
->where('status', 'Aktif') ->where('status', 'Aktif')
->orderBy('nama_lengkap', 'asc') ->orderBy('nama_lengkap', 'asc')
->get(); ->get();
@ -100,7 +101,11 @@ public function edit($id)
// Tetap filter agar hanya muncul daftar pelatih // Tetap filter agar hanya muncul daftar pelatih
$data_pelatih = Anggota::whereHas('roles', function($query) { $data_pelatih = Anggota::whereHas('roles', function($query) {
$query->where('nama_role', 'Pelatih'); $query->where('nama_role', 'Pelatih');
})->get(); })
->with('kolatLatihan')
->where('status', 'Aktif')
->orderBy('nama_lengkap', 'asc')
->get();
return view('jadwal.editjadwal', compact('jadwal', 'data_kolat', 'data_pelatih')); return view('jadwal.editjadwal', compact('jadwal', 'data_kolat', 'data_pelatih'));
} }

View File

@ -79,7 +79,9 @@ public function store(Request $request)
$updateData['foto_sk'] = $path; $updateData['foto_sk'] = $path;
} }
$anggota->roles()->syncWithoutDetaching([$rolePelatih->id]); // $anggota->roles()->syncWithoutDetaching([$rolePelatih->id]);
// KODE BARU (Benar karena akan mengganti role Anggota menjadi murni Pelatih)
$anggota->roles()->sync([$rolePelatih->id]);
$anggota->kolatLatihan()->sync($request->kolat_ids); $anggota->kolatLatihan()->sync($request->kolat_ids);
// Eksekusi Update // Eksekusi Update

View File

@ -67,19 +67,19 @@ public function boot(): void
// C. Kandidat Pelatih Baru (Kriteria: Usia >= 18 & Tingkatan Balik 1) // C. Kandidat Pelatih Baru (Kriteria: Usia >= 18 & Tingkatan Balik 1)
// Kita hitung menggunakan Carbon untuk tanggal lahir // Kita hitung menggunakan Carbon untuk tanggal lahir
$kandidatPelatih = Anggota::where('tingkatan', 'Balik 1') // $kandidatPelatih = Anggota::where('tingkatan', 'Balik 1')
->where('status', 'Aktif') // ->where('status', 'Aktif')
->where('tgl_lahir', '<=', Carbon::now()->subYears(18)) // ->where('tgl_lahir', '<=', Carbon::now()->subYears(18))
->count(); // ->count();
if ($kandidatPelatih > 0) { // if ($kandidatPelatih > 0) {
$notifications[] = [ // $notifications[] = [
'icon' => 'bi-star-fill text-info', // 'icon' => 'bi-star-fill text-info',
'title' => 'Kandidat Pelatih', // 'title' => 'Kandidat Pelatih',
'msg' => "Ada $kandidatPelatih anggota Balik 1 berusia 18th siap jadi Pelatih.", // 'msg' => "Ada $kandidatPelatih anggota Balik 1 berusia 18th siap jadi Pelatih.",
'link' => route('anggota.anggota') // Bisa diarahkan ke daftar anggota filter Balik 1 // 'link' => route('anggota.anggota') // Bisa diarahkan ke daftar anggota filter Balik 1
]; // ];
} // }
// D. Peringatan Saldo Kas Kritis (< 500rb) // D. Peringatan Saldo Kas Kritis (< 500rb)
$saldo = Kas::sum('nominal'); $saldo = Kas::sum('nominal');

View File

@ -13,11 +13,29 @@ class KolatSeeder extends Seeder
*/ */
public function run(): void public function run(): void
{ {
// Menghapus data lama agar tidak bentrok saat migrate:fresh --seed
DB::table('kolat')->delete();
DB::table('kolat')->insert([ DB::table('kolat')->insert([
'id' => 1, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi [
'nama_kolat' => 'Kolat Unej', 'id' => 1, // Wajib ID 1 agar nyambung dengan UserSeeder (Siti, Budi, Andi)
'created_at' => now(), 'nama_kolat' => 'Kolat Unej',
'updated_at' => now(), 'created_at' => now(),
'updated_at' => now(),
],
[
'id' => 2,
'nama_kolat' => 'Kolat Matasa',
'created_at' => now(),
'updated_at' => now(),
],
[
'id' => 3,
'nama_kolat' => 'Kolat Ajung',
'created_at' => now(),
'updated_at' => now(),
]
]); ]);
} }
} }

View File

@ -16,42 +16,97 @@ class UserSeeder extends Seeder
*/ */
public function run(): void public function run(): void
{ {
// 1. Ambil ID Role Pelatih
$rolePelatih = Role::where('nama_role', 'Pelatih')->first();
$rolePengurus = Role::where('nama_role', 'Pengurus')->first(); $rolePengurus = Role::where('nama_role', 'Pengurus')->first();
$rolePelatih = Role::where('nama_role', 'Pelatih')->first();
$roleAnggota = Role::where('nama_role', 'Anggota')->first();
// 2. Buat Akun User untuk Pelatih // ==========================================================
$user = User::create([ // AKUN 1: PENGURUS (Akun kamu: Super Admin = Pelatih + Pengurus)
'email' => 'pelatih@tapakmp.com', // ==========================================================
'password' => Hash::make('password123'), // Password default $userPengurus = User::create([
'email' => 'pengurus@tapakmp.com',
'password' => Hash::make('password123'),
]); ]);
// 3. Buat Profil Anggota untuk User tersebut $anggotaPengurus = Anggota::create([
$anggota = Anggota::create([ 'user_id' => $userPengurus->id,
'user_id' => $user->id,
'no_induk' => 'MP-2026-001', 'no_induk' => 'MP-2026-001',
'nama_lengkap' => 'Siti Afiah Dahliawati', 'nama_lengkap' => 'Siti Afiah Dahliawati',
'jenis_kelamin' => 'P', 'jenis_kelamin' => 'P',
'tempat_lahir' => 'Jember', 'tempat_lahir' => 'Jember',
'tgl_lahir' => '2004-10-29', 'tgl_lahir' => '2004-10-29',
'no_hp' => '081455057453', 'no_hp' => '081455057453',
'kolat_id' => 1, // Menyambung ke Kolat Unej di KolatSeeder 'kolat_id' => 1, // Pastikan ada data Kolat dengan ID 1
'tingkatan' => 'Balik 1', 'tingkatan' => 'Balik 1',
'tgl_gabung' => now(), 'tgl_gabung' => now(),
'status' => 'Aktif', 'status' => 'Aktif',
'alamat' => 'Jl. Merpati Putih No. 1, Jember', 'alamat' => 'Jl. Merpati Putih No. 1, Jember',
'jabatan' => 'Pelatih, Pengurus Kolat', 'jabatan' => 'Pengurus (Ketua)',
]); ]);
// 4. INI BAGIAN TERPENTING: Masukkan data ke tabel pivot anggota_role! // Tempelkan HANYA peran Pengurus
// Perintah attach() ini akan otomatis mengisi tabel anggota_role dengan anggota_id dan role_id if ($rolePengurus) {
$anggotaPengurus->roles()->attach($rolePengurus->id);
}
// ==========================================================
// AKUN 2: PELATIH (Hanya bisa akses menu kepelatihan)
// ==========================================================
$userPelatih = User::create([
'email' => 'pelatih@tapakmp.com',
'password' => Hash::make('password123'),
]);
$anggotaPelatih = Anggota::create([
'user_id' => $userPelatih->id,
'no_induk' => 'MP-2026-002',
'nama_lengkap' => 'Budi Santoso',
'jenis_kelamin' => 'L',
'tempat_lahir' => 'Banyuwangi',
'tgl_lahir' => '2000-05-15',
'no_hp' => '081234567890',
'kolat_id' => 1,
'tingkatan' => 'Kombinasi 1',
'tgl_gabung' => now()->subYears(2),
'status' => 'Aktif',
'alamat' => 'Jl. Mastrip, Jember',
'jabatan' => 'Pelatih',
]);
// Tempelkan peran Pelatih
if ($rolePelatih) { if ($rolePelatih) {
$anggota->roles()->attach($rolePelatih->id); $anggotaPelatih->roles()->attach($rolePelatih->id);
} }
// Menempelkan peran sebagai Pengurus (Menjadikannya akun super ganda untuk kebutuhan pengetesan) // Hubungkan pelatih dengan Kolat (1 = Unej)
if ($rolePengurus) { $anggotaPelatih->kolatLatihan()->attach([1]);
$anggota->roles()->attach($rolePengurus->id);
// ==========================================================
// AKUN 3: ANGGOTA (Hanya bisa akses dashboard anggota, presensi, spp)
// ==========================================================
$userAnggota = User::create([
'email' => 'anggota@tapakmp.com',
'password' => Hash::make('password123'),
]);
$anggotaBiasa = Anggota::create([
'user_id' => $userAnggota->id,
'no_induk' => 'MP-2026-003',
'nama_lengkap' => 'Siti Nurul Amalia',
'jenis_kelamin' => 'P',
'tempat_lahir' => 'Jember',
'tgl_lahir' => '2005-08-20',
'no_hp' => '085712345678',
'kolat_id' => 1,
'tingkatan' => 'Dasar 1',
'tgl_gabung' => now()->subMonths(3),
'status' => 'Aktif',
'alamat' => 'Jl. Kalimantan, Jember',
'jabatan' => 'Anggota',
]);
// Tempelkan hanya peran Anggota
if ($roleAnggota) {
$anggotaBiasa->roles()->attach($roleAnggota->id);
} }
} }

View File

@ -46,13 +46,24 @@
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label fw-bold text-dark">Pelatih Bertugas</label> <label class="form-label fw-bold text-dark">Pelatih Bertugas</label>
<select name="pelatih_id" class="form-select @error('pelatih_id') is-invalid @enderror" required> {{-- <select name="pelatih_id" class="form-select @error('pelatih_id') is-invalid @enderror" required>
<option value="" selected disabled>Pilih Pelatih</option> <option value="" selected disabled>Pilih Pelatih</option>
@foreach($data_pelatih as $pelatih) @foreach($data_pelatih as $pelatih)
<option value="{{ $pelatih->id }}" {{ old('pelatih_id') == $pelatih->id ? 'selected' : '' }}> <option value="{{ $pelatih->id }}" {{ old('pelatih_id') == $pelatih->id ? 'selected' : '' }}>
{{ $pelatih->nama_lengkap }} ({{ $pelatih->no_induk }}) {{ $pelatih->nama_lengkap }} ({{ $pelatih->no_induk }})
</option> </option>
@endforeach @endforeach
</select> --}}
<select name="pelatih_id" class="form-select @error('pelatih_id') is-invalid @enderror" required>
<option value="" selected disabled>Pilih Pelatih</option>
@foreach($data_pelatih as $pelatih)
{{-- Menyisipkan kumpulan ID Kolat milik pelatih ke dalam atribut data-kolats --}}
<option value="{{ $pelatih->id }}"
data-kolats="{{ json_encode($pelatih->kolatLatihan->pluck('id')->toArray()) }}"
{{ old('pelatih_id') == $pelatih->id ? 'selected' : '' }}>
{{ $pelatih->nama_lengkap }} ({{ $pelatih->no_induk }})
</option>
@endforeach
</select> </select>
@error('pelatih_id') @error('pelatih_id')
<div class="invalid-feedback">{{ $message }}</div> <div class="invalid-feedback">{{ $message }}</div>
@ -161,4 +172,50 @@
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
</style> </style>
{{-- SCRIPT FILTER PELATIH BERDASARKAN KOLAT --}}
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() {
const pelatihSelect = $('select[name="pelatih_id"]');
// Kloning seluruh elemen opsi pelatih bawaan sebagai master copy
const masterPelatih = pelatihSelect.find('option').clone();
$('select[name="kolat_id"]').change(function() {
const kolatId = $(this).val();
// Bersihkan dropdown pelatih
pelatihSelect.empty();
pelatihSelect.append('<option value="" selected disabled>Pilih Pelatih</option>');
if (kolatId) {
// Iterasi dan filter opsi pelatih dari master copy
masterPelatih.each(function() {
const kolats = $(this).data('kolats'); // Membaca array dari data-kolats
// Jika pelatih bertugas di kolat yang dipilih, lampirkan kembali opsi ke dropdown
if (kolats && kolats.includes(parseInt(kolatId))) {
pelatihSelect.append($(this).clone());
}
});
} else {
// Jika pilihan kolat kosong/direset, tampilkan semua pelatih kembali
masterPelatih.each(function() {
if ($(this).val() !== "") {
pelatihSelect.append($(this).clone());
}
});
}
});
// Trigger otomatis saat halaman dimuat (untuk old input)
const initialKolat = $('select[name="kolat_id"]').val();
if (initialKolat) {
const currentPelatih = "{{ old('pelatih_id') }}";
$('select[name="kolat_id"]').trigger('change');
if (currentPelatih) {
pelatihSelect.val(currentPelatih);
}
}
});
</script>
@endsection @endsection

View File

@ -1,6 +1,11 @@
@extends('layout.app') @extends('layout.app')
@section('content') @section('content')
@php
$user = auth()->user();
$userRoles = $user && $user->anggota ? $user->anggota->roles->pluck('nama_role')->toArray() : [];
$isPengurus = in_array('Pengurus', $userRoles);
@endphp
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<!-- Header & Breadcrumb --> <!-- Header & Breadcrumb -->
<div class="page-header mb-4 text-start"> <div class="page-header mb-4 text-start">
@ -21,17 +26,20 @@
<!-- Title & Action Button --> <!-- Title & Action Button -->
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<div class="text-start"> <div class="text-start">
<h5 class="card-title mb-0 fw-bold text-dark">Daftar Kolat Pada PPS Betako Merpati Putih</h5> <h5 class="card-title mb-0 fw-bold text-dark">Daftar Kolat Pada PPS Betako Merpati Putih
</h5>
<p class="text-muted small mb-0">Cabang Jember - Kelola lokasi dan unit latihan.</p> <p class="text-muted small mb-0">Cabang Jember - Kelola lokasi dan unit latihan.</p>
</div> </div>
<a href="{{ route('kolat.create') }}" class="btn btn-primary px-4 py-2 shadow-sm" @if ($isPengurus)
style="border-radius: 12px;"> <a href="{{ route('kolat.create') }}" class="btn btn-primary px-4 py-2 shadow-sm"
<i class="bi bi-plus-lg me-1"></i> Tambah Kolat style="border-radius: 12px;">
</a> <i class="bi bi-plus-lg me-1"></i> Tambah Kolat
</a>
@endif
</div> </div>
<!-- Notifikasi Alert (Opsional jika ingin pakai SweetAlert saja, ini bisa dihapus) --> <!-- Notifikasi Alert (Opsional jika ingin pakai SweetAlert saja, ini bisa dihapus) -->
@if(session('success')) @if (session('success'))
<div class="alert alert-success border-0 shadow-sm mb-4" style="border-radius: 12px;"> <div class="alert alert-success border-0 shadow-sm mb-4" style="border-radius: 12px;">
<i class="bi bi-check-circle me-2"></i> {{ session('success') }} <i class="bi bi-check-circle me-2"></i> {{ session('success') }}
</div> </div>
@ -44,7 +52,9 @@
<th width="80">ID</th> <th width="80">ID</th>
<th>Nama Kelompok Latihan</th> <th>Nama Kelompok Latihan</th>
<th>Alamat Lokasi</th> <th>Alamat Lokasi</th>
<th width="200">Aksi</th> @if ($isPengurus)
<th width="200">Aksi</th>
@endif
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -57,31 +67,35 @@
</td> </td>
<td class="text-center"> <td class="text-center">
<span class="text-muted small"> <span class="text-muted small">
<i class="bi bi-geo-alt me-1"></i> {{ $item->alamat_kolat ?? 'Alamat belum diatur' }} <i class="bi bi-geo-alt me-1"></i>
{{ $item->alamat_kolat ?? 'Alamat belum diatur' }}
</span> </span>
</td> </td>
<td class="text-center"> @if ($isPengurus)
<div class="d-flex justify-content-center gap-2"> <td class="text-center">
<!-- Edit Button --> <div class="d-flex justify-content-center gap-2">
<a href="{{ route('kolat.edit', $item->id) }}" <a href="{{ route('kolat.edit', $item->id) }}"
class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs"> class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</a> </a>
<!-- Delete Button dengan SweetAlert --> <form action="{{ route('kolat.destroy', $item->id) }}"
<form action="{{ route('kolat.destroy', $item->id) }}" method="POST" id="delete-form-{{ $item->id }}"> method="POST" id="delete-form-{{ $item->id }}">
@csrf @csrf
@method('DELETE') @method('DELETE')
<button type="button" onclick="confirmDelete({{ $item->id }})" class="btn btn-sm btn-action-delete fw-bold px-3 shadow-xs"> <button type="button"
<i class="bi bi-trash"></i> onclick="confirmDelete({{ $item->id }})"
</button> class="btn btn-sm btn-action-delete fw-bold px-3 shadow-xs">
</form> <i class="bi bi-trash"></i>
</div> </button>
</td> </form>
</div>
</td>
@endif
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="4" class="text-center py-5"> <td colspan="{{ $isPengurus ? '4' : '3' }}" class="text-center py-5">
<p class="text-muted">Belum ada data kolat yang tersedia.</p> <p class="text-muted">Belum ada data kolat yang tersedia.</p>
</td> </td>
</tr> </tr>
@ -97,7 +111,11 @@ class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs">
</div> </div>
<style> <style>
.main-table { text-align: center; vertical-align: middle; } .main-table {
text-align: center;
vertical-align: middle;
}
.main-table thead th { .main-table thead th {
background-color: #f8faff; background-color: #f8faff;
padding: 18px 10px; padding: 18px 10px;
@ -107,12 +125,14 @@ class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs">
text-transform: uppercase; text-transform: uppercase;
border-bottom: 2px solid #edf2f9; border-bottom: 2px solid #edf2f9;
} }
.main-table tbody td { .main-table tbody td {
padding: 20px 10px; padding: 20px 10px;
border-bottom: 1px solid #f1f4f8; border-bottom: 1px solid #f1f4f8;
color: #495057; color: #495057;
font-size: 0.9rem; font-size: 0.9rem;
} }
.btn-action-edit { .btn-action-edit {
background-color: #fff8e6; background-color: #fff8e6;
color: #f59e0b; color: #f59e0b;
@ -120,11 +140,13 @@ class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs">
border-radius: 10px; border-radius: 10px;
transition: 0.3s; transition: 0.3s;
} }
.btn-action-edit:hover { .btn-action-edit:hover {
background-color: #f59e0b !important; background-color: #f59e0b !important;
color: white !important; color: white !important;
transform: translateY(-2px); transform: translateY(-2px);
} }
.btn-action-delete { .btn-action-delete {
background-color: #fff5f5; background-color: #fff5f5;
color: #dc3545; color: #dc3545;
@ -132,47 +154,51 @@ class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs">
border-radius: 10px; border-radius: 10px;
transition: 0.3s; transition: 0.3s;
} }
.btn-action-delete:hover { .btn-action-delete:hover {
background-color: #dc3545 !important; background-color: #dc3545 !important;
color: white !important; color: white !important;
transform: translateY(-2px); transform: translateY(-2px);
} }
.shadow-xs { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); }
.shadow-xs {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
</style> </style>
@push('scripts') @push('scripts')
<script> <script>
// Notifikasi Sukses Otomatis // Notifikasi Sukses Otomatis
@if(session('success')) @if (session('success'))
Swal.fire({ Swal.fire({
icon: 'success', icon: 'success',
title: 'Berhasil!', title: 'Berhasil!',
text: "{{ session('success') }}", text: "{{ session('success') }}",
showConfirmButton: false, showConfirmButton: false,
timer: 2000, timer: 2000,
borderRadius: '20px' borderRadius: '20px'
}); });
@endif @endif
// Fungsi Konfirmasi Hapus // Fungsi Konfirmasi Hapus
function confirmDelete(id) { function confirmDelete(id) {
Swal.fire({ Swal.fire({
title: 'Apakah Anda yakin?', title: 'Apakah Anda yakin?',
text: "Data yang dihapus tidak bisa dikembalikan!", text: "Data yang dihapus tidak bisa dikembalikan!",
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonColor: '#0d6efd', confirmButtonColor: '#0d6efd',
cancelButtonColor: '#6c757d', cancelButtonColor: '#6c757d',
confirmButtonText: 'Ya, Hapus!', confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal', cancelButtonText: 'Batal',
reverseButtons: true, reverseButtons: true,
borderRadius: '20px' borderRadius: '20px'
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
document.getElementById('delete-form-' + id).submit(); document.getElementById('delete-form-' + id).submit();
}
})
} }
}) </script>
} @endpush
</script>
@endpush
@endsection @endsection

View File

@ -1,4 +1,4 @@
<footer id="footer" class="footer"> {{-- <footer id="footer" class="footer">
<div class="copyright"> <div class="copyright">
&copy; Copyright <strong><span>TapakMP</span></strong>. All Rights Reserved &copy; Copyright <strong><span>TapakMP</span></strong>. All Rights Reserved
</div> </div>
@ -9,4 +9,4 @@
<!-- Purchase the pro version with working PHP/AJAX contact form: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ --> <!-- Purchase the pro version with working PHP/AJAX contact form: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ -->
Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a> Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a>
</div> </div>
</footer> </footer> --}}

View File

@ -8,12 +8,12 @@
<i class="bi bi-list toggle-sidebar-btn"></i> <i class="bi bi-list toggle-sidebar-btn"></i>
</div><!-- End Logo --> </div><!-- End Logo -->
<div class="search-bar"> {{-- <div class="search-bar">
<form class="search-form d-flex align-items-center" method="POST" action="#"> <form class="search-form d-flex align-items-center" method="POST" action="#">
<input type="text" name="query" placeholder="Search" title="Enter search keyword"> <input type="text" name="query" placeholder="Search" title="Enter search keyword">
<button type="submit" title="Search"><i class="bi bi-search"></i></button> <button type="submit" title="Search"><i class="bi bi-search"></i></button>
</form> </form>
</div><!-- End Search Bar --> </div><!-- End Search Bar --> --}}
<nav class="header-nav ms-auto"> <nav class="header-nav ms-auto">
<ul class="d-flex align-items-center"> <ul class="d-flex align-items-center">

View File

@ -34,6 +34,17 @@
@endif @endif
</li> </li>
@if($userRole === 'Pengurus')
<li class="nav-item">
{{-- Diubah agar mengarah ke rute pengurus dan mendeteksi status aktif dengan benar --}}
<a class="nav-link {{ request()->routeIs('pengurus.*') ? '' : 'collapsed' }}" href="{{ route('pengurus.index') }}">
<i class="bi bi-shield-lock"></i>
<span>Data Pengurus</span>
</a>
</li>
@endif
{{-- ======================================================== --}} {{-- ======================================================== --}}
{{-- MENU MANAJEMEN ANGGOTA (Hanya Pelatih & Pengurus) --}} {{-- MENU MANAJEMEN ANGGOTA (Hanya Pelatih & Pengurus) --}}
{{-- ======================================================== --}} {{-- ======================================================== --}}

View File

@ -152,6 +152,8 @@ class="text-muted text-decoration-none small">Daftar Pelatih</a></li>
</div> </div>
</div> </div>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
<style> <style>
.form-control, .form-select { border-radius: 10px; padding: 10px 15px; } .form-control, .form-select { border-radius: 10px; padding: 10px 15px; }
.form-control:focus { border-color: #4f46e5; box-shadow: 0 0 0 0.25rem rgba(79, 70, 229, 0.1); } .form-control:focus { border-color: #4f46e5; box-shadow: 0 0 0 0.25rem rgba(79, 70, 229, 0.1); }
@ -160,6 +162,34 @@ class="text-muted text-decoration-none small">Daftar Pelatih</a></li>
.btn-cancel:hover { background-color: #6c757d; color: white; transform: translateY(-2px); } .btn-cancel:hover { background-color: #6c757d; color: white; transform: translateY(-2px); }
.btn-save { border-radius: 12px; background: linear-gradient(45deg, #4f46e5, #3730a3); border: none; transition: all 0.3s ease; } .btn-save { border-radius: 12px; background: linear-gradient(45deg, #4f46e5, #3730a3); border: none; transition: all 0.3s ease; }
.btn-save:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(79, 70, 229, 0.4) !important; } .btn-save:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(79, 70, 229, 0.4) !important; }
/* CUSTOM CSS UNTUK SELECT2 AGAR RAPI DAN MELENGKUNG SAMA DENGAN FORM LAIN */
.select2-container--bootstrap-5 .select2-selection {
border-radius: 10px;
min-height: 45px;
padding: 5px 10px;
border: 1px solid #ced4da;
}
.select2-container--bootstrap-5.select2-container--focus .select2-selection {
border-color: #4f46e5;
box-shadow: 0 0 0 0.25rem rgba(79, 70, 229, 0.1);
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice {
background-color: #4f46e5;
color: white;
border: none;
border-radius: 8px;
padding: 2px 10px;
margin-top: 5px;
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove {
color: white;
margin-right: 8px;
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #ff4d4d;
background: none;
}
</style> </style>
@push('scripts') @push('scripts')

View File

@ -82,14 +82,57 @@
</div> </div>
</div> </div>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
<style>
/* CUSTOM CSS UNTUK SELECT2 AGAR MELENGKUNG 12PX (Sama dengan input lain) */
.select2-container--bootstrap-5 .select2-selection {
border-radius: 12px;
min-height: 48px;
padding: 5px 10px;
border: 1px solid #ced4da;
}
/* Warna saat input di-klik (Focus) */
.select2-container--bootstrap-5.select2-container--focus .select2-selection {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.1);
}
/* Desain untuk mode "Multiple" (Banyak Kolat) */
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice {
background-color: #0d6efd; /* Warna biru primary */
color: white;
border: none;
border-radius: 8px;
padding: 2px 10px;
margin-top: 6px;
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove {
color: white;
margin-right: 8px;
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #ff4d4d;
background: none;
}
/* Penyesuaian tinggi teks untuk mode "Single" (Pilih Anggota) */
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered {
padding-top: 4px;
}
</style>
<!-- Tambahkan script Select2 di layout utama atau di sini --> <!-- Tambahkan script Select2 di layout utama atau di sini -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$('.select2').select2({ $('.select2').select2({
theme: 'bootstrap-5', theme: 'bootstrap-5',
placeholder: "Klik untuk memilih Kolat...", placeholder: "Klik untuk mencari dan memilih...",
allowClear: true, allowClear: true,
width: '100%' // Memastikan dropdown memenuhi lebar kolom width: '100%'
}); });
}); });
</script> </script>

View File

@ -30,46 +30,57 @@
@if(session('success')) @if(session('success'))
<div class="alert alert-success alert-dismissible fade show border-0 mb-3 shadow-sm" style="border-radius: 10px;"> <div class="alert alert-success alert-dismissible fade show border-0 mb-3 shadow-sm" style="border-radius: 10px;">
{{ session('success') }} <i class="bi bi-check-circle-fill me-2"></i>{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
@endif @endif
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover align-middle main-table text-center"> <table class="table table-hover align-middle main-table text-center mb-0">
<thead> <thead class="bg-light text-muted">
<tr> <tr>
<th width="60">No</th> <th width="5%" class="py-3 ps-3">No</th>
<th>Identitas Pengurus</th> <th width="25%" class="py-3">Identitas Pengurus</th>
<th>No. HP / WhatsApp</th> <th width="20%" class="py-3">No. HP / WhatsApp</th>
<th>Jabatan & Peran Sistem</th> <th width="25%" class="py-3">Jabatan & Peran Sistem</th>
<th>Status Akun</th> <th width="15%" class="py-3">Status Akun</th>
<th>Aksi</th> <th width="10%" class="py-3 pe-3">Aksi</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@forelse ($data_pengurus as $no => $item) @forelse ($data_pengurus as $no => $item)
<tr> <tr>
<td class="text-muted small text-center">{{ $no + 1 }}</td> <td class="text-muted small ps-3">{{ $no + 1 }}</td>
<td> <td>
<div class="fw-bold text-dark text-center">{{ $item->nama_lengkap }}</div> <div class="fw-bold text-dark" style="font-size: 0.95rem;">{{ $item->nama_lengkap }}</div>
<small class="text-muted d-block text-center">ID: {{ $item->no_induk }}</small> <small class="text-muted d-block">ID: {{ $item->no_induk }}</small>
</td> </td>
<td>{{ $item->no_hp }}</td>
<td> <td>
<div class="fw-bold text-primary mb-1">{{ ucwords($item->jabatan) }}</div> <span class="text-dark">{{ $item->no_hp ?? '-' }}</span>
@foreach($item->roles as $role)
<span class="badge bg-secondary-soft text-secondary px-2 py-1 small" style="font-size: 10px;">{{ $role->nama_role }}</span>
@endforeach
</td> </td>
<td class="text-center"> <td>
<span class="badge rounded-pill bg-success-soft text-success px-3 border"> <div class="d-flex flex-wrap gap-1 justify-content-center">
{{ $item->status }} @foreach($item->roles as $role)
</span> <span class="badge bg-primary-soft text-primary px-2 py-1 fw-semibold" style="font-size: 0.75rem; border-radius: 6px;">
{{ $role->nama_role }}
</span>
@endforeach
</div>
</td> </td>
<td class="text-center"> <td>
@if($item->status == 'Aktif')
<span class="badge bg-success-soft text-success px-3 py-2 rounded-pill fw-bold border border-success-subtle" style="font-size: 0.75rem;">
<i class="bi bi-check-circle-fill me-1"></i> Aktif
</span>
@else
<span class="badge bg-danger-soft text-danger px-3 py-2 rounded-pill fw-bold border border-danger-subtle" style="font-size: 0.75rem;">
<i class="bi bi-x-circle-fill me-1"></i> Non-Aktif
</span>
@endif
</td>
<td class="pe-3">
<div class="d-flex justify-content-center gap-2"> <div class="d-flex justify-content-center gap-2">
<a href="{{ route('pengurus.edit', $item->id) }}" class="btn btn-sm btn-action-edit fw-bold px-2 shadow-xs"> <a href="{{ route('pengurus.edit', $item->id) }}" class="btn btn-sm btn-action-edit fw-bold px-3 shadow-xs" title="Edit Pengurus">
<i class="bi bi-pencil"></i> <i class="bi bi-pencil"></i>
</a> </a>
</div> </div>
@ -77,7 +88,10 @@
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="6" class="text-muted py-4">Belum ada anggota yang diangkat menjadi pengurus struktural.</td> <td colspan="6" class="text-center py-5 text-muted">
<i class="bi bi-folder-x mb-3 d-block" style="font-size: 3rem; opacity: 0.3;"></i>
<h6 class="fw-bold">Belum ada anggota yang diangkat menjadi pengurus struktural.</h6>
</td>
</tr> </tr>
@endforelse @endforelse
</tbody> </tbody>
@ -91,12 +105,48 @@
</div> </div>
<style> <style>
.main-table thead th { background-color: #f8faff; padding: 18px 10px; font-size: 0.75rem; color: #8c98a5; letter-spacing: 1px; text-transform: uppercase; border-bottom: 2px solid #edf2f9; } .main-table thead th {
.main-table tbody td { padding: 20px 10px; border-bottom: 1px solid #f1f4f8; color: #495057; font-size: 0.9rem; } background-color: #f8faff;
.bg-success-soft { background-color: #e6fffa; color: #38b2ac; } font-size: 0.75rem;
.bg-secondary-soft { background-color: #f1f5f9; color: #475569; } color: #8c98a5;
.btn-action-edit { background-color: #fff8e6; color: #f59e0b; border: 1px solid #ffecb3; border-radius: 10px; transition: 0.3s; } letter-spacing: 1px;
.btn-action-edit:hover { background-color: #f59e0b !important; color: white !important; transform: translateY(-2px); } text-transform: uppercase;
border-bottom: 2px solid #edf2f9;
}
.main-table tbody td {
padding: 16px 10px;
border-bottom: 1px solid #f1f4f8;
color: #495057;
font-size: 0.9rem;
}
.main-table tbody tr:hover {
background-color: #f8faff;
transition: 0.2s;
}
/* WARNA BADGE CUSTOM */
.bg-primary-soft { background-color: #e0f2fe; }
.text-primary { color: #0ea5e9 !important; }
.bg-success-soft { background-color: #d1e7dd; }
.text-success { color: #0f5132 !important; }
.bg-danger-soft { background-color: #f8d7da; }
.text-danger { color: #842029 !important; }
/* ANIMASI TOMBOL EDIT */
.btn-action-edit {
background-color: #fff8e6;
color: #f59e0b;
border: 1px solid #ffecb3;
border-radius: 10px;
transition: 0.3s;
}
.btn-action-edit:hover {
background-color: #f59e0b !important;
color: white !important;
transform: translateY(-2px);
}
.shadow-xs { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .shadow-xs { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); }
</style> </style>
@endsection @endsection

View File

@ -2,7 +2,9 @@
@section('content') @section('content')
@php @php
$userRole = auth()->user()->role->nama_role ?? 'Anggota'; $user = auth()->user();
// Ambil semua role yang dimiliki user ke dalam bentuk array
$userRoles = $user && $user->anggota ? $user->anggota->roles->pluck('nama_role')->toArray() : [];
@endphp @endphp
<div class="container-fluid py-4"> <div class="container-fluid py-4">
@ -14,7 +16,7 @@
</div> </div>
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
{{-- HANYA PENGURUS YANG BISA GENERATE MASAL --}} {{-- HANYA PENGURUS YANG BISA GENERATE MASAL --}}
@if ($userRole == 'Pengurus') @if (in_array('Pengurus', $userRoles))
<form action="{{ route('spp.generate') }}" method="POST" class="m-0" id="formGenerateSpp"> <form action="{{ route('spp.generate') }}" method="POST" class="m-0" id="formGenerateSpp">
@csrf @csrf
<button type="button" <button type="button"
@ -229,13 +231,26 @@ class="btn btn-danger px-3 py-2 shadow-sm d-flex align-items-center"
</td> </td>
<td class="text-center"> <td class="text-center">
@if ($spp->status == 'belum_bayar') @if ($spp->status == 'belum_bayar')
<form action="{{ route('spp.bayar', $spp->id) }}" method="POST" {{-- <form action="{{ route('spp.bayar', $spp->id) }}" method="POST"
class="d-inline"> class="d-inline">
@csrf @csrf
<button type="submit" class="btn btn-sm btn-primary px-3 shadow-sm" <button type="submit" class="btn btn-sm btn-primary px-3 shadow-sm"
style="border-radius: 8px;" style="border-radius: 8px;"
onclick="return confirm('Konfirmasi pembayaran tunai untuk {{ $spp->anggota->nama_lengkap }}?')"> onclick="return confirm('Konfirmasi pembayaran tunai untuk {{ $spp->anggota->nama_lengkap }}?')">
@if ($userRole == 'Pelatih') @if (in_array('Pelatih', $userRoles))
<i class="fas fa-hand-holding-usd me-1"></i> Terima Cash
@else
Bayar Tunai
@endif
</button>
</form> --}}
<form action="{{ route('spp.bayar', $spp->id) }}" method="POST" class="d-inline" id="formBayarTunai-{{ $spp->id }}">
@csrf
<button type="button" class="btn btn-sm btn-primary px-3 shadow-sm"
style="border-radius: 8px;"
onclick="konfirmasiBayarTunai({{ $spp->id }}, '{{ addslashes($spp->anggota->nama_lengkap) }}')">
@if (in_array('Pelatih', $userRoles))
<i class="fas fa-hand-holding-usd me-1"></i> Terima Cash <i class="fas fa-hand-holding-usd me-1"></i> Terima Cash
@else @else
Bayar Tunai Bayar Tunai
@ -252,7 +267,19 @@ class="btn btn-sm btn-info text-white px-2 shadow-sm"
</button> </button>
{{-- HANYA PENGURUS YANG BISA VALIDASI STATUS PENDING --}} {{-- HANYA PENGURUS YANG BISA VALIDASI STATUS PENDING --}}
@if ($userRole == 'Pengurus') {{-- @if ($userRole == 'Pengurus')
<form action="{{ route('spp.bayar', $spp->id) }}" method="POST"
class="d-inline">
@csrf
<button type="submit"
class="btn btn-sm btn-success px-2 shadow-sm"
style="border-radius: 8px;"
onclick="return confirm('Validasi bukti transfer dan nyatakan LUNAS?')">
<i class="fas fa-check"></i> Validasi
</button>
</form>
@endif --}}
@if (in_array('Pengurus', $userRoles))
<form action="{{ route('spp.bayar', $spp->id) }}" method="POST" <form action="{{ route('spp.bayar', $spp->id) }}" method="POST"
class="d-inline"> class="d-inline">
@csrf @csrf
@ -418,5 +445,27 @@ function konfirmasiGenerate() {
@endif @endif
}); });
// Fungsi konfirmasi menggunakan SweetAlert2 untuk Bayar Tunai
function konfirmasiBayarTunai(id, namaAnggota) {
Swal.fire({
title: 'Konfirmasi Pembayaran?',
text: `Apakah Anda yakin ingin memproses pembayaran tunai untuk ${namaAnggota}?`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#0d6efd',
cancelButtonColor: '#6c757d',
confirmButtonText: '<i class="fas fa-check me-1"></i> Ya, Proses!',
cancelButtonText: 'Batal',
reverseButtons: true
}).then((result) => {
if (result.isConfirmed) {
// Submit form spesifik berdasarkan ID
document.getElementById('formBayarTunai-' + id).submit();
}
});
}
</script> </script>
@endsection @endsection

View File

@ -192,7 +192,7 @@
Route::post('/pengurus/store', [PengurusController::class, 'store'])->name('pengurus.store'); Route::post('/pengurus/store', [PengurusController::class, 'store'])->name('pengurus.store');
Route::get('/pengurus/{id}/edit', [PengurusController::class, 'edit'])->name('pengurus.edit'); Route::get('/pengurus/{id}/edit', [PengurusController::class, 'edit'])->name('pengurus.edit');
Route::put('/pengurus/{id}/update', [PengurusController::class, 'update'])->name('pengurus.update'); Route::put('/pengurus/{id}/update', [PengurusController::class, 'update'])->name('pengurus.update');
}); });