genarate spp serta update anggota

This commit is contained in:
SitiAfiah 2026-06-05 19:03:03 +07:00
parent 06f672b441
commit 9c68d4b734
10 changed files with 591 additions and 339 deletions

View File

@ -57,7 +57,8 @@ public function create()
} }
public function store(Request $request) public function store(Request $request)
{ {
// 1. Validasi Input
$request->validate([ $request->validate([
'anggota_id' => 'required|exists:anggotas,id', 'anggota_id' => 'required|exists:anggotas,id',
'bulan' => 'required', 'bulan' => 'required',
@ -66,6 +67,20 @@ public function store(Request $request)
'jatuh_tempo' => 'required|date', 'jatuh_tempo' => 'required|date',
]); ]);
// 2. PENJAGA: Cek apakah tagihan untuk anggota, bulan, dan tahun tersebut sudah ada
$cekDuplikat = Spp::where('anggota_id', $request->anggota_id)
->where('bulan', $request->bulan)
->where('tahun', $request->tahun)
->exists();
// Jika sudah ada, langsung tendang balik ke form dengan pesan error
if ($cekDuplikat) {
return redirect()->back()
->withInput() // withInput menjaga agar form tidak kosong lagi
->with('error', "Gagal! Tagihan untuk bulan {$request->bulan} {$request->tahun} sudah terdaftar untuk anggota ini.");
}
// 3. Proses Penyimpanan (Jika lolos penjagaan)
try { try {
$anggota = Anggota::findOrFail($request->anggota_id); $anggota = Anggota::findOrFail($request->anggota_id);
@ -76,15 +91,17 @@ public function store(Request $request)
'tahun' => $request->tahun, 'tahun' => $request->tahun,
'nominal' => $request->nominal, 'nominal' => $request->nominal,
'jatuh_tempo' => $request->jatuh_tempo, 'jatuh_tempo' => $request->jatuh_tempo,
'status' => 'belum_bayar', // Catatan Kecil: Pastikan penulisan 'belum_bayar' ini sama persis
// dengan filter yang dipakai di Dashboard Anggota ya!
'status' => 'pending',
]); ]);
return redirect()->route('spp.index')->with('success', 'Tagihan berhasil dibuat!'); return redirect()->route('spp.index')->with('success', 'Tagihan manual berhasil dibuat!');
} catch (\Exception $e) { } catch (\Exception $e) {
return redirect()->back()->withInput()->with('error', 'Gagal menyimpan: '.$e->getMessage()); return redirect()->back()->withInput()->with('error', 'Gagal menyimpan: '.$e->getMessage());
} }
} }
public function bayar(Request $request, $id) public function bayar(Request $request, $id)
{ {
$spp = Spp::with('anggota')->findOrFail($id); $spp = Spp::with('anggota')->findOrFail($id);
@ -128,16 +145,31 @@ public function bayar(Request $request, $id)
} }
public function generateTagihan(Request $request) public function generateTagihan(Request $request)
{ {
$bulanIni = Carbon::now()->translatedFormat('F'); // PERBAIKAN BHS INDONESIA: Tambahkan ->locale('id') agar outputnya pasti "Juni"
$tahunIni = Carbon::now()->year; $bulanIni = \Carbon\Carbon::now()->locale('id')->translatedFormat('F');
$jatuhTempo = Carbon::now()->day(10); $tahunIni = \Carbon\Carbon::now()->year;
$anggota = Anggota::all(); // Sesuaikan format jatuh tempo ke Y-m-d agar aman saat masuk ke database
$jatuhTempo = \Carbon\Carbon::now()->day(10)->format('Y-m-d');
// --- 1. PENJAGA: Cek apakah bulan dan tahun ini sudah pernah di-generate ---
$cekSudahGenerate = Spp::where('bulan', $bulanIni)
->where('tahun', $tahunIni)
->exists();
if ($cekSudahGenerate) {
// Jika sudah ada, langsung tolak dan kembalikan ke halaman index dengan pesan warning
return redirect()->route('spp.index')->with('warning', "Tagihan SPP bulan {$bulanIni} {$tahunIni} sudah pernah dibuat!");
}
// --------------------------------------------------------------------------
$anggota = Anggota::where('status', 'Aktif')->get(); // Pastikan hanya menagih yang aktif
DB::beginTransaction(); DB::beginTransaction();
try { try {
foreach ($anggota as $agt) { foreach ($anggota as $agt) {
// Pengecekan per-user untuk keamanan ekstra
$exists = Spp::where('anggota_id', $agt->id) $exists = Spp::where('anggota_id', $agt->id)
->where('bulan', $bulanIni) ->where('bulan', $bulanIni)
->where('tahun', $tahunIni) ->where('tahun', $tahunIni)
@ -151,19 +183,18 @@ public function generateTagihan(Request $request)
'tahun' => $tahunIni, 'tahun' => $tahunIni,
'nominal' => 50000, 'nominal' => 50000,
'jatuh_tempo' => $jatuhTempo, 'jatuh_tempo' => $jatuhTempo,
'status' => 'belum_bayar', 'status' => 'belum_bayar', // PERBAIKAN STATUS: Disamakan menjadi 'belum_bayar'
]); ]);
} }
} }
DB::commit(); DB::commit();
return redirect()->route('spp.index')->with('success', 'Tagihan berhasil dibuat!'); return redirect()->route('spp.index')->with('success', "Tagihan bulan {$bulanIni} berhasil dibuat!");
} catch (\Exception $e) { } catch (\Exception $e) {
DB::rollback(); DB::rollback();
return redirect()->back()->with('error', 'Gagal: '.$e->getMessage()); return redirect()->back()->with('error', 'Gagal: '.$e->getMessage());
} }
} }
public function exportExcel(Request $request) public function exportExcel(Request $request)
{ {

View File

@ -30,6 +30,19 @@ public function role()
return $this->belongsTo(Role::class, 'role_id'); return $this->belongsTo(Role::class, 'role_id');
} }
public function kolatLatihan()
{
return $this->belongsToMany(Kolat::class, 'kolat_pelatih', 'anggota_id', 'kolat_id');
}
// Scope agar gampang memanggil data yang hanya ber-role Pelatih
public function scopeIsPelatih($query)
{
return $query->whereHas('role', function ($q) {
$q->where('nama_role', 'Pelatih');
});
}
// Relasi: Mendapatkan riwayat kehadiran anggota // Relasi: Mendapatkan riwayat kehadiran anggota
public function riwayatPresensi() public function riwayatPresensi()
{ {

View File

@ -23,4 +23,11 @@ public function anggota()
{ {
return $this->hasMany(Anggota::class, 'kolat_id'); return $this->hasMany(Anggota::class, 'kolat_id');
} }
public function pelatihs()
{
// Panggil relasi pivot, lalu sambungkan dengan scope isPelatih()
return $this->belongsToMany(Anggota::class, 'kolat_pelatih', 'kolat_id', 'anggota_id')
->isPelatih();
}
} }

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('kolat_pelatih', function (Blueprint $table) {
$table->id();
// Relasi ke anggota (yang bertindak sebagai pelatih) dan kolat
$table->foreignId('anggota_id')->constrained('anggotas')->onDelete('cascade');
$table->foreignId('kolat_id')->constrained('kolat')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('kolat_pelatih');
}
};

