From 50a5d551699cf8dc8bae82b832863bc2d0393d69 Mon Sep 17 00:00:00 2001 From: ell Date: Thu, 30 Apr 2026 17:40:44 +0700 Subject: [PATCH] presensi --- .../Http/Controllers/AnggotaController.php | 4 +- .../app/Http/Controllers/JadwalController.php | 85 ++- .../Http/Controllers/PresensiController.php | 264 ++++++--- manajemen_mp/app/Http/Controllers/backup.txt | 125 ++++ manajemen_mp/app/Models/Jadwal.php | 4 +- manajemen_mp/app/Models/Presensi.php | 9 +- manajemen_mp/app/Models/User.php | 1 + manajemen_mp/composer.json | 3 +- manajemen_mp/composer.lock | 174 +++++- manajemen_mp/config/app.php | 1 + .../database/seeders/DatabaseSeeder.php | 11 +- manajemen_mp/database/seeders/KolatSeeder.php | 23 + manajemen_mp/database/seeders/UserSeeder.php | 47 ++ .../views/anggota/editanggota.blade.php | 523 +++++------------ .../resources/views/jadwal/backup.blade.php | 547 ++++++++++++++++++ .../views/jadwal/editjadwal.blade.php | 109 ++-- .../resources/views/jadwal/jadwal.blade.php | 295 +++++++--- .../resources/views/jadwal/show.blade.php | 0 .../resources/views/layout/navbar.blade.php | 13 +- .../resources/views/layout/sidebar.blade.php | 58 +- .../views/presensi/anggota/index.blade.php | 165 ++++-- .../views/presensi/anggota/riwayat.blade.php | 179 ++++++ .../views/presensi/anggota/scan.blade.php | 128 ++++ .../views/presensi/pengurus/index.blade.php | 119 +++- .../presensi/pengurus/kehadiran.blade.php | 336 ++++++++--- manajemen_mp/routes/web.php | 92 +-- 26 files changed, 2492 insertions(+), 823 deletions(-) create mode 100644 manajemen_mp/app/Http/Controllers/backup.txt create mode 100644 manajemen_mp/database/seeders/KolatSeeder.php create mode 100644 manajemen_mp/database/seeders/UserSeeder.php create mode 100644 manajemen_mp/resources/views/jadwal/backup.blade.php create mode 100644 manajemen_mp/resources/views/jadwal/show.blade.php diff --git a/manajemen_mp/app/Http/Controllers/AnggotaController.php b/manajemen_mp/app/Http/Controllers/AnggotaController.php index 5a1fc9e..b7f9660 100644 --- a/manajemen_mp/app/Http/Controllers/AnggotaController.php +++ b/manajemen_mp/app/Http/Controllers/AnggotaController.php @@ -84,7 +84,7 @@ public function store(Request $request) 'alamat' => $request->alamat, 'catatan_medis' => $request->catatan_medis, 'jabatan' => $nama_jabatan, - 'status' => 'aktif', // Input admin otomatis aktif + 'status' => 'Aktif', // Input admin otomatis aktif ]); DB::commit(); @@ -120,7 +120,7 @@ public function update(Request $request, $id) 'no_hp' => 'required', 'role_id' => 'required|exists:role,id', 'kolat_id' => 'required|exists:kolat,id', - 'status' => 'required|in:aktif,Non-Aktif', + 'status' => 'required|in:Aktif,Non-Aktif', ]); DB::beginTransaction(); diff --git a/manajemen_mp/app/Http/Controllers/JadwalController.php b/manajemen_mp/app/Http/Controllers/JadwalController.php index 0a0c924..669d5f2 100644 --- a/manajemen_mp/app/Http/Controllers/JadwalController.php +++ b/manajemen_mp/app/Http/Controllers/JadwalController.php @@ -5,15 +5,17 @@ use App\Models\Jadwal; use App\Models\Kolat; use App\Models\Anggota; -use App\Models\Role; +use Illuminate\Support\Facades\Storage; use Illuminate\Http\Request; class JadwalController extends Controller { + // 1. Menampilkan Daftar Jadwal public function index() { - $data_jadwal = Jadwal::with(['kolat', 'pelatih']) + // Tambahkan 'presensi.anggota' agar modal konfirmasi bisa nampilin siapa aja yang hadir + $data_jadwal = Jadwal::with(['kolat', 'pelatih', 'presensi.anggota']) ->orderBy('tanggal', 'desc') ->orderBy('jam_mulai', 'desc') ->get(); @@ -21,33 +23,49 @@ public function index() return view('jadwal.jadwal', compact('data_jadwal')); } - public function konfirmasiSelesai($id) -{ - $jadwal = Jadwal::findOrFail($id); + // 2. Proses Konfirmasi Latihan Selesai (Update Status + Upload Foto Kegiatan) + public function konfirmasiSelesai(Request $request, $id) + { + $jadwal = Jadwal::findOrFail($id); - // Kita paksa ubah status ke 'selesai' - $jadwal->status = 'selesai'; - $jadwal->save(); + // Validasi input dari modal + $request->validate([ + 'foto_bukti' => 'required|image|mimes:jpg,jpeg,png|max:2048', + 'catatan_latihan' => 'nullable|string' + ]); - return redirect()->route('jadwal.index') - ->with('success', 'Latihan ' . $jadwal->judul_kegiatan . ' telah dikonfirmasi selesai!'); -} + // Proses upload foto kegiatan pelatih ke folder storage/app/public/kegiatan_pelatih + if ($request->hasFile('foto_bukti')) { + $path = $request->file('foto_bukti')->store('kegiatan_pelatih', 'public'); + $jadwal->foto_bukti = $path; + } + // Simpan catatan dan ubah status menjadi selesai + $jadwal->catatan_latihan = $request->catatan_latihan; + $jadwal->status = 'selesai'; + $jadwal->save(); + return redirect()->route('jadwal.index') + ->with('success', 'Latihan ' . $jadwal->judul_kegiatan . ' telah dikonfirmasi selesai!'); + } + + // 3. Menampilkan Form Tambah Jadwal public function create() { $data_kolat = Kolat::all(); - $rolePelatih = Role::where('nama_role', 'Pelatih')->first(); - // Ambil Anggota yang memiliki role 'Pelatih' saja (filter lewat relasi) + // Ambil Anggota yang memiliki role 'Pelatih' dan statusnya 'Aktif' $data_pelatih = Anggota::whereHas('role', function($query) { $query->where('nama_role', 'Pelatih'); - })->get(); + }) + ->where('status', 'Aktif') + ->orderBy('nama_lengkap', 'asc') + ->get(); return view('jadwal.jadwalcreate', compact('data_kolat', 'data_pelatih')); } - + // 4. Proses Simpan Jadwal Baru public function store(Request $request) { $request->validate([ @@ -66,14 +84,14 @@ public function store(Request $request) ->with('success', 'Jadwal latihan berhasil ditambahkan!'); } + // 5. Menampilkan Detail Jadwal (Opsional jika pakai modal) public function show($id) -{ - // Ambil satu data dengan relasinya - $jadwal = Jadwal::with(['kolat', 'pelatih'])->findOrFail($id); - - return view('jadwal.show', compact('jadwal')); -} + { + $jadwal = Jadwal::with(['kolat', 'pelatih'])->findOrFail($id); + return view('jadwal.show', compact('jadwal')); + } + // 6. Menampilkan Form Edit Jadwal public function edit($id) { $jadwal = Jadwal::findOrFail($id); @@ -87,18 +105,19 @@ public function edit($id) return view('jadwal.editjadwal', compact('jadwal', 'data_kolat', 'data_pelatih')); } + // 7. Proses Update Jadwal public function update(Request $request, $id) { $jadwal = Jadwal::findOrFail($id); - $request->validate([ - 'judul_kegiatan' => 'required|string|max:255', - 'kolat_id' => 'required|exists:kolat,id', - 'pelatih_id' => 'required|exists:anggotas,id', - 'jenis' => 'required|in:Rutin,Tambahan,Pengumuman', - 'tanggal' => 'required|date', - 'jam_mulai' => 'required', - 'lokasi' => 'required|string', + $request->validate([ + 'judul_kegiatan' => 'required|string|max:255', + 'kolat_id' => 'required|exists:kolat,id', + 'pelatih_id' => 'required|exists:anggotas,id', + 'jenis' => 'required|in:Rutin,Tambahan,Pengumuman', + 'tanggal' => 'required|date', + 'jam_mulai' => 'required', + 'lokasi' => 'required|string', ]); $jadwal->update($request->all()); @@ -107,10 +126,16 @@ public function update(Request $request, $id) ->with('success', 'Jadwal berhasil diperbarui!'); } - + // 8. Proses Hapus Jadwal public function destroy($id) { $jadwal = Jadwal::findOrFail($id); + + // Cek dan hapus file foto bukti dari storage jika ada, biar hosting nggak penuh + if ($jadwal->foto_bukti) { + Storage::disk('public')->delete($jadwal->foto_bukti); + } + $jadwal->delete(); return redirect()->route('jadwal.index') diff --git a/manajemen_mp/app/Http/Controllers/PresensiController.php b/manajemen_mp/app/Http/Controllers/PresensiController.php index 1351a48..c83a42b 100644 --- a/manajemen_mp/app/Http/Controllers/PresensiController.php +++ b/manajemen_mp/app/Http/Controllers/PresensiController.php @@ -2,127 +2,177 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; - +use App\Models\Anggota; // TAMBAHAN WAJIB: Memanggil Facades dan Model yang digunakan -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Auth; use App\Models\Jadwal; use App\Models\Presensi; -use App\Models\Anggota; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; class PresensiController extends Controller { + /** + * AKTOR: PENGURUS/PELATIH + * Menampilkan daftar semua jadwal untuk dipilih mana yang mau dicek presensinya + */ public function index() { - // Mengambil semua jadwal secara manual menggunakan Query Builder (JOIN) - $data_jadwal = DB::table('jadwals') - ->leftJoin('anggotas', 'jadwals.pelatih_id', '=', 'anggotas.id') - ->leftJoin('kolat', 'jadwals.kolat_id', '=', 'kolat.id') // Sesuaikan jika nama tabel di database kamu adalah 'kolat' - ->select( - 'jadwals.*', - 'anggotas.nama_lengkap as nama_pelatih', - 'kolat.nama_kolat as nama_kolat' - ) - ->orderBy('jadwals.tanggal', 'desc') + // Menggunakan Eloquent agar lebih konsisten dengan fungsi lainnya + $data_jadwal = Jadwal::with(['kolat', 'pelatih']) + ->orderBy('tanggal', 'desc') + ->orderBy('jam_mulai', 'desc') ->get(); - // Pastikan nama foldernya konsisten (admin atau pengurus) return view('presensi.pengurus.index', compact('data_jadwal')); } + /** + * AKTOR: PENGURUS/PELATIH + * Menampilkan detail siapa saja yang sudah scan/absen di jadwal tertentu + */ public function kehadiran($jadwal_id) -{ - // Variabel dibuat dengan nama $jadwal (tunggal) - $jadwal = Jadwal::with(['kolat', 'pelatih'])->findOrFail($jadwal_id); + { + $jadwal = Jadwal::with(['kolat', 'pelatih'])->findOrFail($jadwal_id); - $data_presensi = Presensi::with('anggota', 'pelatihVerifikator') - ->where('jadwal_id', $jadwal_id) - ->get(); + $data_presensi = Presensi::with('anggota', 'pelatihVerifikator') + ->where('jadwal_id', $jadwal_id) + ->get(); - $daftar_anggota = Anggota::where('kolat_id', $jadwal->kolat_id) - ->where('status', 'Aktif') - ->get(); + // 1. Ambil array ID anggota yang SUDAH absen + $id_sudah_absen = $data_presensi->pluck('anggota_id')->toArray(); - // Pastikan di sini tulisannya 'jadwal' (sesuai nama variabel di atas) - return view('presensi.pengurus.kehadiran', compact('jadwal', 'data_presensi', 'daftar_anggota')); -} + // 2. Ambil anggota yang se-Kolat, status Aktif, tapi ID-nya BELUM ADA di daftar absen + $anggota_belum_absen = Anggota::where('kolat_id', $jadwal->kolat_id) + ->where('status', 'Aktif') + ->whereNotIn('id', $id_sudah_absen) + ->get(); + + // Kirim variabel baru ini ke View + return view('presensi.pengurus.kehadiran', compact('jadwal', 'data_presensi', 'anggota_belum_absen')); + } /** - * 2. Proses Pelatih klik "Konfirmasi Hadir" + * AKTOR: PENGURUS/PELATIH + * Mensahkan (Verify) absensi anggota yang masuk lewat scan QR */ - public function konfirmasi($presensi_id) + public function konfirmasi(Request $request, $presensi_id) { $presensi = Presensi::findOrFail($presensi_id); - // Ubah status menjadi terverifikasi dan catat ID Pelatih yang klik - $presensi->update([ - 'is_verified' => true, - 'verified_by' => Auth::user()->id + $request->validate([ + 'action' => 'required|in:sah,tolak', + 'keterangan' => 'nullable|string' // <-- Tambahan validasi keterangan ]); - return redirect()->back()->with('success', 'Kehadiran ' . $presensi->anggota->nama_lengkap . ' berhasil dikonfirmasi!'); + if ($request->action == 'sah') { + $presensi->update([ + 'is_verified' => true, + 'status' => 'Hadir', + 'verified_by' => Auth::user()->anggota->id, + ]); + $pesan = 'Kehadiran ' . $presensi->anggota->nama_lengkap . ' berhasil disahkan!'; + } else { + $presensi->update([ + 'is_verified' => true, + 'status' => 'Alfa', + 'verified_by' => Auth::user()->anggota->id, + 'keterangan' => $request->keterangan // <-- Simpan alasan penolakan + ]); + $pesan = 'Absensi ' . $presensi->anggota->nama_lengkap . ' ditolak (Alfa).'; + } + + return redirect()->back()->with('success', $pesan); } /** - * 3. Proses Pelatih input manual dari Modal (Kasus Lupa HP) + * AKTOR: PENGURUS/PELATIH + * Input manual jika anggota lupa bawa HP (Otomatis is_verified = true) */ public function storeManual(Request $request) { - // Validasi input dari form modal $request->validate([ 'jadwal_id' => 'required|exists:jadwals,id', 'anggota_id' => 'required|exists:anggotas,id', + 'status' => 'required|in:Hadir,Izin,Sakit,Alfa', + 'keterangan' => 'nullable|string' // <-- Tambahan validasi keterangan ]); - // Cek apakah anggota tersebut sudah absen sebelumnya agar tidak ada data ganda (double input) $sudah_absen = Presensi::where('jadwal_id', $request->jadwal_id) - ->where('anggota_id', $request->anggota_id) - ->first(); + ->where('anggota_id', $request->anggota_id) + ->first(); if ($sudah_absen) { - return redirect()->back()->with('error', 'Anggota tersebut sudah tercatat di daftar kehadiran!'); + return redirect()->back()->with('error', 'Anggota tersebut sudah memiliki catatan kehadiran di jadwal ini!'); } - // Simpan data presensi baru - // Karena diinput langsung oleh pelatih, is_verified otomatis bernilai true Presensi::create([ 'jadwal_id' => $request->jadwal_id, 'anggota_id' => $request->anggota_id, - 'status' => 'Hadir', - 'waktu_presensi' => now(), // Waktu dicatat saat form disubmit - 'is_verified' => true, // Langsung Sah - 'verified_by' => Auth::user()->id + 'status' => $request->status, + 'keterangan' => $request->keterangan, // <-- Simpan keterangan manual + 'waktu_presensi' => now(), + 'is_verified' => true, + 'verified_by' => Auth::user()->anggota->id, ]); - return redirect()->back()->with('success', 'Presensi manual berhasil ditambahkan!'); + return redirect()->back()->with('success', 'Presensi manual berhasil disimpan!'); } - + /** + * AKTOR: ANGGOTA + * Melihat daftar jadwal latihan hari ini untuk di-scan + */ + /** + * AKTOR: ANGGOTA + * Melihat daftar jadwal latihan hari ini dan riwayat 7 hari terakhir + */ public function indexAnggota() { + if (!Auth::check()) { + return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu.'); + } + + $user = Auth::user(); $now = \Carbon\Carbon::now('Asia/Jakarta'); $hari_ini = $now->format('Y-m-d'); - $user = Auth::user(); - // 1. Ambil jadwal latihan yang terjadi HARI INI sesuai dengan Kolat si Anggota - // Asumsinya di tabel anggotas (users) ada kolom kolat_id - $jadwal_hari_ini = Jadwal::with('pelatih') - ->where('tanggal', $hari_ini) - ->where('kolat_id', $user->kolat_id) - ->orderBy('jam_mulai', 'asc') + // Ambil tanggal 7 hari yang lalu + $seminggu_lalu = $now->copy()->subDays(7)->format('Y-m-d'); + + if (!$user->anggota) { + return redirect()->route('presensi.index')->with('error', 'Akun Anda tidak terdaftar sebagai peserta latihan.'); + } + + // Ambil jadwal dalam rentang 7 hari terakhir hingga hari ini + $jadwal_terbaru = Jadwal::with('pelatih') + ->whereBetween('tanggal', [$seminggu_lalu, $hari_ini]) + ->where('kolat_id', $user->anggota->kolat_id) + ->orderBy('tanggal', 'desc') + ->orderBy('jam_mulai', 'desc') ->get(); - // 2. Cek apakah anggota ini sudah absen di jadwal tersebut - // Menggunakan pluck() untuk mengambil ID jadwal hari ini saja - $presensi_saya = Presensi::where('anggota_id', $user->id) - ->whereIn('jadwal_id', $jadwal_hari_ini->pluck('id')) + $presensi_saya = Presensi::where('anggota_id', $user->anggota->id) + ->whereIn('jadwal_id', $jadwal_terbaru->pluck('id')) ->get() - ->keyBy('jadwal_id'); // Jadikan ID jadwal sebagai key array agar mudah dicek di View + ->keyBy('jadwal_id'); - return view('presensi.anggota.index', compact('jadwal_hari_ini', 'presensi_saya', 'now')); + return view('presensi.anggota.index', compact('jadwal_terbaru', 'presensi_saya', 'now', 'hari_ini')); } + /** + * AKTOR: ANGGOTA + * Menampilkan halaman kamera scanner + */ + public function scan($jadwal_id) + { + $jadwal = Jadwal::findOrFail($jadwal_id); + return view('presensi.anggota.scan', compact('jadwal')); + } + + /** + * AKTOR: ANGGOTA + * Menyimpan data presensi hasil scan (Menunggu Verifikasi Pelatih) + */ public function storeMandiri(Request $request) { $request->validate([ @@ -130,26 +180,86 @@ public function storeMandiri(Request $request) ]); $user = Auth::user(); + $profilAnggota = $user->anggota; - // Cek agar tidak bisa klik absen 2 kali (Double submit) - $sudah_absen = Presensi::where('jadwal_id', $request->jadwal_id) - ->where('anggota_id', $user->id) - ->exists(); - - if ($sudah_absen) { - return redirect()->back()->with('error', 'Kamu sudah melakukan absensi untuk jadwal ini!'); + if (!$profilAnggota) { + return redirect()->back()->with('error', 'Data profil tidak lengkap.'); + } + + $jadwal = Jadwal::findOrFail($request->jadwal_id); + $now = \Carbon\Carbon::now('Asia/Jakarta'); + + // Validasi Waktu: Gabungkan tanggal dan jam untuk pengecekan presisi + $jam_selesai = $jadwal->jam_selesai + ? $jadwal->jam_selesai + : \Carbon\Carbon::parse($jadwal->jam_mulai)->addHours(3)->format('H:i:s'); + + $batas_waktu = \Carbon\Carbon::parse($jadwal->tanggal . ' ' . $jam_selesai, 'Asia/Jakarta'); + + if ($now->gt($batas_waktu)) { + return redirect()->route('presensi.anggota.index')->with('error', 'Sesi absensi sudah berakhir pada pukul ' . substr($jam_selesai, 0, 5) . ' WIB.'); + } + + if ($jadwal->status == 'selesai') { + return redirect()->route('presensi.anggota.index')->with('error', 'Sesi latihan sudah ditutup oleh pelatih.'); + } + + // Cek apakah sudah absen + $sudah_absen = Presensi::where('jadwal_id', $request->jadwal_id) + ->where('anggota_id', $profilAnggota->id) + ->exists(); + + if ($sudah_absen) { + return redirect()->route('presensi.anggota.index')->with('error', 'Kamu sudah melakukan absensi untuk jadwal ini!'); } - // Simpan data presensi dengan status is_verified = false (Menunggu ACC) Presensi::create([ - 'jadwal_id' => $request->jadwal_id, - 'anggota_id' => $user->id, - 'status' => 'Hadir', - 'waktu_presensi' => now(), - 'is_verified' => false, // Belum Sah, butuh ACC Pelatih - 'verified_by' => null + 'jadwal_id' => $request->jadwal_id, + 'anggota_id' => $profilAnggota->id, + 'status' => 'Hadir', + 'waktu_presensi' => $now, + 'is_verified' => false, + 'verified_by' => null, ]); - return redirect()->back()->with('success', 'Berhasil! Kehadiranmu sedang menunggu konfirmasi Pelatih.'); + return redirect()->route('presensi.anggota.index')->with('success', 'Berhasil scan! Silakan minta Pelatih untuk konfirmasi kehadiran.'); + } + + /** + * AKTOR: ANGGOTA + * Menampilkan riwayat lengkap presensi dengan filter bulan dan tahun + */ + public function riwayatAnggota(Request $request) + { + if (!Auth::check()) { + return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu.'); + } + + $user = Auth::user(); + + if (!$user->anggota) { + return redirect()->route('presensi.index')->with('error', 'Profil anggota tidak ditemukan.'); + } + + // Tangkap request filter bulan & tahun (Default: Bulan & Tahun saat ini) + $bulan = $request->input('bulan', date('m')); + $tahun = $request->input('tahun', date('Y')); + + // Ambil semua jadwal sesuai filter bulan & tahun di Kolat anggota tersebut + $jadwal_riwayat = Jadwal::with('pelatih') + ->where('kolat_id', $user->anggota->kolat_id) + ->whereMonth('tanggal', $bulan) + ->whereYear('tanggal', $tahun) + ->orderBy('tanggal', 'desc') + ->orderBy('jam_mulai', 'desc') + ->get(); + + // Ambil data presensi anggota pada jadwal-jadwal tersebut + $presensi_saya = Presensi::where('anggota_id', $user->anggota->id) + ->whereIn('jadwal_id', $jadwal_riwayat->pluck('id')) + ->get() + ->keyBy('jadwal_id'); + + return view('presensi.anggota.riwayat', compact('jadwal_riwayat', 'presensi_saya', 'bulan', 'tahun')); } } diff --git a/manajemen_mp/app/Http/Controllers/backup.txt b/manajemen_mp/app/Http/Controllers/backup.txt new file mode 100644 index 0000000..7403213 --- /dev/null +++ b/manajemen_mp/app/Http/Controllers/backup.txt @@ -0,0 +1,125 @@ +// controller jadwal // +orderBy('tanggal', 'desc') + ->orderBy('jam_mulai', 'desc') + ->get(); + + return view('jadwal.jadwal', compact('data_jadwal')); + } + + public function konfirmasiSelesai($id) +{ + $jadwal = Jadwal::findOrFail($id); + + // Kita paksa ubah status ke 'selesai' + $jadwal->status = 'selesai'; + $jadwal->save(); + + return redirect()->route('jadwal.index') + ->with('success', 'Latihan ' . $jadwal->judul_kegiatan . ' telah dikonfirmasi selesai!'); +} + + + public function create() + { + $data_kolat = Kolat::all(); + $rolePelatih = Role::where('nama_role', 'Pelatih')->first(); + + // Ambil Anggota yang memiliki role 'Pelatih' saja (filter lewat relasi) + $data_pelatih = Anggota::whereHas('role', function($query) { + $query->where('nama_role', 'Pelatih'); + }) + ->where('status', 'Aktif') // <-- INI BATASAN TAMBAHANNYA + ->orderBy('nama_lengkap', 'asc') // (Bonus) Biar namanya urut abjad di form + ->get(); + + return view('jadwal.jadwalcreate', compact('data_kolat', 'data_pelatih')); + } + + + public function store(Request $request) + { + $request->validate([ + 'judul_kegiatan' => 'required|string|max:255', + 'kolat_id' => 'required|exists:kolat,id', + 'pelatih_id' => 'required|exists:anggotas,id', + 'jenis' => 'required|in:Rutin,Tambahan,Pengumuman', + 'tanggal' => 'required|date', + 'jam_mulai' => 'required', + 'lokasi' => 'required|string', + ]); + + Jadwal::create($request->all()); + + return redirect()->route('jadwal.index') + ->with('success', 'Jadwal latihan berhasil ditambahkan!'); + } + + public function show($id) +{ + // Ambil satu data dengan relasinya + $jadwal = Jadwal::with(['kolat', 'pelatih'])->findOrFail($id); + + return view('jadwal.show', compact('jadwal')); +} + + public function edit($id) + { + $jadwal = Jadwal::findOrFail($id); + $data_kolat = Kolat::all(); + + // Tetap filter agar hanya muncul daftar pelatih + $data_pelatih = Anggota::whereHas('role', function($query) { + $query->where('nama_role', 'Pelatih'); + })->get(); + + return view('jadwal.editjadwal', compact('jadwal', 'data_kolat', 'data_pelatih')); + } + + public function update(Request $request, $id) + { + $jadwal = Jadwal::findOrFail($id); + + $request->validate([ + 'judul_kegiatan' => 'required|string|max:255', + 'kolat_id' => 'required|exists:kolat,id', + 'pelatih_id' => 'required|exists:anggotas,id', + 'jenis' => 'required|in:Rutin,Tambahan,Pengumuman', + 'tanggal' => 'required|date', + 'jam_mulai' => 'required', + 'lokasi' => 'required|string', + ]); + + $jadwal->update($request->all()); + + return redirect()->route('jadwal.index') + ->with('success', 'Jadwal berhasil diperbarui!'); + } + + + public function destroy($id) + { + $jadwal = Jadwal::findOrFail($id); + $jadwal->delete(); + + return redirect()->route('jadwal.index') + ->with('success', 'Jadwal telah dihapus dari sistem.'); + } + +} + diff --git a/manajemen_mp/app/Models/Jadwal.php b/manajemen_mp/app/Models/Jadwal.php index c80bd7e..89b0c39 100644 --- a/manajemen_mp/app/Models/Jadwal.php +++ b/manajemen_mp/app/Models/Jadwal.php @@ -19,7 +19,9 @@ class Jadwal extends Model 'jam_mulai', 'jam_selesai', 'lokasi', - 'deskripsi' + 'deskripsi', + 'foto_bukti', // Tambahan baru untuk foto kegiatan (Pelatih) + 'catatan_latihan', ]; // Relasi: Jadwal ini milik Kolat mana? diff --git a/manajemen_mp/app/Models/Presensi.php b/manajemen_mp/app/Models/Presensi.php index 658cd9f..a47988d 100644 --- a/manajemen_mp/app/Models/Presensi.php +++ b/manajemen_mp/app/Models/Presensi.php @@ -14,8 +14,8 @@ class Presensi extends Model 'status', 'keterangan', 'waktu_presensi', - 'is_verified', - 'verified_by' + 'is_verified', + 'verified_by' ]; // Relasi: Presensi ini untuk jadwal yang mana? @@ -29,4 +29,9 @@ public function anggota() { return $this->belongsTo(Anggota::class, 'anggota_id'); } + + public function pelatihVerifikator() + { + return $this->belongsTo(Anggota::class, 'verified_by'); + } } diff --git a/manajemen_mp/app/Models/User.php b/manajemen_mp/app/Models/User.php index 598e32a..d685df1 100644 --- a/manajemen_mp/app/Models/User.php +++ b/manajemen_mp/app/Models/User.php @@ -54,4 +54,5 @@ public function role() { return $this->belongsTo(Role::class, 'role_id'); } + } diff --git a/manajemen_mp/composer.json b/manajemen_mp/composer.json index 8a3d72d..87a63f9 100644 --- a/manajemen_mp/composer.json +++ b/manajemen_mp/composer.json @@ -9,7 +9,8 @@ "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.10", "laravel/sanctum": "^3.3", - "laravel/tinker": "^2.8" + "laravel/tinker": "^2.8", + "simplesoftwareio/simple-qrcode": "^4.2" }, "require-dev": { "fakerphp/faker": "^1.9.1", diff --git a/manajemen_mp/composer.lock b/manajemen_mp/composer.lock index 4a878b9..4a99d85 100644 --- a/manajemen_mp/composer.lock +++ b/manajemen_mp/composer.lock @@ -4,8 +4,62 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9c491b8531eec05ba41a11d9276a5749", + "content-hash": "d0e9a868dabf1fd8df971928d0612783", "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.1", + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" + }, + "time": "2022-12-07T17:46:57+00:00" + }, { "name": "brick/math", "version": "0.12.3", @@ -135,6 +189,56 @@ ], "time": "2023-12-11T17:09:12+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -3159,6 +3263,74 @@ }, "time": "2025-12-14T04:43:48+00:00" }, + { + "name": "simplesoftwareio/simple-qrcode", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/SimpleSoftwareIO/simple-qrcode.git", + "reference": "916db7948ca6772d54bb617259c768c9cdc8d537" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SimpleSoftwareIO/simple-qrcode/zipball/916db7948ca6772d54bb617259c768c9cdc8d537", + "reference": "916db7948ca6772d54bb617259c768c9cdc8d537", + "shasum": "" + }, + "require": { + "bacon/bacon-qr-code": "^2.0", + "ext-gd": "*", + "php": ">=7.2|^8.0" + }, + "require-dev": { + "mockery/mockery": "~1", + "phpunit/phpunit": "~9" + }, + "suggest": { + "ext-imagick": "Allows the generation of PNG QrCodes.", + "illuminate/support": "Allows for use within Laravel." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "QrCode": "SimpleSoftwareIO\\QrCode\\Facades\\QrCode" + }, + "providers": [ + "SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SimpleSoftwareIO\\QrCode\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Simple Software LLC", + "email": "support@simplesoftware.io" + } + ], + "description": "Simple QrCode is a QR code generator made for Laravel.", + "homepage": "https://www.simplesoftware.io/#/docs/simple-qrcode", + "keywords": [ + "Simple", + "generator", + "laravel", + "qrcode", + "wrapper" + ], + "support": { + "issues": "https://github.com/SimpleSoftwareIO/simple-qrcode/issues", + "source": "https://github.com/SimpleSoftwareIO/simple-qrcode/tree/4.2.0" + }, + "time": "2021-02-08T20:43:55+00:00" + }, { "name": "symfony/console", "version": "v6.4.32", diff --git a/manajemen_mp/config/app.php b/manajemen_mp/config/app.php index 3556121..7f9de8c 100644 --- a/manajemen_mp/config/app.php +++ b/manajemen_mp/config/app.php @@ -183,6 +183,7 @@ 'aliases' => Facade::defaultAliases()->merge([ // 'Example' => App\Facades\Example::class, + 'QrCode' => SimpleSoftwareIO\QrCode\Facades\QrCode::class, ])->toArray(), ]; diff --git a/manajemen_mp/database/seeders/DatabaseSeeder.php b/manajemen_mp/database/seeders/DatabaseSeeder.php index 24fd953..2d51fff 100644 --- a/manajemen_mp/database/seeders/DatabaseSeeder.php +++ b/manajemen_mp/database/seeders/DatabaseSeeder.php @@ -14,15 +14,8 @@ public function run(): void { $this->call([ RoleSeeder::class, - ]); - // \App\Models\User::factory(10)->create(); - - // \App\Models\User::factory()->create([ - // 'name' => 'Test User', - // 'email' => 'test@example.com', - // ]); - $this->call([ - RoleSeeder::class, + KolatSeeder::class, + UserSeeder::class, ]); } } diff --git a/manajemen_mp/database/seeders/KolatSeeder.php b/manajemen_mp/database/seeders/KolatSeeder.php new file mode 100644 index 0000000..6a41931 --- /dev/null +++ b/manajemen_mp/database/seeders/KolatSeeder.php @@ -0,0 +1,23 @@ +insert([ + 'id' => 1, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi + 'nama_kolat' => 'Kolat Polije', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } +} diff --git a/manajemen_mp/database/seeders/UserSeeder.php b/manajemen_mp/database/seeders/UserSeeder.php new file mode 100644 index 0000000..ea23dff --- /dev/null +++ b/manajemen_mp/database/seeders/UserSeeder.php @@ -0,0 +1,47 @@ +first(); + + // 2. Buat Akun User untuk Pelatih + $user = User::create([ + 'role_id' => $rolePelatih->id, + 'email' => 'pelatih@tapakmp.com', + 'password' => Hash::make('password123'), // Password default + ]); + + // 3. Buat Profil Anggota untuk User tersebut + Anggota::create([ + 'user_id' => $user->id, + 'role_id' => $rolePelatih->id, + 'no_induk' => 'MP-2026-001', + 'nama_lengkap' => 'Siti Afiah Dahliawati', + 'jenis_kelamin' => 'P', + 'tempat_lahir' => 'Jember', + 'tgl_lahir' => '2004-10-29', + 'no_hp' => '081455057453', + 'kolat_id' => 1, // Pastikan sudah ada data Kolat ID 1 atau sesuaikan + 'tingkatan' => 'Balik 1', + 'tgl_gabung' => now(), + 'status' => 'Aktif', + 'alamat' => 'Jl. Merpati Putih No. 1, Jember', + 'jabatan' => 'Pelatih Kolat', + ]); + + } +} diff --git a/manajemen_mp/resources/views/anggota/editanggota.blade.php b/manajemen_mp/resources/views/anggota/editanggota.blade.php index 50448e5..8eac963 100644 --- a/manajemen_mp/resources/views/anggota/editanggota.blade.php +++ b/manajemen_mp/resources/views/anggota/editanggota.blade.php @@ -1,395 +1,156 @@ @extends('layout.app') @section('content') -
-<<<<<<< Updated upstream - -======= ->>>>>>> Stashed changes - +
+ - + @endsection diff --git a/manajemen_mp/resources/views/jadwal/backup.blade.php b/manajemen_mp/resources/views/jadwal/backup.blade.php new file mode 100644 index 0000000..12d0e82 --- /dev/null +++ b/manajemen_mp/resources/views/jadwal/backup.blade.php @@ -0,0 +1,547 @@ +// index jadwal.blade.php +@extends('layout.app') + +@section('content') +
+ + + @if(session('success')) + + @endif + +
+
+
+
+ +
+
+
Agenda PPS Merpati Putih
+

