From 2ccaa0b4c5aaf3ebc53325c0ac0b3dc89262e18b Mon Sep 17 00:00:00 2001 From: SitiAfiah Date: Wed, 6 May 2026 20:55:12 +0700 Subject: [PATCH] rekap dan dashboard --- .../Http/Controllers/DashboardController.php | 73 ++++- .../Http/Controllers/ProfileController.php | 89 ++++++ .../app/Http/Controllers/RekapController.php | 60 ++++ .../app/Http/Controllers/SppController.php | 115 +++++++- manajemen_mp/app/Models/Anggota.php | 7 + manajemen_mp/app/Models/Spp.php | 3 +- ...4232_add_bukti_pembayaran_to_spp_table.php | 28 ++ .../uploads/bukti_spp/1777835228_profile.jpeg | Bin 0 -> 21700 bytes .../views/anggota/anggotacreate.blade.php | 27 +- .../views/anggota/editanggota.blade.php | 77 +++++- .../views/dashboard/dashboard.blade.php | 209 +++++++++++++- .../resources/views/layout/sidebar.blade.php | 150 +++++++--- .../views/login-register/login.blade.php | 18 +- .../resources/views/pelatih/pelatih.blade.php | 18 +- .../resources/views/profil/edit.blade.php | 238 ++++++++++++++++ .../resources/views/rekap/index.blade.php | 132 +++++++++ .../views/spp/anggota/bayar.blade.php | 51 ++++ .../views/spp/anggota/index.blade.php | 85 ++++++ .../views/spp/pengurus/create.blade.php | 2 +- .../views/spp/pengurus/index.blade.php | 121 +++++--- manajemen_mp/routes/web.php | 259 ++++++++---------- 21 files changed, 1507 insertions(+), 255 deletions(-) create mode 100644 manajemen_mp/app/Http/Controllers/ProfileController.php create mode 100644 manajemen_mp/app/Http/Controllers/RekapController.php create mode 100644 manajemen_mp/database/migrations/2026_05_04_014232_add_bukti_pembayaran_to_spp_table.php create mode 100644 manajemen_mp/public/uploads/bukti_spp/1777835228_profile.jpeg create mode 100644 manajemen_mp/resources/views/profil/edit.blade.php create mode 100644 manajemen_mp/resources/views/rekap/index.blade.php create mode 100644 manajemen_mp/resources/views/spp/anggota/bayar.blade.php create mode 100644 manajemen_mp/resources/views/spp/anggota/index.blade.php diff --git a/manajemen_mp/app/Http/Controllers/DashboardController.php b/manajemen_mp/app/Http/Controllers/DashboardController.php index 86ca402..38ff2cc 100644 --- a/manajemen_mp/app/Http/Controllers/DashboardController.php +++ b/manajemen_mp/app/Http/Controllers/DashboardController.php @@ -1,13 +1,78 @@ count(); + $total_kolat = Kolat::count(); + + // Ambil ID Role Pelatih secara dinamis + $rolePelatih = Role::where('nama_role', 'Pelatih')->first(); + $total_pelatih = $rolePelatih ? Anggota::where('role_id', $rolePelatih->id)->count() : 0; + + // Hitung SPP yang statusnya masih 'pending' atau 'belum_lunas' + $spp_pending = Spp::whereIn('status', ['pending', 'belum_lunas'])->count(); + + // --- 2. DATA GRAFIK LINE (TREN PRESENSI 7 HARI) --- + $grafikPresensi = Presensi::select( + DB::raw('DATE(created_at) as date'), + DB::raw('count(*) as total') + ) + ->where('status', 'hadir') + ->where('created_at', '>=', now()->subDays(7)) + ->groupBy('date') + ->orderBy('date', 'ASC') + ->get(); + + // --- 3. DATA GRAFIK DOUGHNUT (KOMPOSISI SABUK) --- + $persebaran_sabuk = Anggota::select('tingkatan', DB::raw('count(*) as total')) + ->groupBy('tingkatan') + ->get(); + + // --- 4. DATA AKTIVITAS TERBARU (REAL DARI SPP) --- + $aktivitas = Spp::with('anggota') + ->latest() + ->take(5) + ->get() + ->map(function ($item) { + return [ + 'waktu' => $item->created_at->format('d/m/Y H:i'), + 'kegiatan' => 'Pembayaran SPP: ' . ($item->keterangan ?? 'Iuran Bulanan'), + 'user' => $item->anggota->nama_lengkap ?? 'System', + 'status' => strtoupper($item->status), + 'badge' => $item->status == 'lunas' ? 'success' : ($item->status == 'pending' ? 'warning' : 'danger') + ]; + }); + + // --- 5. LOGIKA KELAYAKAN UNTUK RINGKASAN --- + $anggota = Anggota::with('presensi')->get(); + $jumlah_layak_ujian = $anggota->filter(function($item) { + $total = $item->presensi->count(); + $hadir = $item->presensi->where('status', 'hadir')->count(); + $persen = $total > 0 ? ($hadir / $total) * 100 : 0; + return $persen >= 75; + })->count(); + + return view('dashboard.dashboard', compact( + 'total_anggota_aktif', + 'total_pelatih', + 'total_kolat', + 'spp_pending', + 'grafikPresensi', + 'persebaran_sabuk', + 'aktivitas', + 'jumlah_layak_ujian' + )); } } diff --git a/manajemen_mp/app/Http/Controllers/ProfileController.php b/manajemen_mp/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..3610fa4 --- /dev/null +++ b/manajemen_mp/app/Http/Controllers/ProfileController.php @@ -0,0 +1,89 @@ +anggota; + + if (!$anggota) { + return redirect()->back()->with('error', 'Data profil tidak ditemukan.'); + } + + return view('profil.edit', compact('anggota')); + } + + /** + * Menyimpan perubahan data yang diperbaiki anggota. + */ + public function update(Request $request) + { + $anggota = Auth::user()->anggota; + + // Validasi data yang boleh diubah oleh anggota[cite: 1, 2] + $request->validate([ + 'nama_lengkap' => 'required|string|max:255', + 'no_hp' => 'required|numeric', + 'tempat_lahir' => 'required|string', + 'tgl_lahir' => 'required|date', + 'jenis_kelamin'=> 'required|in:L,P', + 'alamat' => 'required|string', + ]); + + // Mengambil data input + $input = $request->only([ + 'nama_lengkap', 'no_hp', 'tempat_lahir', 'tgl_lahir', 'jenis_kelamin', 'alamat' + ]); + + // Proses update data ke database + $anggota->update($input); + + // Update nama di tabel users agar sinkron + Auth::user()->update([ + 'name' => $request->nama_lengkap + ]); + + return redirect()->route('profil.edit')->with('success', 'Profil Anda berhasil diperbarui!'); + } + + /** + * Menyimpan perubahan password (FUNGSI BARU). + */ + public function updatePassword(Request $request) + { + // 1. Validasi input + // 'confirmed' akan otomatis mencocokkan input 'password' dengan 'password_confirmation' + $request->validate([ + 'current_password' => 'required', + 'password' => 'required|min:8|confirmed', + ], [ + 'password.confirmed' => 'Konfirmasi password baru tidak cocok!', + 'password.min' => 'Password minimal harus 8 karakter!' + ]); + + $user = Auth::user(); + + // 2. Cek apakah password lama yang dimasukkan benar + if (!Hash::check($request->current_password, $user->password)) { + // Jika salah, kembalikan ke halaman profil dengan pesan error + return back()->withErrors(['current_password' => 'Password saat ini salah.']); + } + + // 3. Jika benar, update dengan password baru yang di-hash (enkripsi) + $user->update([ + 'password' => Hash::make($request->password) + ]); + + return redirect()->route('profile.edit')->with('success', 'Password berhasil diperbarui!'); + } +} diff --git a/manajemen_mp/app/Http/Controllers/RekapController.php b/manajemen_mp/app/Http/Controllers/RekapController.php new file mode 100644 index 0000000..d76ca76 --- /dev/null +++ b/manajemen_mp/app/Http/Controllers/RekapController.php @@ -0,0 +1,60 @@ +kolat_id) { + $query->where('kolat_id', $request->kolat_id); + } + + if ($request->status) { + $query->where('status', $request->status); + } + + $anggota = $query->get(); + + // 3. Transformasi data untuk Rekap + $dataRekap = $anggota->map(function ($item) { + // Logika Presensi + $totalPertemuan = $item->presensi->count(); + $hadir = $item->presensi->where('status', 'hadir')->count(); + $persentasePresensi = $totalPertemuan > 0 ? ($hadir / $totalPertemuan) * 100 : 0; + + // Logika SPP + $punyaTunggakan = $item->spp->where('status', 'belum_lunas')->count() > 0; + + // Logika Keaktifan + $statusAktif = ($totalPertemuan > 0 && $persentasePresensi > 0) ? 'Aktif' : 'Non-Aktif'; + + // Syarat Ujian (Presensi >= 75% dan SPP Lunas) + $layakUjian = ($persentasePresensi >= 75 && !$punyaTunggakan); + + return [ + 'nama' => $item->nama_lengkap, // Sesuaikan dengan kolom tabel Anda + 'no_induk' => $item->no_induk, + 'tingkatan' => $item->tingkatan, + 'nama_kolat' => $item->kolat->nama_kolat ?? '-', + 'status_aktif' => $statusAktif, + 'persentase_presensi' => round($persentasePresensi, 1), + 'status_spp' => $punyaTunggakan ? 'Ada Tunggakan' : 'Lunas', + 'layak' => $layakUjian + ]; + }); + + // 4. Kirim variabel yang dibutuhkan view + return view('rekap.index', compact('dataRekap', 'data_kolat')); + } +} diff --git a/manajemen_mp/app/Http/Controllers/SppController.php b/manajemen_mp/app/Http/Controllers/SppController.php index 45317a0..b5226f3 100644 --- a/manajemen_mp/app/Http/Controllers/SppController.php +++ b/manajemen_mp/app/Http/Controllers/SppController.php @@ -87,7 +87,6 @@ public function store(Request $request) public function bayar(Request $request, $id) { - // Perbaikan: Pakai 'anggota' (singular) sesuai relasi model biasanya $spp = Spp::with('anggota')->findOrFail($id); if ($spp->status == 'lunas') { @@ -99,28 +98,32 @@ public function bayar(Request $request, $id) $saldo_terakhir = Kas::saldoTerakhir() ?? 0; $saldo_baru = $saldo_terakhir + $spp->nominal; + // Buat Catatan Kas $kas = Kas::create([ 'tanggal' => now(), 'jenis' => 'masuk', 'kategori' => 'SPP', 'nominal' => $spp->nominal, - 'keterangan' => 'Bayar SPP '.$spp->anggota->nama.' - '.$spp->bulan.' '.$spp->tahun, + 'keterangan' => 'Bayar SPP ' . $spp->anggota->nama_lengkap . ' - ' . $spp->bulan . ' ' . $spp->tahun, 'saldo_akhir' => $saldo_baru, ]); + // Update Status SPP $spp->update([ 'status' => 'lunas', - 'tanggal_bayar' => now(), + // Jangan timpa tanggal_bayar jika sudah diisi saat upload bukti TF + 'tanggal_bayar' => $spp->tanggal_bayar ?? now(), 'kas_id' => $kas->id, ]); DB::commit(); - return redirect()->back()->with('success', 'Pembayaran SPP berhasil dicatat!'); + $pesan = $spp->status == 'pending' ? 'Pembayaran via transfer berhasil divalidasi!' : 'Pembayaran SPP tunai berhasil dicatat!'; + return redirect()->back()->with('success', $pesan); + } catch (\Exception $e) { DB::rollback(); - - return redirect()->back()->with('error', 'Terjadi kesalahan: '.$e->getMessage()); + return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage()); } } @@ -213,4 +216,104 @@ public function exportPdf(Request $request) return $pdf->download('Laporan_SPP_TapakMP_'.date('Ymd').'.pdf'); } + + // public function indexAnggota() + // { + // // Sesuaikan cara pemanggilan ID anggota dengan struktur tabel User kamu + // // Contoh ini mengasumsikan di tabel users ada kolom 'anggota_id' + // $anggota_id = auth()->user()->anggota_id; + + // $data_spp = Spp::where('anggota_id', $anggota_id) + // ->orderBy('tahun', 'desc') + // ->orderBy('bulan', 'desc') + // ->get(); + + // return view('spp.anggota.index', compact('data_spp')); + // } + + public function indexAnggota() + { + // 1. Panggil relasi anggota dari user yang sedang login + $profil_anggota = auth()->user()->anggota; + + // 2. Proteksi jika user belum punya profil anggota (mencegah error) + if (!$profil_anggota) { + $data_spp = collect(); // Kirim data kosong agar tidak error di view + return view('spp.anggota.index', compact('data_spp')) + ->with('error', 'Profil anggota Anda belum terhubung. Silakan hubungi pengurus.'); + } + + // 3. Jika aman, ambil ID anggotanya + $anggota_id = $profil_anggota->id; + + // 4. Cari tagihan SPP + $data_spp = Spp::where('anggota_id', $anggota_id) + ->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->get(); + + return view('spp.anggota.index', compact('data_spp')); + } + // 2. Menampilkan form upload bukti transfer + // public function formBayarAnggota($id) + // { + // $spp = Spp::findOrFail($id); + + // // Keamanan: Pastikan yang diakses adalah tagihannya sendiri + // if ($spp->anggota_id != auth()->user()->anggota_id) { + // abort(403, 'Akses ditolak.'); + // } + + // return view('spp.anggota.bayar', compact('spp')); + // } + public function formBayarAnggota($id) + { + $spp = Spp::findOrFail($id); + $profil_anggota = auth()->user()->anggota; + + // Proteksi 1: Pastikan user punya profil anggota + if (!$profil_anggota) { + abort(403, 'Profil anggota belum terhubung.'); + } + + // Proteksi 2: Pastikan yang diakses adalah tagihannya sendiri + if ($spp->anggota_id != $profil_anggota->id) { + abort(403, 'Akses ditolak. Ini bukan tagihan Anda.'); + } + + return view('spp.anggota.bayar', compact('spp')); + } + + // 3. Memproses upload gambar bukti TF + public function prosesBayarAnggota(Request $request, $id) + { + $request->validate([ + 'bukti_pembayaran' => 'required|image|mimes:jpeg,png,jpg|max:2048', // Maksimal 2MB + ], [ + 'bukti_pembayaran.required' => 'Bukti transfer wajib diunggah.', + 'bukti_pembayaran.image' => 'File harus berupa gambar.', + ]); + + $spp = Spp::findOrFail($id); + + if ($request->hasFile('bukti_pembayaran')) { + $file = $request->file('bukti_pembayaran'); + // Membuat nama file unik + $nama_file = time() . "_" . $file->getClientOriginalName(); + + // Pindahkan gambar ke folder public/uploads/bukti_spp + $file->move(public_path('uploads/bukti_spp'), $nama_file); + + // Update status menjadi pending dan simpan nama file + $spp->update([ + 'bukti_pembayaran' => $nama_file, + 'status' => 'pending', + 'tanggal_bayar' => now(), // Mencatat waktu upload + ]); + + return redirect()->route('spp.anggota.index')->with('success', 'Bukti pembayaran berhasil diunggah! Menunggu konfirmasi pengurus.'); + } + + return redirect()->back()->with('error', 'Gagal mengunggah bukti pembayaran.'); + } } diff --git a/manajemen_mp/app/Models/Anggota.php b/manajemen_mp/app/Models/Anggota.php index 53178d5..bab64ee 100644 --- a/manajemen_mp/app/Models/Anggota.php +++ b/manajemen_mp/app/Models/Anggota.php @@ -54,5 +54,12 @@ public function user() return $this->belongsTo(User::class, 'user_id'); } +public function presensi() { + return $this->hasMany(Presensi::class); +} + +public function spp() { + return $this->hasMany(Spp::class); +} } diff --git a/manajemen_mp/app/Models/Spp.php b/manajemen_mp/app/Models/Spp.php index ba23102..cde2ea9 100644 --- a/manajemen_mp/app/Models/Spp.php +++ b/manajemen_mp/app/Models/Spp.php @@ -17,7 +17,8 @@ class Spp extends Model 'nominal', 'status', 'jatuh_tempo', - 'keterangan' + 'keterangan', + 'bukti_pembayaran' ]; // Relasi ke Anggota diff --git a/manajemen_mp/database/migrations/2026_05_04_014232_add_bukti_pembayaran_to_spp_table.php b/manajemen_mp/database/migrations/2026_05_04_014232_add_bukti_pembayaran_to_spp_table.php new file mode 100644 index 0000000..1951b10 --- /dev/null +++ b/manajemen_mp/database/migrations/2026_05_04_014232_add_bukti_pembayaran_to_spp_table.php @@ -0,0 +1,28 @@ +string('bukti_pembayaran')->nullable()->after('status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('spp', function (Blueprint $table) { + // + }); + } +}; diff --git a/manajemen_mp/public/uploads/bukti_spp/1777835228_profile.jpeg b/manajemen_mp/public/uploads/bukti_spp/1777835228_profile.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8231a55335b1f84d6deb600b83851a2feaaa7047 GIT binary patch literal 21700 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 Q5C&4^5E7LtQ7V7`*=*onD*ylh literal 0 HcmV?d00001 diff --git a/manajemen_mp/resources/views/anggota/anggotacreate.blade.php b/manajemen_mp/resources/views/anggota/anggotacreate.blade.php index 433ad65..174495a 100644 --- a/manajemen_mp/resources/views/anggota/anggotacreate.blade.php +++ b/manajemen_mp/resources/views/anggota/anggotacreate.blade.php @@ -1,6 +1,11 @@ @extends('layout.app') @section('content') +@php + // Ambil nama role user yang login saat ini + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; +@endphp +
+ @endif
@@ -189,7 +209,8 @@ function toggleSKFields() { const roleSelect = document.getElementById('roleSelect'); const boxSK = document.getElementById('boxSK'); - if (roleSelect.selectedIndex > 0) { + + if (boxSK && roleSelect.selectedIndex > 0) { const selectedText = roleSelect.options[roleSelect.selectedIndex].text.toLowerCase(); // Tampilkan box SK jika role mengandung kata Pelatih atau Pengurus diff --git a/manajemen_mp/resources/views/anggota/editanggota.blade.php b/manajemen_mp/resources/views/anggota/editanggota.blade.php index 6a86e51..879486b 100644 --- a/manajemen_mp/resources/views/anggota/editanggota.blade.php +++ b/manajemen_mp/resources/views/anggota/editanggota.blade.php @@ -1,14 +1,22 @@ @extends('layout.app') @section('content') +@php + // Ambil nama role user yang login saat ini + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; +@endphp +
@if ($errors->any()) -
-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
+
+
+ +
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
@endif @@ -43,6 +51,11 @@ - - + + {{-- HANYA PENGURUS YANG BISA JADIKAN PELATIH/PENGURUS --}} + @if($userRole == 'Pengurus') + + + @endif + + @if($userRole != 'Pengurus') + Anda hanya dapat mengubah data Anggota biasa. + @endif
@@ -97,6 +118,8 @@
+ {{-- BOX SK HANYA DIBUKA UNTUK PENGURUS --}} + @if($userRole == 'Pengurus')
+ @endif
@@ -134,8 +158,8 @@
@@ -146,10 +170,37 @@ + + @endsection diff --git a/manajemen_mp/resources/views/dashboard/dashboard.blade.php b/manajemen_mp/resources/views/dashboard/dashboard.blade.php index f8aaa3a..13663d9 100644 --- a/manajemen_mp/resources/views/dashboard/dashboard.blade.php +++ b/manajemen_mp/resources/views/dashboard/dashboard.blade.php @@ -1,5 +1,212 @@ @extends('layout.app') @section('content') -

Halo aku afi

+
+ +
+
+

Dashboard Pengurus

+

Ringkasan data Merpati Putih Cabang Jember • {{ date('d M Y') }}

+
+
+ + Mode Administrator + +
+
+ + +
+
+
+
+
+ +
+
+ Anggota Aktif +

{{ $total_anggota_aktif }}

+
+
+
+
+
+
+
+
+ +
+
+ Total Pelatih +

{{ $total_pelatih }}

+
+
+
+
+
+
+
+
+ +
+
+ Total Kolat +

{{ $total_kolat }}

+
+
+
+
+
+
+
+
+ +
+
+ SPP Tertunda +

{{ $spp_pending }}

+
+
+
+
+
+ + +
+
+
+
Tren Kehadiran Latihan (7 Hari Terakhir)
+
+ +
+
+
+
+
+
Komposisi Tingkatan
+
+ +
+
+
+
+ + +
+ +
+
+
Aktivitas Transaksi Terbaru
+
+ + + + + + + + + + + @foreach($aktivitas as $act) + + + + + + + @endforeach + +
WAKTUKEGIATANUSERSTATUS
{{ $act['waktu'] }}{{ $act['kegiatan'] }}{{ $act['user'] }} + + {{ $act['status'] }} + +
+
+
+
+ + +
+
+
Ringkasan Operasional
+ +
+ Kelayakan Ujian Saat Ini +
+

{{ $jumlah_layak_ujian }}

+ Anggota Siap Ujian +
+
+
+
+
+ + +
+
+
+
+ + + + + + @endsection diff --git a/manajemen_mp/resources/views/layout/sidebar.blade.php b/manajemen_mp/resources/views/layout/sidebar.blade.php index d77bddc..698a791 100644 --- a/manajemen_mp/resources/views/layout/sidebar.blade.php +++ b/manajemen_mp/resources/views/layout/sidebar.blade.php @@ -6,7 +6,9 @@ - - diff --git a/manajemen_mp/resources/views/login-register/login.blade.php b/manajemen_mp/resources/views/login-register/login.blade.php index ea3d1b8..d49b310 100644 --- a/manajemen_mp/resources/views/login-register/login.blade.php +++ b/manajemen_mp/resources/views/login-register/login.blade.php @@ -133,7 +133,7 @@ }); @endif - // Popup jika gagal login atau akun non-aktif + // Popup jika gagal login manual (dilempar via with('error', '...')) @if(session('error')) Swal.fire({ icon: 'warning', @@ -142,6 +142,22 @@ confirmButtonColor: '#06163a' }); @endif + + // [TAMBAHAN BARU] Popup jika validasi gagal (Email/Password salah) + @if($errors->any()) + Swal.fire({ + icon: 'error', + title: 'Login Gagal!', + html: ` +
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+ `, + confirmButtonColor: '#06163a' + }); + @endif diff --git a/manajemen_mp/resources/views/pelatih/pelatih.blade.php b/manajemen_mp/resources/views/pelatih/pelatih.blade.php index 38a34f1..bad0a5a 100644 --- a/manajemen_mp/resources/views/pelatih/pelatih.blade.php +++ b/manajemen_mp/resources/views/pelatih/pelatih.blade.php @@ -1,6 +1,11 @@ @extends('layout.app') @section('content') + @php + // Ambil nama role user yang login saat ini + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; + @endphp +
- + + + @if($userRole == 'Pengurus') Upgrade Anggota ke Pelatih + @endif
@@ -38,7 +46,11 @@ No. SK & Masa Berlaku Kolat Status SK + + + @if($userRole == 'Pengurus') Aksi + @endif @@ -64,6 +76,9 @@ {{ !$isExpired ? 'Berlaku' : 'Expired' }} + + + @if($userRole == 'Pengurus') + @endif @endforeach diff --git a/manajemen_mp/resources/views/profil/edit.blade.php b/manajemen_mp/resources/views/profil/edit.blade.php new file mode 100644 index 0000000..1c04644 --- /dev/null +++ b/manajemen_mp/resources/views/profil/edit.blade.php @@ -0,0 +1,238 @@ +@extends('layout.app') + +@section('content') +
+ +
+
+
+
+ +
+ +
+ + +
+ Avatar +
+ +
+

{{ $anggota->nama_lengkap }}

+

Kolat {{ $anggota->kolat->nama_kolat }}

+
+
+
+
+
+ + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + +
+ +
+ + +
+ @csrf + @method('PUT') + +
+
+
Data Pribadi
+ + +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + + +
+
+ + +
+ @csrf + @method('PUT') +
+
+
Keamanan & Password
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
Status Organisasi
+
+
+
+ + {{ $anggota->no_induk }} +
+
+
+ +
+ {{ $anggota->role->nama_role }} +
+
+
+ +
+ Kolat {{ $anggota->kolat->nama_kolat }} +
+
+
+ +
+ {{ $anggota->tingkatan ?? 'Belum ada data' }} +
+
+
+
+ + +
+
+
+ +
+
Butuh Bantuan?
+

Jika data organisasi (Kolat/Jabatan/Tingkat) tidak sesuai, silakan hubungi Pengurus Cabang melalui pelatih Anda.

+
+
+
+
+
+ + + + + +@endsection diff --git a/manajemen_mp/resources/views/rekap/index.blade.php b/manajemen_mp/resources/views/rekap/index.blade.php new file mode 100644 index 0000000..9c1b345 --- /dev/null +++ b/manajemen_mp/resources/views/rekap/index.blade.php @@ -0,0 +1,132 @@ +@extends('layout.app') + +@section('content') +
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+
Hasil Analisis
+

Menampilkan {{ $dataRekap->count() }} data hasil kalkulasi.

+
+
+ +
+ + + + + + + + + + + + @forelse($dataRekap as $no => $item) + + + + + + + + @empty + + + + @endforelse + +
NoIdentitasPresensiStatus SPPKelayakan Ujian
{{ $no + 1 }} +
{{ $item['nama'] }}
+ Tingkat: {{ $item['tingkatan'] }} +
+ + {{ $item['persentase_presensi'] }}% + +
{{ $item['status_aktif'] }}
+
+ + {{ $item['status_spp'] }} + + + @if($item['layak']) + + Layak + + @else + + Belum Layak + + @endif +
+ Data tidak ditemukan. +
+
+
+
+
+ + +@endsection diff --git a/manajemen_mp/resources/views/spp/anggota/bayar.blade.php b/manajemen_mp/resources/views/spp/anggota/bayar.blade.php new file mode 100644 index 0000000..bd85025 --- /dev/null +++ b/manajemen_mp/resources/views/spp/anggota/bayar.blade.php @@ -0,0 +1,51 @@ +@extends('layout.app') + +@section('content') +
+
+
+
+ + + +

Upload Bukti Pembayaran

+
+ + @if($errors->any()) +
+
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+
+
+
Informasi Tagihan
+

Anda akan melakukan pembayaran SPP periode {{ $spp->bulan }} {{ $spp->tahun }} sebesar Rp {{ number_format($spp->nominal, 0, ',', '.') }}.

+
+ + +
+ @csrf + +
+ + + +
* Format yang didukung: JPG, JPEG, PNG. Maksimal ukuran file 2MB.
+
+ + +
+
+
+
+
+
+@endsection diff --git a/manajemen_mp/resources/views/spp/anggota/index.blade.php b/manajemen_mp/resources/views/spp/anggota/index.blade.php new file mode 100644 index 0000000..c13b425 --- /dev/null +++ b/manajemen_mp/resources/views/spp/anggota/index.blade.php @@ -0,0 +1,85 @@ +@extends('layout.app') + +@section('content') +
+
+
+

Tagihan SPP Saya

+

Lihat riwayat dan lakukan pembayaran SPP bulanan.

+
+
+ + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + +
+
+
Daftar Tagihan
+
+
+ @if($data_spp->isEmpty()) +
+ +
Belum ada tagihan SPP untuk Anda.
+
+ @else +
+ + + + + + + + + + + + @foreach($data_spp as $spp) + + + + + + + + @endforeach + +
PeriodeNominalBatas BayarStatusAksi
+ {{ $spp->bulan }} {{ $spp->tahun }} + Rp {{ number_format($spp->nominal, 0, ',', '.') }} + {{ \Carbon\Carbon::parse($spp->jatuh_tempo)->translatedFormat('d F Y') }} + + @if($spp->status == 'lunas') + Lunas + @elseif($spp->status == 'pending') + Menunggu Verifikasi + @else + Belum Bayar + @endif + + @if($spp->status == 'belum_bayar') + + Upload Bukti + + @elseif($spp->status == 'pending') + + @else + + @endif +
+
+ @endif +
+
+
+ + +@endsection diff --git a/manajemen_mp/resources/views/spp/pengurus/create.blade.php b/manajemen_mp/resources/views/spp/pengurus/create.blade.php index 2e2a831..e5839bd 100644 --- a/manajemen_mp/resources/views/spp/pengurus/create.blade.php +++ b/manajemen_mp/resources/views/spp/pengurus/create.blade.php @@ -33,7 +33,7 @@ @foreach($data_anggota as $agt) @endforeach diff --git a/manajemen_mp/resources/views/spp/pengurus/index.blade.php b/manajemen_mp/resources/views/spp/pengurus/index.blade.php index c21bf50..37c3b09 100644 --- a/manajemen_mp/resources/views/spp/pengurus/index.blade.php +++ b/manajemen_mp/resources/views/spp/pengurus/index.blade.php @@ -1,43 +1,27 @@ @extends('layout.app') @section('content') +@php + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; +@endphp +
- {{--
-
-

Manajemen SPP

-

Kelola tagihan dan pembayaran SPP anggota Merpati Putih.

-
-
-
- @csrf - -
- - - Tambah Manual - - - -
-
--}} -

Manajemen SPP

Kelola tagihan dan pembayaran SPP anggota Merpati Putih.

+ {{-- HANYA PENGURUS YANG BISA GENERATE MASAL --}} + @if($userRole == 'Pengurus')
@csrf
+ @endif Excel @@ -53,6 +37,13 @@
+ + @if(session('success')) +
+ {{ session('success') }} +
+ @endif +
@@ -129,6 +120,7 @@
@@ -178,7 +170,6 @@
-
{{ strtoupper(substr($spp->anggota->nama_lengkap, 0, 1)) }} @@ -203,6 +194,10 @@ Lunas + @elseif($spp->status == 'pending') + + Menunggu Validasi + @elseif($spp->status == 'belum_bayar' && \Carbon\Carbon::parse($spp->jatuh_tempo)->isPast()) Terlambat @@ -214,18 +209,70 @@ @endif - @if($spp->status != 'lunas') + @if($spp->status == 'belum_bayar')
@csrf
+ + @elseif($spp->status == 'pending') +
+ + + {{-- HANYA PENGURUS YANG BISA VALIDASI STATUS PENDING --}} + @if($userRole == 'Pengurus') +
+ @csrf + +
+ @endif +
+ + + + @else - Terbayar + Terverifikasi @endif @@ -243,24 +290,10 @@ .bg-light { background-color: #f8f9fa !important; } .uppercase { text-transform: uppercase; letter-spacing: 0.8px; font-size: 10px; font-weight: 700; } .fs-1 { font-size: 2.2rem !important; opacity: 0.2; } - - /* Soft Badge Styles */ .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; - } - - .card { - transition: transform 0.2s; - } + .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; } @endsection diff --git a/manajemen_mp/routes/web.php b/manajemen_mp/routes/web.php index d3ca4f7..ae71342 100644 --- a/manajemen_mp/routes/web.php +++ b/manajemen_mp/routes/web.php @@ -10,6 +10,8 @@ use App\Http\Controllers\PelatihController; use App\Http\Controllers\PenilaianController; use App\Http\Controllers\PresensiController; +use App\Http\Controllers\ProfileController; +use App\Http\Controllers\RekapController; use App\Http\Controllers\SppController; use Illuminate\Support\Facades\Route; @@ -24,175 +26,156 @@ | */ -Route::get('/', function () { - return view('index'); -}); - -Route::get('/index', function () { - return view('layout.app'); -}); - -Route::get('/p', function () { - return view('user.user'); -}); - -Route::get('/anggota', function () { - return view('anggota.anggota'); -}); +// ======================================================= +// RUTE PUBLIK (TIDAK PERLU LOGIN) +// ======================================================= +Route::get('/', function () { return view('index'); }); +Route::get('/index', function () { return view('layout.app'); }); Route::middleware('guest')->group(function () { Route::get('/login', [AuthController::class, 'showLogin'])->name('login'); - Route::post('/login', [AuthController::class, 'login']); // Sesuaikan nama method - + Route::post('/login', [AuthController::class, 'login']); Route::get('/register', [AuthController::class, 'showRegister'])->name('register'); - Route::post('/register', [AuthController::class, 'register']); // Sesuaikan nama method + Route::post('/register', [AuthController::class, 'register']); }); -// Route untuk user yang SUDAH login +// ======================================================= +// RUTE GLOBAL (SEMUA ROLE BISA AKSES SETELAH LOGIN) +// ======================================================= Route::middleware('auth')->group(function () { Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); - - // Route dashboard atau menu lainnya masukkan di sini Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); - // anggota - Route::get('/anggota', [AnggotaController::class, 'anggota'])->name('anggota.anggota'); - Route::get('/anggota/tambah', [AnggotaController::class, 'create'])->name('anggota.create'); - Route::post('/anggota/simpan', [AnggotaController::class, 'store'])->name('anggota.store'); - Route::get('/anggota/{id}/edit', [AnggotaController::class, 'edit'])->name('anggota.edit'); - Route::put('/anggota/{id}', [AnggotaController::class, 'update'])->name('anggota.update'); - Route::get('/anggota/export/excel', [AnggotaController::class, 'exportExcel'])->name('anggota.export.excel'); - Route::get('/anggota/export/pdf', [AnggotaController::class, 'exportPdf'])->name('anggota.export.pdf'); + // ======================================================= + // 1. BLOK KHUSUS ANGGOTA + // ======================================================= + Route::middleware('checkrole:Anggota')->group(function () { + // SPP Anggota (Upload Bukti) + Route::prefix('anggota/spp')->name('spp.anggota.')->group(function () { + Route::get('/', [SppController::class, 'indexAnggota'])->name('index'); + Route::get('/bayar/{id}', [SppController::class, 'formBayarAnggota'])->name('formBayar'); + Route::post('/bayar/{id}', [SppController::class, 'prosesBayarAnggota'])->name('prosesBayar'); + }); - // kolat - Route::get('/kolat', [KolatController::class, 'kolat'])->name('kolat.index'); - Route::get('/kolat/tambah', [KolatController::class, 'create'])->name('kolat.create'); - Route::post('/kolat/simpan', [KolatController::class, 'store'])->name('kolat.store'); - Route::get('/kolat/{id}/edit', [KolatController::class, 'edit'])->name('kolat.edit'); - Route::put('/kolat/{id}', [KolatController::class, 'update'])->name('kolat.update'); - Route::delete('/kolat/{id}', [KolatController::class, 'destroy'])->name('kolat.destroy'); + // Presensi Mandiri + Route::prefix('presensi/anggota')->name('presensi.anggota.')->group(function () { + Route::get('/', [PresensiController::class, 'indexAnggota'])->name('index'); + Route::get('/riwayat', [PresensiController::class, 'riwayatAnggota'])->name('riwayat'); + Route::get('/scan/{jadwal_id}', [PresensiController::class, 'scan'])->name('scan'); + Route::post('/store-mandiri', [PresensiController::class, 'storeMandiri'])->name('storeMandiri'); + }); - // pelatih - // prefix diguakan untuk mengelompokkan route yang berhubungan dengan pelatih, sehingga URL nya menjadi /manajemen-pelatih/... - Route::prefix('manajemen-pelatih')->group(function () { - Route::get('/', [PelatihController::class, 'index'])->name('pelatih.index'); - Route::get('/upgrade', [PelatihController::class, 'create'])->name('pelatih.upgrade'); - Route::post('/upgrade', [PelatihController::class, 'store'])->name('pelatih.store'); - Route::get('/{id}/edit', [PelatihController::class, 'edit'])->name('pelatih.edit'); - Route::put('/{id}', [PelatihController::class, 'update'])->name('pelatih.update'); + // Penilaian (Anggota mengisi nilai) + Route::get('/penilaian/create/{id}', [PenilaianController::class, 'create'])->name('penilaian.create'); + Route::post('/penilaian/store', [PenilaianController::class, 'store'])->name('penilaian.store'); }); - // jadwal - // prefix diguakan untuk mengelompokkan route yang berhubungan dengan jadwal - Route::prefix('jadwal')->group(function () { - Route::get('/', [JadwalController::class, 'index'])->name('jadwal.index'); - Route::post('/jadwal/{id}/konfirmasi', [JadwalController::class, 'konfirmasiSelesai'])->name('jadwal.konfirmasi'); - Route::get('/create', [JadwalController::class, 'create'])->name('jadwal.create'); - Route::get('/{id}/detail', [JadwalController::class, 'show'])->name('jadwal.show'); - Route::post('/store', [JadwalController::class, 'store'])->name('jadwal.store'); - Route::get('/{id}/edit', [JadwalController::class, 'edit'])->name('jadwal.edit'); - Route::put('/{id}/update', [JadwalController::class, 'update'])->name('jadwal.update'); - Route::delete('/{id}/delete', [JadwalController::class, 'destroy'])->name('jadwal.destroy'); - }); + // ======================================================= + // 2. BLOK KHUSUS PENGURUS & PELATIH (Bisa Akses Bersama) + // ======================================================= + Route::middleware('checkrole:Pengurus,Pelatih')->group(function () { + // Lihat Master Data Dasar + Route::get('/anggota', [AnggotaController::class, 'anggota'])->name('anggota.anggota'); + Route::get('/anggota/tambah', [AnggotaController::class, 'create'])->name('anggota.create'); + Route::post('/anggota/simpan', [AnggotaController::class, 'store'])->name('anggota.store'); + Route::get('/anggota/{id}/edit', [AnggotaController::class, 'edit'])->name('anggota.edit'); + Route::put('/anggota/{id}', [AnggotaController::class, 'update'])->name('anggota.update'); + Route::get('/kolat', [KolatController::class, 'kolat'])->name('kolat.index'); + Route::get('/manajemen-pelatih', [PelatihController::class, 'index'])->name('pelatih.index'); + Route::get('/jadwal', [JadwalController::class, 'index'])->name('jadwal.index'); + Route::get('/jadwal/{id}/detail', [JadwalController::class, 'show'])->name('jadwal.show'); - // Rute untuk Presensi - Route::prefix('presensi')->name('presensi.')->group(function () { - Route::get('/', [PresensiController::class, 'index'])->name('index'); + // Presensi (Verifikasi & Manual) + Route::prefix('presensi')->name('presensi.')->group(function () { + Route::get('/', [PresensiController::class, 'index'])->name('index'); + Route::get('/kehadiran/{jadwal_id}', [PresensiController::class, 'kehadiran'])->name('kehadiran'); + Route::post('/konfirmasi/{presensi_id}', [PresensiController::class, 'konfirmasi'])->name('konfirmasi'); + Route::post('/tambah-manual', [PresensiController::class, 'storeManual'])->name('storeManual'); + }); - // Halaman daftar kehadiran anggota pada jadwal tertentu (tampil QR Code & List) - Route::get('/kehadiran/{jadwal_id}', [PresensiController::class, 'kehadiran'])->name('kehadiran'); + Route::post('/jadwal/{id}/konfirmasi', [JadwalController::class, 'konfirmasiSelesai'])->name('jadwal.konfirmasi'); + Route::get('/jadwal/create', [JadwalController::class, 'create'])->name('jadwal.create'); + Route::post('/jadwal/store', [JadwalController::class, 'store'])->name('jadwal.store'); + Route::get('/jadwal/{id}/edit', [JadwalController::class, 'edit'])->name('jadwal.edit'); + Route::put('/jadwal/{id}/update', [JadwalController::class, 'update'])->name('jadwal.update'); + Route::delete('/jadwal/{id}/delete', [JadwalController::class, 'destroy'])->name('jadwal.destroy'); - // Proses konfirmasi/verifikasi pelatih klik tombol "Sah" - Route::post('/konfirmasi/{presensi_id}', [PresensiController::class, 'konfirmasi'])->name('konfirmasi'); + // Lihat SPP & Penilaian + Route::get('/spp', [SppController::class, 'index'])->name('spp.index'); + Route::get('/penilaian', [PenilaianController::class, 'index'])->name('penilaian.index'); + Route::get('/penilaian/rekap/{id}', [PenilaianController::class, 'show'])->name('penilaian.show'); - // Proses tambah presensi manual (Kasus Lupa HP) - Route::post('/tambah-manual', [PresensiController::class, 'storeManual'])->name('storeManual'); + // Laporan (Hanya Presensi) + Route::get('/laporan/presensi', [LaporanController::class, 'presensi'])->name('laporan.presensi'); + Route::get('/laporan/presensi/excel', [LaporanController::class, 'presensiExcel'])->name('laporan.presensi.excel'); + Route::get('/laporan/presensi/pdf', [LaporanController::class, 'presensiPdf'])->name('laporan.presensi.pdf'); - // --- AKTOR: ANGGOTA --- - // Halaman daftar jadwal hari ini di sisi anggota (Tampil tombol "Scan Absen") - Route::get('/anggota', [PresensiController::class, 'indexAnggota'])->name('anggota.index'); - - Route::get('/riwayat', [PresensiController::class, 'riwayatAnggota'])->name('anggota.riwayat'); - - // Halaman Kamera Scanner (Tempat anggota men-scan QR Code pelatih) - Route::get('/scan/{jadwal_id}', [PresensiController::class, 'scan'])->name('anggota.scan'); - - // Proses penyimpanan absen mandiri setelah sukses scan - Route::post('/store-mandiri', [PresensiController::class, 'storeMandiri'])->name('storeMandiri'); + // Menu Rekap Kelayakan (Ditambahkan di sini) + Route::get('/rekap/kelayakan', [RekapController::class, 'index'])->name('rekap.index'); }); -//Penialaian + // ======================================================= + // 3. BLOK KHUSUS PENGURUS SAJA (Akses Tertinggi) + // ======================================================= + Route::middleware('checkrole:Pengurus')->group(function () { + // Aksi CRUD Anggota - Route::prefix('penilaian')->name('penilaian.')->group(function () { - Route::get('/', [PenilaianController::class, 'index'])->name('index'); - // Hilangkan kata 'penilaian' di dalam sini karena sudah ada di prefix - Route::get('/create/{id}', [PenilaianController::class, 'create'])->name('create'); - Route::post('/store', [PenilaianController::class, 'store'])->name('store'); - Route::get('/penilaian/rekap/{id}', [PenilaianController::class, 'show'])->name('show'); -}); + Route::get('/anggota/export/excel ', [AnggotaController::class, 'exportExcel'])->name('anggota.export.excel'); + Route::get('/anggota/export/pdf', [AnggotaController::class, 'exportPdf'])->name('anggota.export.pdf'); -//Kas -Route::prefix('kas')->name('kas.')->group(function () { - Route::get('/', [KasController::class, 'index'])->name('index'); - Route::post('/store', [KasController::class, 'store'])->name('store'); - Route::put('/update/{id}', [KasController::class, 'update'])->name('update'); - Route::delete('/{id}', [KasController::class, 'destroy'])->name('destroy'); -}); + // Aksi CRUD Kolat + Route::get('/kolat/tambah', [KolatController::class, 'create'])->name('kolat.create'); + Route::post('/kolat/simpan', [KolatController::class, 'store'])->name('kolat.store'); + Route::get('/kolat/{id}/edit', [KolatController::class, 'edit'])->name('kolat.edit'); + Route::put('/kolat/{id}', [KolatController::class, 'update'])->name('kolat.update'); + Route::delete('/kolat/{id}', [KolatController::class, 'destroy'])->name('kolat.destroy'); -// Group SPP -Route::prefix('spp')->name('spp.')->group(function () { - Route::get('/', [SppController::class, 'index'])->name('index'); - Route::get('/create', [SppController::class, 'create'])->name('create'); - Route::post('/store', [SppController::class, 'store'])->name('store'); - Route::post('/bayar/{id}', [SppController::class, 'bayar'])->name('bayar'); - Route::post('/generate', [SppController::class, 'generateTagihan'])->name('generate'); -}); + // Aksi CRUD Pelatih + Route::get('/manajemen-pelatih/upgrade', [PelatihController::class, 'create'])->name('pelatih.upgrade'); + Route::post('/manajemen-pelatih/upgrade', [PelatihController::class, 'store'])->name('pelatih.store'); + Route::get('/manajemen-pelatih/{id}/edit', [PelatihController::class, 'edit'])->name('pelatih.edit'); + Route::put('/manajemen-pelatih/{id}', [PelatihController::class, 'update'])->name('pelatih.update'); - // --- AKTOR: PENGURUS / PELATIH --- - // Halaman utama rekap jadwal (untuk pilih jadwal mana yang mau dicek) + // Aksi CRUD Jadwal -//Penialaian + // Aksi Transaksi SPP + Route::prefix('spp')->name('spp.')->group(function () { + Route::get('/create', [SppController::class, 'create'])->name('create'); + Route::post('/store', [SppController::class, 'store'])->name('store'); + Route::post('/bayar/{id}', [SppController::class, 'bayar'])->name('bayar'); + Route::post('/generate', [SppController::class, 'generateTagihan'])->name('generate'); + Route::get('/export/excel', [SppController::class, 'exportExcel'])->name('export.excel'); + Route::get('/export/pdf', [SppController::class, 'exportPdf'])->name('export.pdf'); + }); - Route::prefix('penilaian')->name('penilaian.')->group(function () { - Route::get('/', [PenilaianController::class, 'index'])->name('index'); - // Hilangkan kata 'penilaian' di dalam sini karena sudah ada di prefix - Route::get('/create/{id}', [PenilaianController::class, 'create'])->name('create'); - Route::post('/store', [PenilaianController::class, 'store'])->name('store'); - Route::get('/penilaian/rekap/{id}', [PenilaianController::class, 'show'])->name('show'); -}); + // Kas Cabang (Terkunci Mutlak) + Route::prefix('kas')->name('kas.')->group(function () { + Route::get('/', [KasController::class, 'index'])->name('index'); + Route::post('/store', [KasController::class, 'store'])->name('store'); + Route::put('/update/{id}', [KasController::class, 'update'])->name('update'); + Route::delete('/{id}', [KasController::class, 'destroy'])->name('destroy'); + Route::get('/export/excel', [KasController::class, 'exportExcel'])->name('export.excel'); + Route::get('/export/pdf', [KasController::class, 'exportPdf'])->name('export.pdf'); + }); -//Kas -Route::prefix('kas')->name('kas.')->group(function () { - Route::get('/', [KasController::class, 'index'])->name('index'); - Route::post('/store', [KasController::class, 'store'])->name('store'); - Route::put('/update/{id}', [KasController::class, 'update'])->name('update'); - Route::delete('/{id}', [KasController::class, 'destroy'])->name('destroy'); - Route::get('/export/excel', [KasController::class, 'exportExcel'])->name('export.excel'); - Route::get('/export/pdf', [KasController::class, 'exportPdf'])->name('export.pdf'); -}); - -// Group SPP -Route::prefix('spp')->name('spp.')->group(function () { - Route::get('/', [SppController::class, 'index'])->name('index'); - Route::get('/create', [SppController::class, 'create'])->name('create'); - Route::post('/store', [SppController::class, 'store'])->name('store'); - Route::post('/bayar/{id}', [SppController::class, 'bayar'])->name('bayar'); - Route::post('/generate', [SppController::class, 'generateTagihan'])->name('generate'); - Route::get('/export/excel', [SppController::class, 'exportExcel'])->name('export.excel'); - Route::get('/export/pdf', [SppController::class, 'exportPdf'])->name('export.pdf'); -}); - -Route::prefix('laporan')->name('laporan.')->group(function () { - Route::get('/anggota', [LaporanController::class, 'anggota'])->name('anggota'); - Route::get('/kas', [LaporanController::class, 'kas'])->name('kas'); - Route::get('/spp', [LaporanController::class, 'spp'])->name('spp'); - Route::get('/presensi', [LaporanController::class, 'presensi'])->name('presensi'); - Route::get('/presensi/excel', [LaporanController::class, 'presensiExcel'])->name('presensi.excel'); - Route::get('/presensi/pdf', [LaporanController::class, 'presensiPdf'])->name('presensi.pdf'); -}); + // Sisa Laporan + Route::prefix('laporan')->name('laporan.')->group(function () { + Route::get('/anggota', [LaporanController::class, 'anggota'])->name('anggota'); + Route::get('/kas', [LaporanController::class, 'kas'])->name('kas'); + Route::get('/spp', [LaporanController::class, 'spp'])->name('spp'); + }); + }); + + Route::get('/profile/edit', [ProfileController::class, 'edit'])->name('profile.edit'); + Route::put('/profile/update', [ProfileController::class, 'update'])->name('profile.update'); + Route::put('/profile/password', [ProfileController::class, 'updatePassword'])->name('profile.password'); + }); + + +