View File

@ -13,7 +13,7 @@ class DatabaseSeeder extends Seeder
public function run(): void public function run(): void
{ {
$this->call([ $this->call([
// RoleSeeder::class, RoleSeeder::class,
KolatSeeder::class, KolatSeeder::class,
UserSeeder::class, UserSeeder::class,
]); ]);

View File

@ -14,7 +14,7 @@ class KolatSeeder extends Seeder
public function run(): void public function run(): void
{ {
DB::table('kolat')->insert([ DB::table('kolat')->insert([
'id' => 2, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi 'id' => 1, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi
'nama_kolat' => 'Kolat Unej', 'nama_kolat' => 'Kolat Unej',
'created_at' => now(), 'created_at' => now(),
'updated_at' => now(), 'updated_at' => now(),

View File

@ -100,6 +100,22 @@
</script> </script>
@endif @endif
<script>
document.addEventListener('DOMContentLoaded', function() {
// Cari tombol dengan class toggle-sidebar-btn
const toggleBtn = document.querySelector('.toggle-sidebar-btn');
// Pastikan tombolnya ada
if (toggleBtn) {
// Berikan aksi ketika tombol diklik
toggleBtn.addEventListener('click', function(e) {
// Tambahkan atau hapus class 'toggle-sidebar' pada tag body
document.body.classList.toggle('toggle-sidebar');
});
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</body> </body>

View File

@ -6,7 +6,7 @@
<meta content="width=device-width, initial-scale=1.0" name="viewport"> <meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Daftar Anggota - TapakMP</title> <title>Daftar Anggota - TapakMP</title>
<link href="{{ asset('assets/img/favicon.png') }}" rel="icon"> <link href="{{ asset('assets/img/logo1.jpg') }}" rel="icon">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Ubuntu:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Ubuntu:wght@400;500;700&display=swap" rel="stylesheet">
<link href="{{ asset('assets/vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet"> <link href="{{ asset('assets/vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">

View File

@ -13,11 +13,11 @@
</div> </div>
<!-- Alert Notifikasi --> <!-- Alert Notifikasi -->
@if(session('error')) {{-- @if(session('error'))
<div class="alert alert-danger border-0 shadow-sm mb-4" style="border-radius: 12px;"> <div class="alert alert-danger border-0 shadow-sm mb-4" style="border-radius: 12px;">
{{ session('error') }} {{ session('error') }}
</div> </div>
@endif @endif --}}
<div class="card border-0 shadow-sm" style="border-radius: 20px;"> <div class="card border-0 shadow-sm" style="border-radius: 20px;">
<div class="card-body p-4"> <div class="card-body p-4">
@ -106,4 +106,34 @@
border: 1px solid #0d6efd !important; border: 1px solid #0d6efd !important;
} }
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// POP-UP JIKA DATA DUPLIKAT / ERROR
@if(session('error'))
Swal.fire({
title: 'Data Sudah Ada!',
text: "{{ session('error') }}",
icon: 'warning', // Saya pakai icon warning agar kesannya "peringatan", bukan error sistem rusak
confirmButtonColor: '#f59e0b',
confirmButtonText: 'Oke, Ganti Data'
});
@endif
// POP-UP JIKA BERHASIL SIMPAN MANUAL (Sekalian aja wkwk)
@if(session('success'))
Swal.fire({
title: 'Berhasil!',
text: "{{ session('success') }}",
icon: 'success',
confirmButtonColor: '#10b981',
confirmButtonText: 'Mantap!'
});
@endif
});
</script>
@endsection @endsection

View File

@ -1,11 +1,11 @@
@extends('layout.app') @extends('layout.app')
@section('content') @section('content')
@php @php
$userRole = auth()->user()->role->nama_role ?? 'Anggota'; $userRole = auth()->user()->role->nama_role ?? 'Anggota';
@endphp @endphp
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<!-- Header Section --> <!-- Header Section -->
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3"> <div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
<div class="text-start"> <div class="text-start">
@ -14,31 +14,38 @@
</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 ($userRole == 'Pengurus')
<form action="{{ route('spp.generate') }}" method="POST" class="m-0"> <form action="{{ route('spp.generate') }}" method="POST" class="m-0" id="formGenerateSpp">
@csrf @csrf
<button type="submit" class="btn btn-warning text-white px-3 py-2 shadow-sm d-flex align-items-center h-100" style="border-radius: 10px;" onclick="return confirm('Generate tagihan otomatis untuk bulan ini?')"> <button type="button"
class="btn btn-warning text-white px-3 py-2 shadow-sm d-flex align-items-center h-100"
style="border-radius: 10px;" onclick="konfirmasiGenerate()">
<i class="fas fa-cog me-2"></i> Generate <i class="fas fa-cog me-2"></i> Generate
</button> </button>
</form> </form>
@endif @endif
<a href="{{ route('spp.export.excel', request()->all()) }}" class="btn btn-success px-3 py-2 shadow-sm d-flex align-items-center" style="border-radius: 10px; text-decoration: none;"> <a href="{{ route('spp.export.excel', request()->all()) }}"
class="btn btn-success px-3 py-2 shadow-sm d-flex align-items-center"
style="border-radius: 10px; text-decoration: none;">
<i class="fas fa-file-excel me-2"></i> Excel <i class="fas fa-file-excel me-2"></i> Excel
</a> </a>
<a href="{{ route('spp.export.pdf', request()->all()) }}" class="btn btn-danger px-3 py-2 shadow-sm d-flex align-items-center" style="border-radius: 10px; text-decoration: none;"> <a href="{{ route('spp.export.pdf', request()->all()) }}"
class="btn btn-danger px-3 py-2 shadow-sm d-flex align-items-center"
style="border-radius: 10px; text-decoration: none;">
<i class="fas fa-file-pdf me-2"></i> PDF <i class="fas fa-file-pdf me-2"></i> PDF
</a> </a>
<a href="{{ route('spp.create') }}" class="btn btn-primary px-3 py-2 shadow-sm d-flex align-items-center" style="border-radius: 10px; text-decoration: none;"> <a href="{{ route('spp.create') }}" class="btn btn-primary px-3 py-2 shadow-sm d-flex align-items-center"
style="border-radius: 10px; text-decoration: none;">
<i class="fas fa-plus me-2"></i> Tambah Manual <i class="fas fa-plus me-2"></i> Tambah Manual
</a> </a>
</div> </div>
</div> </div>
<!-- Alert Notifikasi --> <!-- Alert Notifikasi -->
@if(session('success')) @if (session('success'))
<div class="alert alert-success border-0 shadow-sm mb-4" style="border-radius: 10px;"> <div class="alert alert-success border-0 shadow-sm mb-4" style="border-radius: 10px;">
<i class="fas fa-check-circle me-1"></i> {{ session('success') }} <i class="fas fa-check-circle me-1"></i> {{ session('success') }}
</div> </div>
@ -47,7 +54,8 @@
<!-- Statistik Cards --> <!-- Statistik Cards -->
<div class="row g-3 mb-4"> <div class="row g-3 mb-4">
<div class="col-md-3"> <div class="col-md-3">
<div class="card border-0 shadow-sm border-start border-success border-4 h-100" style="border-radius: 15px;"> <div class="card border-0 shadow-sm border-start border-success border-4 h-100"
style="border-radius: 15px;">
<div class="card-body p-4 d-flex justify-content-between align-items-center"> <div class="card-body p-4 d-flex justify-content-between align-items-center">
<div> <div>
<p class="text-success small fw-bold mb-1 uppercase">SPP LUNAS</p> <p class="text-success small fw-bold mb-1 uppercase">SPP LUNAS</p>
@ -58,7 +66,8 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="card border-0 shadow-sm border-start border-warning border-4 h-100" style="border-radius: 15px;"> <div class="card border-0 shadow-sm border-start border-warning border-4 h-100"
style="border-radius: 15px;">
<div class="card-body p-4 d-flex justify-content-between align-items-center"> <div class="card-body p-4 d-flex justify-content-between align-items-center">
<div> <div>
<p class="text-warning small fw-bold mb-1 uppercase">PENDING</p> <p class="text-warning small fw-bold mb-1 uppercase">PENDING</p>
@ -80,7 +89,8 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="card border-0 shadow-sm border-start border-primary border-4 h-100" style="border-radius: 15px;"> <div class="card border-0 shadow-sm border-start border-primary border-4 h-100"
style="border-radius: 15px;">
<div class="card-body p-4 d-flex justify-content-between align-items-center"> <div class="card-body p-4 d-flex justify-content-between align-items-center">
<div> <div>
<p class="text-primary small fw-bold mb-1 uppercase">TOTAL MASUK</p> <p class="text-primary small fw-bold mb-1 uppercase">TOTAL MASUK</p>
@ -110,8 +120,9 @@
<label class="form-label small fw-bold">Bulan</label> <label class="form-label small fw-bold">Bulan</label>
<select name="bulan" class="form-select border-0 bg-light"> <select name="bulan" class="form-select border-0 bg-light">
<option value="">Semua Bulan</option> <option value="">Semua Bulan</option>
@foreach(['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'] as $m) @foreach (['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'] as $m)
<option value="{{ $m }}" {{ request('bulan') == $m ? 'selected' : '' }}>{{ $m }}</option> <option value="{{ $m }}" {{ request('bulan') == $m ? 'selected' : '' }}>
{{ $m }}</option>
@endforeach @endforeach
</select> </select>
</div> </div>
@ -121,18 +132,22 @@
<option value="">Semua Status</option> <option value="">Semua Status</option>
<option value="lunas" {{ request('status') == 'lunas' ? 'selected' : '' }}>Lunas</option> <option value="lunas" {{ request('status') == 'lunas' ? 'selected' : '' }}>Lunas</option>
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option> <option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option>
<option value="belum_bayar" {{ request('status') == 'belum_bayar' ? 'selected' : '' }}>Belum Bayar</option> <option value="belum_bayar" {{ request('status') == 'belum_bayar' ? 'selected' : '' }}>Belum
Bayar</option>
</select> </select>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label class="form-label small fw-bold">Cari Anggota</label> <label class="form-label small fw-bold">Cari Anggota</label>
<input type="text" name="cari" class="form-control border-0 bg-light" placeholder="Nama anggota..." value="{{ request('cari') }}"> <input type="text" name="cari" class="form-control border-0 bg-light"
placeholder="Nama anggota..." value="{{ request('cari') }}">
</div> </div>
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<button type="submit" class="btn btn-primary px-4 shadow-sm" style="background-color: #0d6efd; border-radius: 10px;"> <button type="submit" class="btn btn-primary px-4 shadow-sm"
style="background-color: #0d6efd; border-radius: 10px;">
<i class="fas fa-search me-1"></i> Filter <i class="fas fa-search me-1"></i> Filter
</button> </button>
<a href="{{ route('spp.index') }}" class="btn btn-light px-4 shadow-sm" style="border-radius: 10px;"> <a href="{{ route('spp.index') }}" class="btn btn-light px-4 shadow-sm"
style="border-radius: 10px;">
<i class="fas fa-sync-alt me-1"></i> Reset <i class="fas fa-sync-alt me-1"></i> Reset
</a> </a>
</div> </div>
@ -142,16 +157,18 @@
<!-- Daftar Tagihan Section --> <!-- Daftar Tagihan Section -->
<div class="card border-0 shadow-sm" style="border-radius: 20px; overflow: hidden;"> <div class="card border-0 shadow-sm" style="border-radius: 20px; overflow: hidden;">
<div class="card-header border-0 py-3 text-white d-flex justify-content-between align-items-center" style="background-color: #0d6efd;"> <div class="card-header border-0 py-3 text-white d-flex justify-content-between align-items-center"
style="background-color: #0d6efd;">
<h6 class="mb-0 fw-bold"><i class="fas fa-money-check me-2"></i>Daftar Tagihan SPP</h6> <h6 class="mb-0 fw-bold"><i class="fas fa-money-check me-2"></i>Daftar Tagihan SPP</h6>
</div> </div>
<div class="card-body p-0"> <div class="card-body p-0">
@if($data_spp->isEmpty()) @if ($data_spp->isEmpty())
<div class="py-5 text-center"> <div class="py-5 text-center">
<i class="fas fa-file-invoice-dollar text-muted mb-4" style="font-size: 80px; opacity: 0.2;"></i> <i class="fas fa-file-invoice-dollar text-muted mb-4" style="font-size: 80px; opacity: 0.2;"></i>
<h5 class="fw-bold text-muted">Belum ada data tagihan SPP</h5> <h5 class="fw-bold text-muted">Belum ada data tagihan SPP</h5>
<p class="text-muted small">Silakan tambah manual atau generate otomatis.</p> <p class="text-muted small">Silakan tambah manual atau generate otomatis.</p>
<a href="{{ route('spp.create') }}" class="btn btn-danger text-white px-4 py-2 mt-2" style="border-radius: 10px;">Tambah Manual</a> <a href="{{ route('spp.create') }}" class="btn btn-danger text-white px-4 py-2 mt-2"
style="border-radius: 10px;">Tambah Manual</a>
</div> </div>
@else @else
<div class="table-responsive"> <div class="table-responsive">
@ -166,7 +183,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach($data_spp as $spp) @foreach ($data_spp as $spp)
<tr> <tr>
<td class="ps-4"> <td class="ps-4">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -175,8 +192,10 @@
{{ strtoupper(substr($spp->anggota->nama_lengkap, 0, 1)) }} {{ strtoupper(substr($spp->anggota->nama_lengkap, 0, 1)) }}
</div> </div>
<div> <div>
<div class="fw-bold text-dark" style="font-size: 0.95rem;">{{ $spp->anggota->nama_lengkap }}</div> <div class="fw-bold text-dark" style="font-size: 0.95rem;">
<span class="badge bg-light text-muted border fw-normal" style="font-size: 0.75rem;"> {{ $spp->anggota->nama_lengkap }}</div>
<span class="badge bg-light text-muted border fw-normal"
style="font-size: 0.75rem;">
ID: {{ $spp->anggota_id }} ID: {{ $spp->anggota_id }}
</span> </span>
</div> </div>
@ -190,7 +209,7 @@
Rp {{ number_format($spp->nominal, 0, ',', '.') }} Rp {{ number_format($spp->nominal, 0, ',', '.') }}
</td> </td>
<td> <td>
@if($spp->status == 'lunas') @if ($spp->status == 'lunas')
<span class="badge bg-success-soft text-success rounded-pill px-3"> <span class="badge bg-success-soft text-success rounded-pill px-3">
<i class="fas fa-check-circle me-1"></i> Lunas <i class="fas fa-check-circle me-1"></i> Lunas
</span> </span>
@ -209,33 +228,36 @@
@endif @endif
</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" class="d-inline"> <form action="{{ route('spp.bayar', $spp->id) }}" method="POST"
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 ($userRole == 'Pelatih')
<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
@endif @endif
</button> </button>
</form> </form>
@elseif($spp->status == 'pending') @elseif($spp->status == 'pending')
<div class="d-flex justify-content-center gap-1"> <div class="d-flex justify-content-center gap-1">
<button type="button" class="btn btn-sm btn-info text-white px-2 shadow-sm" <button type="button"
style="border-radius: 8px;" class="btn btn-sm btn-info text-white px-2 shadow-sm"
data-bs-toggle="modal" data-bs-target="#modalBukti{{ $spp->id }}"> style="border-radius: 8px;" data-bs-toggle="modal"
data-bs-target="#modalBukti{{ $spp->id }}">
<i class="fas fa-eye"></i> Bukti <i class="fas fa-eye"></i> Bukti
</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"> <form action="{{ route('spp.bayar', $spp->id) }}" method="POST"
class="d-inline">
@csrf @csrf
<button type="submit" class="btn btn-sm btn-success px-2 shadow-sm" <button type="submit"
class="btn btn-sm btn-success px-2 shadow-sm"
style="border-radius: 8px;" style="border-radius: 8px;"
onclick="return confirm('Validasi bukti transfer dan nyatakan LUNAS?')"> onclick="return confirm('Validasi bukti transfer dan nyatakan LUNAS?')">
<i class="fas fa-check"></i> Validasi <i class="fas fa-check"></i> Validasi
@ -245,23 +267,33 @@
</div> </div>
<!-- MODAL BUKTI TRANSFER --> <!-- MODAL BUKTI TRANSFER -->
<div class="modal fade text-start" id="modalBukti{{ $spp->id }}" tabindex="-1" aria-hidden="true"> <div class="modal fade text-start" id="modalBukti{{ $spp->id }}"
tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius: 15px;"> <div class="modal-content" style="border-radius: 15px;">
<div class="modal-header border-0 pb-0"> <div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold">Bukti Transfer</h5> <h5 class="modal-title fw-bold">Bukti Transfer</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body text-center pt-3 pb-4"> <div class="modal-body text-center pt-3 pb-4">
<p class="text-muted small mb-3">Atas Nama: <strong>{{ $spp->anggota->nama_lengkap }}</strong></p> <p class="text-muted small mb-3">Atas Nama:
@if($spp->bukti_pembayaran) <strong>{{ $spp->anggota->nama_lengkap }}</strong></p>
<img src="{{ asset('uploads/bukti_spp/' . $spp->bukti_pembayaran) }}" alt="Bukti Transfer" class="img-fluid rounded shadow-sm" style="max-height: 400px; object-fit: contain;"> @if ($spp->bukti_pembayaran)
@if($spp->tanggal_bayar) <img src="{{ asset('uploads/bukti_spp/' . $spp->bukti_pembayaran) }}"
<p class="text-muted small mt-3 mb-0">Diunggah pada: {{ \Carbon\Carbon::parse($spp->tanggal_bayar)->translatedFormat('d F Y H:i') }}</p> alt="Bukti Transfer"
class="img-fluid rounded shadow-sm"
style="max-height: 400px; object-fit: contain;">
@if ($spp->tanggal_bayar)
<p class="text-muted small mt-3 mb-0">Diunggah
pada:
{{ \Carbon\Carbon::parse($spp->tanggal_bayar)->translatedFormat('d F Y H:i') }}
</p>
@endif @endif
@else @else
<div class="text-danger my-4"> <div class="text-danger my-4">
<i class="fas fa-exclamation-triangle fs-1 mb-2"></i> <i
class="fas fa-exclamation-triangle fs-1 mb-2"></i>
<h6>Bukti gambar tidak ditemukan.</h6> <h6>Bukti gambar tidak ditemukan.</h6>
</div> </div>
@endif @endif
@ -269,7 +301,6 @@
</div> </div>
</div> </div>
</div> </div>
@else @else
<span class="text-success small fw-bold"> <span class="text-success small fw-bold">
<i class="fas fa-check-double me-1"></i> Terverifikasi <i class="fas fa-check-double me-1"></i> Terverifikasi
@ -284,16 +315,108 @@
@endif @endif
</div> </div>
</div> </div>
</div> </div>
<style> <style>
.bg-light { background-color: #f8f9fa !important; } .bg-light {
.uppercase { text-transform: uppercase; letter-spacing: 0.8px; font-size: 10px; font-weight: 700; } background-color: #f8f9fa !important;
.fs-1 { font-size: 2.2rem !important; opacity: 0.2; } }
.bg-success-soft { background-color: rgba(25, 135, 84, 0.12); }
.bg-danger-soft { background-color: rgba(220, 53, 69, 0.12); } .uppercase {
.bg-warning-soft { background-color: rgba(255, 193, 7, 0.15); } text-transform: uppercase;
.table-hover tbody tr:hover { background-color: rgba(13, 110, 253, 0.03); transition: 0.2s; } letter-spacing: 0.8px;
.form-select, .form-control { border-radius: 10px; padding: 0.6rem 1rem; } font-size: 10px;
</style> font-weight: 700;
}
.fs-1 {
font-size: 2.2rem !important;
opacity: 0.2;
}
.bg-success-soft {
background-color: rgba(25, 135, 84, 0.12);
}
.bg-danger-soft {
background-color: rgba(220, 53, 69, 0.12);
}
.bg-warning-soft {
background-color: rgba(255, 193, 7, 0.15);
}
.table-hover tbody tr:hover {
background-color: rgba(13, 110, 253, 0.03);
transition: 0.2s;
}
.form-select,
.form-control {
border-radius: 10px;
padding: 0.6rem 1rem;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// 2. Fungsi ini yang dipanggil saat tombol Generate diklik
function konfirmasiGenerate() {
Swal.fire({
title: 'Generate Tagihan?',
text: "Sistem akan membuat tagihan SPP untuk semua anggota aktif bulan ini.",
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#f59e0b',
cancelButtonColor: '#6c757d',
confirmButtonText: '<i class="fas fa-cog"></i> Ya, Generate!',
cancelButtonText: 'Batal',
reverseButtons: true
}).then((result) => {
if (result.isConfirmed) {
// Jika user klik "Ya", submit form secara otomatis
document.getElementById('formGenerateSpp').submit();
}
});
}
// 3. Fungsi ini otomatis berjalan saat halaman selesai dimuat
document.addEventListener("DOMContentLoaded", function() {
// Menangkap Notifikasi Warning (Jika sudah pernah generate)
@if(session('warning'))
Swal.fire({
title: 'Tidak Bisa Generate!',
text: "{{ session('warning') }}",
icon: 'warning',
confirmButtonColor: '#f59e0b',
confirmButtonText: 'Oke, Mengerti'
});
@endif
// Menangkap Notifikasi Sukses (Jika generate berhasil)
@if(session('success'))
Swal.fire({
title: 'Berhasil!',
text: "{{ session('success') }}",
icon: 'success',
confirmButtonColor: '#10b981',
confirmButtonText: 'Sip!'
});
@endif
// Menangkap Notifikasi Error (Jika ada kegagalan sistem database)
@if(session('error'))
Swal.fire({
title: 'Gagal!',
text: "{{ session('error') }}",
icon: 'error',
confirmButtonColor: '#ef4444',
confirmButtonText: 'Tutup'
});
@endif
});
</script>
@endsection @endsection