Cabang Jember - Kelola jadwal rutin dan pengumuman latihan.

+
+ + Buat Jadwal + +
+ +
+ + + + + + + + + + + + + @forelse ($data_jadwal as $no => $item) + @php + // Set timezone ke Jakarta agar sinkron dengan waktu Indonesia + $now = \Carbon\Carbon::now('Asia/Jakarta'); + $start = \Carbon\Carbon::parse($item->tanggal . ' ' . $item->jam_mulai, 'Asia/Jakarta'); + $end = $item->jam_selesai + ? \Carbon\Carbon::parse($item->tanggal . ' ' . $item->jam_selesai, 'Asia/Jakarta') + : $start->copy()->addHours(2); + + $dbStatus = trim(strtolower($item->status)); + + // Logika Penentuan Label Status + if ($dbStatus == 'selesai') { + $status_label = 'Selesai'; + $status_color = 'success'; + } elseif ($now->greaterThanOrEqualTo($start) && $now->lessThanOrEqualTo($end)) { + $status_label = 'Berlangsung'; + $status_color = 'primary'; + } elseif ($now->lt($start)) { + $status_label = 'Mendatang'; + $status_color = 'secondary'; + } else { + $status_label = 'Menunggu Konfirmasi'; + $status_color = 'warning'; + } + @endphp + + + + + + + + + + {{-- MODAL DETAIL JADWAL --}} + + @empty + + + + @endforelse + +
NoKegiatan & PelatihStatusWaktu & LokasiTipeAksi
{{ $no + 1 }} +
+ {{ $item->judul_kegiatan }} + @if($dbStatus == 'selesai') + + @endif +
+ + {{ $item->pelatih->nama_lengkap ?? 'Belum Ditentukan' }} + +
+ + {{ $status_label }} + + +
+ {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
+
+ {{ substr($item->jam_mulai, 0, 5) }} - {{ $item->jam_selesai ? substr($item->jam_selesai, 0, 5) : 'Selesai' }} +
+
+ {{ $item->lokasi }} +
+
+ @php + $bg_type = 'bg-secondary-soft text-secondary'; + if($item->jenis == 'Rutin') $bg_type = 'bg-success-soft text-success border-success'; + elseif($item->jenis == 'Tambahan') $bg_type = 'bg-info-soft text-info border-info'; + elseif($item->jenis == 'Pengumuman') $bg_type = 'bg-danger-soft text-danger border-danger'; + @endphp + + {{ $item->jenis }} + + +
+ + {{-- TOMBOL KONFIRMASI: Muncul jika sudah masuk jamnya, bukan pengumuman, dan belum selesai --}} + @if($dbStatus != 'selesai' && $item->jenis != 'Pengumuman' && $now->greaterThanOrEqualTo($start)) +
+ @csrf + +
+ @endif + + + + + + +
+
Belum ada jadwal latihan atau pengumuman.
+
+ +
+
+
+
+
+ + + + +@endsection + + +{{-- edit --}} +@extends('layout.app') + +@section('content') +
+ + +
+
+
+ @csrf + @method('PUT') + +
+
+
Informasi Agenda
+
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+
+ +
+
+ @csrf + @method('DELETE') + +
+
+
+
+
+ + +@endsection + +{{-- create --}} +@extends('layout.app') + +@section('content') +
+ + +
+
+
+
+
+ @csrf + +
+
+ + + @error('judul_kegiatan') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('kolat_id') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('pelatih_id') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('jenis') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('tanggal') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('jam_mulai') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('jam_selesai') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('lokasi') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('deskripsi') +
{{ $message }}
+ @enderror +
+
+ +
+ Batal + +
+
+
+
+
+ +
+
+
+
Tips Pengisian
+
    +
  • Pastikan memilih Pelatih yang sedang aktif.
  • +
  • Pilih jenis Pengumuman jika hanya ingin memberikan informasi tanpa perlu presensi.
  • +
  • Gunakan lokasi yang spesifik agar anggota tidak kesulitan mencari tempat.
  • +
+
+
+
+
+
+ + +@endsection + + diff --git a/manajemen_mp/resources/views/jadwal/editjadwal.blade.php b/manajemen_mp/resources/views/jadwal/editjadwal.blade.php index 95ff4b1..c70fe30 100644 --- a/manajemen_mp/resources/views/jadwal/editjadwal.blade.php +++ b/manajemen_mp/resources/views/jadwal/editjadwal.blade.php @@ -22,21 +22,26 @@ class="text-muted text-decoration-none small">Daftar Jadwal
-
Informasi Agenda
+
Informasi Agenda +
- +
- + - @foreach($data_pelatih as $pelatih) - + @foreach ($data_pelatih as $pelatih) + @endforeach @@ -55,12 +63,12 @@ class="text-muted text-decoration-none small">Daftar Jadwal
- +
- +
@@ -71,23 +79,29 @@ class="text-muted text-decoration-none small">Daftar Jadwal
- + + value="{{ old('lokasi', $jadwal->lokasi) }}" placeholder="Contoh: GOR Perjuangan" + required>
- +
- +
@@ -95,10 +109,18 @@ class="text-muted text-decoration-none small">Daftar Jadwal @endsection diff --git a/manajemen_mp/resources/views/jadwal/jadwal.blade.php b/manajemen_mp/resources/views/jadwal/jadwal.blade.php index 18150c5..7e46b15 100644 --- a/manajemen_mp/resources/views/jadwal/jadwal.blade.php +++ b/manajemen_mp/resources/views/jadwal/jadwal.blade.php @@ -29,12 +29,12 @@
Agenda PPS Merpati Putih

Cabang Jember - Kelola jadwal rutin dan pengumuman latihan.

- - Buat Jadwal + + Buat Jadwal + {{-- TABEL DATA JADWAL --}}
@@ -50,7 +50,6 @@ @forelse ($data_jadwal as $no => $item) @php - // Set timezone ke Jakarta agar sinkron dengan waktu Indonesia $now = \Carbon\Carbon::now('Asia/Jakarta'); $start = \Carbon\Carbon::parse($item->tanggal . ' ' . $item->jam_mulai, 'Asia/Jakarta'); $end = $item->jam_selesai @@ -59,7 +58,6 @@ $dbStatus = trim(strtolower($item->status)); - // Logika Penentuan Label Status if ($dbStatus == 'selesai') { $status_label = 'Selesai'; $status_color = 'success'; @@ -84,7 +82,7 @@ @endif - {{ $item->pelatih->nama_lengkap ?? 'Belum Ditentukan' }} + {{ $item->pelatih->nama_lengkap ?? 'Belum Ditentukan' }} - - {{-- MODAL DETAIL JADWAL --}} - @empty @@ -205,6 +154,183 @@ class="btn btn-sm btn-action-edit fw-bold px-2 shadow-xs"> + {{-- KUMPULAN MODAL DITEMPATKAN DI LUAR TABLE --}} + @foreach ($data_jadwal as $item) + @php + // Perlu inisialisasi status lagi untuk tampilan dalam Modal + $now = \Carbon\Carbon::now('Asia/Jakarta'); + $start = \Carbon\Carbon::parse($item->tanggal . ' ' . $item->jam_mulai, 'Asia/Jakarta'); + $end = $item->jam_selesai + ? \Carbon\Carbon::parse($item->tanggal . ' ' . $item->jam_selesai, 'Asia/Jakarta') + : $start->copy()->addHours(2); + $dbStatus = trim(strtolower($item->status)); + + if ($dbStatus == 'selesai') { + $status_label = 'Selesai'; + $status_color = 'success'; + } elseif ($now->greaterThanOrEqualTo($start) && $now->lessThanOrEqualTo($end)) { + $status_label = 'Berlangsung'; + $status_color = 'primary'; + } elseif ($now->lt($start)) { + $status_label = 'Mendatang'; + $status_color = 'secondary'; + } else { + $status_label = 'Menunggu Konfirmasi'; + $status_color = 'warning'; + } + @endphp + + {{-- MODAL KONFIRMASI SELESAI --}} +
@@ -94,13 +92,13 @@
- {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} + {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }}
- {{ substr($item->jam_mulai, 0, 5) }} - {{ $item->jam_selesai ? substr($item->jam_selesai, 0, 5) : 'Selesai' }} + {{ substr($item->jam_mulai, 0, 5) }} - {{ $item->jam_selesai ? substr($item->jam_selesai, 0, 5) : 'Selesai' }}
- {{ $item->lokasi }} + {{ $item->lokasi }}
@@ -117,79 +115,30 @@
- {{-- TOMBOL KONFIRMASI: Muncul jika sudah masuk jamnya, bukan pengumuman, dan belum selesai --}} @if($dbStatus != 'selesai' && $item->jenis != 'Pengumuman' && $now->greaterThanOrEqualTo($start)) -
- @csrf - -
+ @endif - - - + + + +
+ @csrf + @method('DELETE') + +
Belum ada jadwal latihan atau pengumuman.
+ + + + + + + + + @php + $semua_absen = $item->presensi ? $item->presensi->where('is_verified', true) : collect([]); + $total_hadir = $semua_absen->where('status', 'Hadir')->count(); + @endphp + @forelse($semua_absen as $idx => $absen) + + + + + + @empty + + + + @endforelse + +
NoNama AnggotaStatus
{{ $idx + 1 }}{{ $absen->anggota->nama_lengkap ?? 'Tidak diketahui' }} + @php + $bgBadge = 'bg-secondary'; + if($absen->status == 'Hadir') $bgBadge = 'bg-success'; + elseif($absen->status == 'Izin') $bgBadge = 'bg-info'; + elseif($absen->status == 'Sakit') $bgBadge = 'bg-warning text-dark'; + elseif($absen->status == 'Alfa') $bgBadge = 'bg-danger'; + @endphp + {{ $absen->status }} +
Belum ada data presensi yang diverifikasi.
+
+
+ Total Hadir: {{ $total_hadir }} Orang +
+ + +
+ + + Upload foto bersama sebagai bukti latihan telah dilaksanakan. +
+ +
+ + +
+ + + + + + + + {{-- MODAL DETAIL JADWAL --}} + + @endforeach + + @endsection diff --git a/manajemen_mp/resources/views/jadwal/show.blade.php b/manajemen_mp/resources/views/jadwal/show.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/manajemen_mp/resources/views/layout/navbar.blade.php b/manajemen_mp/resources/views/layout/navbar.blade.php index 178e5be..a0536e0 100644 --- a/manajemen_mp/resources/views/layout/navbar.blade.php +++ b/manajemen_mp/resources/views/layout/navbar.blade.php @@ -212,10 +212,15 @@
  • - - - Sign Out - + + + Sign Out + + +
    + @csrf +
  • diff --git a/manajemen_mp/resources/views/layout/sidebar.blade.php b/manajemen_mp/resources/views/layout/sidebar.blade.php index e7b5bc3..0d69d03 100644 --- a/manajemen_mp/resources/views/layout/sidebar.blade.php +++ b/manajemen_mp/resources/views/layout/sidebar.blade.php @@ -1,6 +1,12 @@ +@php + // Ambil nama role user yang login saat ini + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; +@endphp + -{{-- SCRIPT PENTING: Supaya menu yang otomatis terbuka BISA DITUTUP KEMBALI --}} + + + +@endsection diff --git a/manajemen_mp/resources/views/presensi/pengurus/index.blade.php b/manajemen_mp/resources/views/presensi/pengurus/index.blade.php index 2d4f9d5..f1d3189 100644 --- a/manajemen_mp/resources/views/presensi/pengurus/index.blade.php +++ b/manajemen_mp/resources/views/presensi/pengurus/index.blade.php @@ -12,8 +12,9 @@

    Rekap Presensi Latihan

    - @if(session('success')) - @endsection diff --git a/manajemen_mp/resources/views/presensi/pengurus/kehadiran.blade.php b/manajemen_mp/resources/views/presensi/pengurus/kehadiran.blade.php index 990fa81..e37d86a 100644 --- a/manajemen_mp/resources/views/presensi/pengurus/kehadiran.blade.php +++ b/manajemen_mp/resources/views/presensi/pengurus/kehadiran.blade.php @@ -2,6 +2,7 @@ @section('content')
    + {{-- HEADER BREADCRUMB --}} - {{-- Menampilkan Pesan Sukses --}} - @if(session('success')) + {{-- NOTIFIKASI --}} + @if (session('success')) @endif - {{-- Menampilkan Pesan Error (Misal: Input manual ganda) --}} - @if(session('error')) + @if (session('error')) @endif + @php + $now = \Carbon\Carbon::now('Asia/Jakarta'); + $start = \Carbon\Carbon::parse($jadwal->tanggal . ' ' . $jadwal->jam_mulai, 'Asia/Jakarta'); + $end = $jadwal->jam_selesai + ? \Carbon\Carbon::parse($jadwal->tanggal . ' ' . $jadwal->jam_selesai, 'Asia/Jakarta') + : $start->copy()->addHours(3); + + $is_active = $now->between($start, $end); + @endphp + {{-- KARTU INFORMASI JADWAL --}}
    -
    -
    {{ $jadwal->judul_kegiatan }}
    +
    + + Kolat: {{ $jadwal->kolat->nama_kolat ?? '-' }} + +

    {{ $jadwal->judul_kegiatan }}

    - {{ $jadwal->lokasi }} | - {{ \Carbon\Carbon::parse($jadwal->tanggal)->format('d M Y') }} | - {{ substr($jadwal->jam_mulai, 0, 5) }} - {{ $jadwal->jam_selesai ? substr($jadwal->jam_selesai, 0, 5) : 'Selesai' }} + {{ $jadwal->lokasi }}  |  + {{ \Carbon\Carbon::parse($jadwal->tanggal)->translatedFormat('d F Y') }}  |  + {{ substr($jadwal->jam_mulai, 0, 5) }} - {{ $jadwal->jam_selesai ? substr($jadwal->jam_selesai, 0, 5) : 'Selesai' }} WIB

    -
    - - Kolat: {{ $jadwal->kolat->nama_kolat ?? '-' }} - +
    + @if ($is_active && $jadwal->status != 'selesai') + + @else + + @endif
    @@ -54,33 +73,31 @@
    - {{-- TABEL PRESENSI --}} -
    + {{-- TABEL DAFTAR ABSENSI MASUK --}} +
    -
    -
    Daftar Absensi Masuk
    -

    Klik konfirmasi untuk mensahkan kehadiran anggota.

    +
    Daftar Absensi Masuk
    +

    Verifikasi kehadiran anggota yang sudah melakukan absensi.

    - - {{-- Tombol Buka Modal Tambah Manual --}} -
    - +
    - - - - - + + + + + + @@ -88,106 +105,259 @@ + @empty - + @endforelse
    NoIdentitas AnggotaWaktu AbsenStatus VerifikasiAksiNoIdentitas AnggotaWaktu AbsenStatus KehadiranVerifikasiAksi
    {{ $no + 1 }} -
    {{ $item->anggota->nama_lengkap }}
    - ID: {{ $item->anggota->no_induk }} +
    {{ $item->anggota->nama_lengkap }}
    + {{ $item->anggota->no_induk }}
    - - {{ \Carbon\Carbon::parse($item->waktu_presensi)->format('H:i') }} WIB + + {{ \Carbon\Carbon::parse($item->waktu_presensi)->format('H:i') }} WIB - @if($item->is_verified) - - Sah - -
    - Oleh: {{ $item->pelatihVerifikator->nama_lengkap ?? 'Pelatih' }} + @php + $statusColor = match($item->status) { + 'Hadir' => 'success', + 'Izin' => 'info', + 'Sakit' => 'warning', + 'Alfa' => 'danger', + default => 'secondary' + }; + @endphp + + {{ $item->status }} + + + @if($item->keterangan) +
    + {{ $item->keterangan }}
    - @else - - Menunggu - @endif
    - @if(!$item->is_verified) - {{-- Tombol Konfirmasi Hadir --}} -
    - @csrf - -
    + @if ($item->is_verified) + Sah +
    + Oleh: {{ $item->pelatihVerifikator->nama_lengkap ?? 'Sistem' }} +
    @else - + Menunggu + @endif +
    + @if (!$item->is_verified) +
    +
    + @csrf + + +
    + +
    + + {{-- Modal Khusus Penolakan --}} + + @else + + Selesai + @endif
    Belum ada anggota yang melakukan absensi masuk. + No Data +
    Belum ada absensi masuk
    +

    Anggota yang men-scan barcode akan muncul di sini.

    +
    +
    +
    +
    +
    + {{-- TABEL DAFTAR ANGGOTA BELUM ABSEN --}} +
    +
    +
    +
    +
    +
    +
    + Belum Melakukan Presensi + @if(isset($anggota_belum_absen) && $anggota_belum_absen->count() > 0) + {{ $anggota_belum_absen->count() }} Orang + @endif +
    +

    Daftar anggota aktif di kolat ini yang belum tercatat kehadirannya.

    +
    +
    + +
    + + + + + + + + + + + @if(isset($anggota_belum_absen)) + @forelse ($anggota_belum_absen as $no => $belum) + + + + + + + @empty + + + + @endforelse + @endif + +
    NoIdentitas AnggotaNo. HP (WhatsApp)Aksi Cepat
    {{ $no + 1 }} +
    {{ $belum->nama_lengkap }}
    + {{ $belum->no_induk }} +
    + @if($belum->no_hp) + + Hubungi + + @else + - + @endif + + +
    +

    Mantap! Semua anggota aktif sudah tercatat kehadirannya.
    +
    +
    - {{-- MODAL TAMBAH MANUAL (KASUS LUPA HP) --}} + {{-- MODAL QR CODE --}} + + + {{-- MODAL TAMBAH MANUAL --}}