diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index b6a3016..fdbf565 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -64,7 +64,8 @@ public function sendOtp(Request $request) 'email' => 'required|email|exists:users,email', ]); - $user = User::where('email', $request->email)->first(); + // ✅ FIX: tambah with('guru') agar relasi ter-load + $user = User::with('guru')->where('email', $request->email)->first(); } else { $request->validate([ @@ -76,7 +77,8 @@ public function sendOtp(Request $request) $no = '62' . substr($no, 1); } - $user = User::whereHas('guru', function ($q) use ($no) { + // ✅ FIX: tambah with('guru') agar relasi ter-load + $user = User::with('guru')->whereHas('guru', function ($q) use ($no) { $q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$no}%"]); })->orWhereHas('siswa', function ($q) use ($no) { $q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$no}%"]); @@ -87,6 +89,11 @@ public function sendOtp(Request $request) } } + // ✅ FIX: blokir pengiriman OTP untuk guru nonaktif + if ($user->role === 'guru' && $user->guru && $user->guru->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + // Kirim OTP sesuai method — email SAJA atau WA SAJA app(\App\Services\OtpService::class)->send($user, $method); @@ -107,7 +114,8 @@ public function loginOtp(Request $request) 'otp' => 'nullable|digits:6', ]); - $user = User::where('email', $request->email)->first(); + // ✅ FIX: tambah with('guru') agar relasi ter-load + $user = User::with('guru')->where('email', $request->email)->first(); if (!$user) { return back()->with('error', 'Email tidak ditemukan.'); @@ -134,6 +142,14 @@ public function loginOtp(Request $request) // ================================================= if (in_array($user->role, ['guru', 'siswa'])) { + // ✅ FIX: cek status nonaktif sebelum proses OTP + if ($user->role === 'guru') { + $user->load('guru'); // reload fresh dari DB + if (!$user->guru || $user->guru->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + } + if (empty($request->otp)) { return back()->with('error', 'OTP wajib diisi.'); } @@ -227,34 +243,35 @@ public function logout(Request $request) } // ================= VERIFY OTP (halaman terpisah jika ada) ================= - // ================= VERIFY OTP (lupa password) ================= -public function showVerifyOtp() -{ - return view('auth.verify-otp'); -} + public function showVerifyOtp() + { + return view('auth.verify-otp'); + } -public function verifyOtp(Request $request) + public function verifyOtp(Request $request) { - $user = User::find(session('otp_user')); + $request->validate([ + 'otp' => 'required|digits:6' + ]); + + $user = User::where('email', session('email'))->first(); if (!$user) { - return back()->with('error', 'Sesi tidak valid, ulangi dari awal.'); + return back()->with('error', 'User tidak ditemukan'); } - // Cek OTP dari kolom database, bukan session + // cek OTP if ((string)$request->otp !== (string)$user->otp) { - return back()->with('error', 'Kode OTP salah.'); + return back()->with('error', 'OTP salah'); } + // cek expired if (!$user->otp_expired_at || now()->gt($user->otp_expired_at)) { - return back()->with('error', 'OTP sudah kadaluarsa, kirim ulang.'); + return back()->with('error', 'OTP sudah kadaluarsa'); } - // OTP valid — hapus OTP, redirect ke reset password - $user->update([ - 'otp' => null, - 'otp_expired_at' => null, - ]); + // simpan user reset password + session(['otp_user' => $user->id]); return redirect()->route('reset.password.form'); } @@ -314,29 +331,36 @@ private function redirectByRole($user) }; } - // ================= KIRIM OTP LUPA PASSWORD ================= -// ================= KIRIM OTP LUPA PASSWORD ================= -public function sendForgotPasswordOtp(Request $request) + // ================= FORGOT PASSWORD OTP ================= + public function sendForgotPasswordOtp(Request $request) { $request->validate([ - 'email' => 'required|email|exists:users,email', - ], [ - 'email.exists' => 'Email tidak terdaftar dalam sistem.', + 'email' => 'required|email' ]); $user = User::where('email', $request->email)->first(); - // Kirim OTP via email menggunakan OtpService yang sudah ada - app(\App\Services\OtpService::class)->send($user, 'email'); + if (!$user) { + return back()->with('error', 'Email tidak ditemukan'); + } - // Simpan ke session untuk dipakai saat verifikasi & reset - session([ - 'otp_user' => $user->id, - 'email' => $user->email, + $otp = rand(100000, 999999); + + $user->update([ + 'otp' => $otp, + 'otp_expired_at' => now()->addMinutes(5), ]); - // Redirect ke halaman verifikasi OTP - return redirect()->route('verify.otp') - ->with('success', 'OTP telah dikirim ke ' . $user->email); + Mail::raw("Kode OTP reset password Anda adalah: $otp", function ($message) use ($user) { + $message->to($user->email) + ->subject('OTP Reset Password'); + }); + + session([ + 'email' => $user->email + ]); + + return redirect()->route('verify.otp.form') + ->with('success', 'OTP reset password berhasil dikirim'); } } \ No newline at end of file diff --git a/app/Http/Controllers/auth/OtpController.php b/app/Http/Controllers/auth/OtpController.php index b453b7a..7560611 100644 --- a/app/Http/Controllers/auth/OtpController.php +++ b/app/Http/Controllers/auth/OtpController.php @@ -14,15 +14,13 @@ public function sendOtp(Request $request) $method = $request->input('method', 'email'); if ($method === 'email') { - // ── EMAIL ── $request->validate([ 'email' => 'required|email|exists:users,email', ]); - $user = User::where('email', $request->email)->first(); + $user = User::with(['guru', 'siswa'])->where('email', $request->email)->first(); } else { - // ── WHATSAPP ── $request->validate([ 'telepon' => 'required|string', ]); @@ -33,7 +31,7 @@ public function sendOtp(Request $request) } $no08 = '0' . substr($no, 2); - $user = User::whereHas('guru', function ($q) use ($no, $no08) { + $user = User::with(['guru', 'siswa'])->whereHas('guru', function ($q) use ($no, $no08) { $q->where('telepon', $no) ->orWhere('telepon', $no08) ->orWhere('telepon', 'like', '%' . substr($no, -9) . '%'); @@ -48,13 +46,21 @@ public function sendOtp(Request $request) } } - // Kirim OTP sesuai method (email SAJA atau WA SAJA) + // ✅ FIX: blokir guru nonaktif + if ($user->role === 'guru' && $user->guru && $user->guru->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + + // ✅ FIX BARU: blokir siswa nonaktif + if ($user->role === 'siswa' && $user->siswa && $user->siswa->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + app(\App\Services\OtpService::class)->send($user, $method); - // Simpan email ke session untuk prefill form login session(['email' => $user->email]); - return back()->with('otp_sent', 'OTP berhasil dikirim!'); + return back()->with('otp_sent', 'OTP berhasil dikirim!'); } // ================= LOGIN DENGAN OTP ================= @@ -66,7 +72,7 @@ public function loginOtp(Request $request) 'otp' => 'nullable|digits:6', ]); - $user = User::where('email', $request->email)->first(); + $user = User::with(['guru', 'siswa'])->where('email', $request->email)->first(); if (!$user) { return back()->with('error', 'User tidak ditemukan'); @@ -76,6 +82,22 @@ public function loginOtp(Request $request) return back()->with('error', 'Password salah'); } + // ✅ FIX: cek status nonaktif untuk guru + if ($user->role === 'guru') { + $user->load('guru'); + if (!$user->guru || $user->guru->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + } + + // ✅ FIX BARU: cek status nonaktif untuk siswa + if ($user->role === 'siswa') { + $user->load('siswa'); + if (!$user->siswa || $user->siswa->status === 'nonaktif') { + return back()->with('error', 'Akun Anda telah dinonaktifkan. Hubungi administrator.'); + } + } + // Guru & Siswa wajib OTP if (in_array($user->role, ['guru', 'siswa'])) { @@ -99,7 +121,6 @@ public function loginOtp(Request $request) \Auth::login($user); $request->session()->regenerate(); - // Hapus OTP setelah dipakai if (in_array($user->role, ['guru', 'siswa'])) { $user->update(['otp' => null, 'otp_expired_at' => null]); } diff --git a/app/Http/Controllers/guru/TugasController.php b/app/Http/Controllers/guru/TugasController.php index 1f0ada4..cd5a0c8 100644 --- a/app/Http/Controllers/guru/TugasController.php +++ b/app/Http/Controllers/guru/TugasController.php @@ -88,7 +88,7 @@ public function store(Request $request) 'kelas_id' => 'required', 'judul' => 'required', 'deadline' => 'required', - 'file' => 'nullable|mimes:pdf,doc,docx,ppt,pptx|max:2048', + 'file' => 'nullable|max:10240', ]); $filePath = null; @@ -139,27 +139,42 @@ public function edit($id) return view('guru.tugas.edit', compact('tugas', 'kelas')); } - public function update(Request $request, $id) - { - $request->validate([ - 'judul' => 'required', - 'kelas_id' => 'required', - 'deadline' => 'required', - ]); + // SESUDAH +public function update(Request $request, $id) +{ + $request->validate([ + 'judul' => 'required', + 'kelas_id' => 'required', + 'deadline' => 'required', + 'file' => 'nullable|max:10240', // ✅ validasi file + ], [ + 'file.max' => 'Ukuran file maksimal 10MB.', + ]); - $tugas = Tugas::findOrFail($id); + $tugas = Tugas::findOrFail($id); - $tugas->update([ - 'judul' => $request->judul, - 'kelas_id' => $request->kelas_id, - 'deskripsi' => $request->deskripsi, - 'deadline' => $request->deadline, - ]); + $updateData = [ + 'judul' => $request->judul, + 'kelas_id' => $request->kelas_id, + 'deskripsi' => $request->deskripsi, + 'deadline' => $request->deadline, + ]; - return redirect()->route('guru.tugas.index') - ->with('success', 'Tugas berhasil diupdate'); + // ✅ Jika ada file baru diupload + if ($request->hasFile('file')) { + // Hapus file lama jika ada + if ($tugas->file && Storage::disk('public')->exists($tugas->file)) { + Storage::disk('public')->delete($tugas->file); + } + // Simpan file baru + $updateData['file'] = $request->file('file')->store('tugas_file', 'public'); } + $tugas->update($updateData); + + return redirect()->route('guru.tugas.index') + ->with('success', 'Tugas berhasil diupdate'); +} public function download($id) { $tugas = Tugas::findOrFail($id); diff --git a/app/Http/Controllers/siswa/UjianController.php b/app/Http/Controllers/siswa/UjianController.php index 45adbfe..ebb8522 100644 --- a/app/Http/Controllers/siswa/UjianController.php +++ b/app/Http/Controllers/siswa/UjianController.php @@ -12,9 +12,8 @@ class UjianController extends Controller { public function index() { - $siswa = auth()->user()->siswa; + $siswa = auth()->user()->siswa; - // Hanya tampilkan ujian yang dikirim ke kelas siswa ini & status terkirim $ujians = Ujian::whereHas('kelas', fn($q) => $q->where('kelas.id', $siswa->kelas_id)) ->where('status_kirim', 'terkirim') ->latest() @@ -26,64 +25,89 @@ public function index() $hasilSiswa = Hasil::where('siswa_id', $siswa->id) ->get()->keyBy('ujian_id'); - return view('siswa.ujian.index', compact('ujians', 'sudahDikerjakan', 'hasilSiswa')); + // ✅ FIX BARU: tandai ujian yang sudah lewat waktu + $sudahBerakhir = $ujians + ->filter(fn($u) => $u->selesai && \Carbon\Carbon::parse($u->selesai)->lt(now())) + ->pluck('id') + ->toArray(); + + return view('siswa.ujian.index', compact('ujians', 'sudahDikerjakan', 'hasilSiswa', 'sudahBerakhir')); +} + public function kerjakan($id) +{ + $siswa = auth()->user()->siswa; + + $sudah = Hasil::where('siswa_id', $siswa->id) + ->where('ujian_id', $id) + ->exists(); + + if ($sudah) { + return redirect()->route('siswa.ujian.index') + ->with('warning', 'Kamu sudah mengerjakan ujian ini.'); + } + + $ujian = Ujian::findOrFail($id); + + // ✅ FIX: parse eksplisit dengan Carbon + $mulai = $ujian->mulai ? \Carbon\Carbon::parse($ujian->mulai) : null; + $selesai = $ujian->selesai ? \Carbon\Carbon::parse($ujian->selesai) : null; + + if ($mulai && now()->lt($mulai)) { + return redirect()->route('siswa.ujian.index') + ->with('warning', 'Ujian belum dimulai. Silakan tunggu waktu ujian.'); + } + + if ($selesai && now()->gt($selesai)) { + return redirect()->route('siswa.ujian.index') + ->with('warning', 'Waktu ujian sudah berakhir, kamu tidak dapat mengerjakan ujian ini.'); + } + + $soals = Soal::where('ujian_id', $id)->get(); + + return view('siswa.ujian.kerjakan', compact('ujian', 'soals')); } - public function kerjakan($id) - { - $siswa = auth()->user()->siswa; +public function submit(Request $request, $id) +{ + $siswa = auth()->user()->siswa; - $sudah = Hasil::where('siswa_id', $siswa->id) - ->where('ujian_id', $id) - ->exists(); + $sudah = Hasil::where('siswa_id', $siswa->id) + ->where('ujian_id', $id) + ->exists(); - if ($sudah) { - return redirect()->route('siswa.ujian.index') - ->with('warning', 'Kamu sudah mengerjakan ujian ini.'); - } - - $ujian = Ujian::findOrFail($id); - $soals = Soal::where('ujian_id', $id)->get(); - - return view('siswa.ujian.kerjakan', compact('ujian', 'soals')); + if ($sudah) { + return redirect()->route('siswa.ujian.index') + ->with('warning', 'Kamu sudah mengerjakan ujian ini.'); } - public function submit(Request $request, $id) - { - $siswa = auth()->user()->siswa; + $ujian = Ujian::findOrFail($id); + $selesai = $ujian->selesai ? \Carbon\Carbon::parse($ujian->selesai) : null; - // Cegah submit ulang - $sudah = Hasil::where('siswa_id', $siswa->id) - ->where('ujian_id', $id) - ->exists(); - - if ($sudah) { - return redirect()->route('siswa.ujian.index') - ->with('warning', 'Kamu sudah mengerjakan ujian ini.'); - } - - $soals = Soal::where('ujian_id', $id)->get(); - $ujian = Ujian::findOrFail($id); - - $benar = 0; - $total = $soals->count(); - - foreach ($soals as $s) { - $jawabanUser = $request->jawaban[$s->id] ?? null; - if ($jawabanUser == $s->jawaban_benar) { - $benar++; - } - } - - $nilai = round(($benar / $total) * 100); - - // Simpan ke tabel hasils (hanya kolom yang ada) - Hasil::create([ - 'siswa_id' => $siswa->id, - 'ujian_id' => $id, - 'nilai' => $nilai, - ]); - - return view('siswa.ujian.hasil', compact('nilai', 'benar', 'total', 'ujian')); + // ✅ FIX: Cegah submit jika waktu sudah habis + if ($selesai && now()->gt($selesai)) { + return redirect()->route('siswa.ujian.index') + ->with('warning', 'Waktu ujian sudah berakhir, jawaban tidak dapat dikumpulkan.'); } + + $soals = Soal::where('ujian_id', $id)->get(); + $benar = 0; + $total = $soals->count(); + + foreach ($soals as $s) { + $jawabanUser = $request->jawaban[$s->id] ?? null; + if ($jawabanUser == $s->jawaban_benar) { + $benar++; + } + } + + $nilai = round(($benar / $total) * 100); + + Hasil::create([ + 'siswa_id' => $siswa->id, + 'ujian_id' => $id, + 'nilai' => $nilai, + ]); + + return view('siswa.ujian.hasil', compact('nilai', 'benar', 'total', 'ujian')); +} } \ No newline at end of file diff --git a/public/uploads/1779301204.PNG b/public/uploads/1779301204.PNG new file mode 100644 index 0000000..c773722 Binary files /dev/null and b/public/uploads/1779301204.PNG differ diff --git a/public/uploads/1779301496.jpg b/public/uploads/1779301496.jpg new file mode 100644 index 0000000..1b44835 Binary files /dev/null and b/public/uploads/1779301496.jpg differ diff --git a/resources/views/admin/kelas/index.blade.php b/resources/views/admin/kelas/index.blade.php index bdf6cb5..9561a07 100644 --- a/resources/views/admin/kelas/index.blade.php +++ b/resources/views/admin/kelas/index.blade.php @@ -2,156 +2,345 @@ @section('content') - + .sw-btn-danger { + background: var(--danger-light); + color: var(--danger); + border: 1px solid #FECACA; + } -