parent
21f0229058
commit
7e3718f6a3
|
|
@ -64,7 +64,8 @@ public function sendOtp(Request $request)
|
||||||
'email' => 'required|email|exists:users,email',
|
'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 {
|
} else {
|
||||||
$request->validate([
|
$request->validate([
|
||||||
|
|
@ -76,7 +77,8 @@ public function sendOtp(Request $request)
|
||||||
$no = '62' . substr($no, 1);
|
$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}%"]);
|
$q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$no}%"]);
|
||||||
})->orWhereHas('siswa', function ($q) use ($no) {
|
})->orWhereHas('siswa', function ($q) use ($no) {
|
||||||
$q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$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
|
// Kirim OTP sesuai method — email SAJA atau WA SAJA
|
||||||
app(\App\Services\OtpService::class)->send($user, $method);
|
app(\App\Services\OtpService::class)->send($user, $method);
|
||||||
|
|
||||||
|
|
@ -107,7 +114,8 @@ public function loginOtp(Request $request)
|
||||||
'otp' => 'nullable|digits:6',
|
'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) {
|
if (!$user) {
|
||||||
return back()->with('error', 'Email tidak ditemukan.');
|
return back()->with('error', 'Email tidak ditemukan.');
|
||||||
|
|
@ -134,6 +142,14 @@ public function loginOtp(Request $request)
|
||||||
// =================================================
|
// =================================================
|
||||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
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)) {
|
if (empty($request->otp)) {
|
||||||
return back()->with('error', 'OTP wajib diisi.');
|
return back()->with('error', 'OTP wajib diisi.');
|
||||||
}
|
}
|
||||||
|
|
@ -227,34 +243,35 @@ public function logout(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= VERIFY OTP (halaman terpisah jika ada) =================
|
// ================= VERIFY OTP (halaman terpisah jika ada) =================
|
||||||
// ================= VERIFY OTP (lupa password) =================
|
public function showVerifyOtp()
|
||||||
public function showVerifyOtp()
|
{
|
||||||
{
|
return view('auth.verify-otp');
|
||||||
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) {
|
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) {
|
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)) {
|
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
|
// simpan user reset password
|
||||||
$user->update([
|
session(['otp_user' => $user->id]);
|
||||||
'otp' => null,
|
|
||||||
'otp_expired_at' => null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('reset.password.form');
|
return redirect()->route('reset.password.form');
|
||||||
}
|
}
|
||||||
|
|
@ -314,29 +331,36 @@ private function redirectByRole($user)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= KIRIM OTP LUPA PASSWORD =================
|
// ================= FORGOT PASSWORD OTP =================
|
||||||
// ================= KIRIM OTP LUPA PASSWORD =================
|
public function sendForgotPasswordOtp(Request $request)
|
||||||
public function sendForgotPasswordOtp(Request $request)
|
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'email' => 'required|email|exists:users,email',
|
'email' => 'required|email'
|
||||||
], [
|
|
||||||
'email.exists' => 'Email tidak terdaftar dalam sistem.',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = User::where('email', $request->email)->first();
|
$user = User::where('email', $request->email)->first();
|
||||||
|
|
||||||
// Kirim OTP via email menggunakan OtpService yang sudah ada
|
if (!$user) {
|
||||||
app(\App\Services\OtpService::class)->send($user, 'email');
|
return back()->with('error', 'Email tidak ditemukan');
|
||||||
|
}
|
||||||
|
|
||||||
// Simpan ke session untuk dipakai saat verifikasi & reset
|
$otp = rand(100000, 999999);
|
||||||
session([
|
|
||||||
'otp_user' => $user->id,
|
$user->update([
|
||||||
'email' => $user->email,
|
'otp' => $otp,
|
||||||
|
'otp_expired_at' => now()->addMinutes(5),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Redirect ke halaman verifikasi OTP
|
Mail::raw("Kode OTP reset password Anda adalah: $otp", function ($message) use ($user) {
|
||||||
return redirect()->route('verify.otp')
|
$message->to($user->email)
|
||||||
->with('success', 'OTP telah dikirim ke ' . $user->email);
|
->subject('OTP Reset Password');
|
||||||
|
});
|
||||||
|
|
||||||
|
session([
|
||||||
|
'email' => $user->email
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('verify.otp.form')
|
||||||
|
->with('success', 'OTP reset password berhasil dikirim');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,15 +14,13 @@ public function sendOtp(Request $request)
|
||||||
$method = $request->input('method', 'email');
|
$method = $request->input('method', 'email');
|
||||||
|
|
||||||
if ($method === 'email') {
|
if ($method === 'email') {
|
||||||
// ── EMAIL ──
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'email' => 'required|email|exists:users,email',
|
'email' => 'required|email|exists:users,email',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = User::where('email', $request->email)->first();
|
$user = User::with(['guru', 'siswa'])->where('email', $request->email)->first();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// ── WHATSAPP ──
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'telepon' => 'required|string',
|
'telepon' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
@ -33,7 +31,7 @@ public function sendOtp(Request $request)
|
||||||
}
|
}
|
||||||
$no08 = '0' . substr($no, 2);
|
$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)
|
$q->where('telepon', $no)
|
||||||
->orWhere('telepon', $no08)
|
->orWhere('telepon', $no08)
|
||||||
->orWhere('telepon', 'like', '%' . substr($no, -9) . '%');
|
->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);
|
app(\App\Services\OtpService::class)->send($user, $method);
|
||||||
|
|
||||||
// Simpan email ke session untuk prefill form login
|
|
||||||
session(['email' => $user->email]);
|
session(['email' => $user->email]);
|
||||||
|
|
||||||
return back()->with('otp_sent', 'OTP berhasil dikirim!');
|
return back()->with('otp_sent', 'OTP berhasil dikirim!');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= LOGIN DENGAN OTP =================
|
// ================= LOGIN DENGAN OTP =================
|
||||||
|
|
@ -66,7 +72,7 @@ public function loginOtp(Request $request)
|
||||||
'otp' => 'nullable|digits:6',
|
'otp' => 'nullable|digits:6',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = User::where('email', $request->email)->first();
|
$user = User::with(['guru', 'siswa'])->where('email', $request->email)->first();
|
||||||
|
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
return back()->with('error', 'User tidak ditemukan');
|
return back()->with('error', 'User tidak ditemukan');
|
||||||
|
|
@ -76,6 +82,22 @@ public function loginOtp(Request $request)
|
||||||
return back()->with('error', 'Password salah');
|
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
|
// Guru & Siswa wajib OTP
|
||||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
if (in_array($user->role, ['guru', 'siswa'])) {
|
||||||
|
|
||||||
|
|
@ -99,7 +121,6 @@ public function loginOtp(Request $request)
|
||||||
\Auth::login($user);
|
\Auth::login($user);
|
||||||
$request->session()->regenerate();
|
$request->session()->regenerate();
|
||||||
|
|
||||||
// Hapus OTP setelah dipakai
|
|
||||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
if (in_array($user->role, ['guru', 'siswa'])) {
|
||||||
$user->update(['otp' => null, 'otp_expired_at' => null]);
|
$user->update(['otp' => null, 'otp_expired_at' => null]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ public function store(Request $request)
|
||||||
'kelas_id' => 'required',
|
'kelas_id' => 'required',
|
||||||
'judul' => 'required',
|
'judul' => 'required',
|
||||||
'deadline' => 'required',
|
'deadline' => 'required',
|
||||||
'file' => 'nullable|mimes:pdf,doc,docx,ppt,pptx|max:2048',
|
'file' => 'nullable|max:10240',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$filePath = null;
|
$filePath = null;
|
||||||
|
|
@ -139,27 +139,42 @@ public function edit($id)
|
||||||
return view('guru.tugas.edit', compact('tugas', 'kelas'));
|
return view('guru.tugas.edit', compact('tugas', 'kelas'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, $id)
|
// SESUDAH
|
||||||
{
|
public function update(Request $request, $id)
|
||||||
$request->validate([
|
{
|
||||||
'judul' => 'required',
|
$request->validate([
|
||||||
'kelas_id' => 'required',
|
'judul' => 'required',
|
||||||
'deadline' => '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([
|
$updateData = [
|
||||||
'judul' => $request->judul,
|
'judul' => $request->judul,
|
||||||
'kelas_id' => $request->kelas_id,
|
'kelas_id' => $request->kelas_id,
|
||||||
'deskripsi' => $request->deskripsi,
|
'deskripsi' => $request->deskripsi,
|
||||||
'deadline' => $request->deadline,
|
'deadline' => $request->deadline,
|
||||||
]);
|
];
|
||||||
|
|
||||||
return redirect()->route('guru.tugas.index')
|
// ✅ Jika ada file baru diupload
|
||||||
->with('success', 'Tugas berhasil diupdate');
|
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)
|
public function download($id)
|
||||||
{
|
{
|
||||||
$tugas = Tugas::findOrFail($id);
|
$tugas = Tugas::findOrFail($id);
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,8 @@ class UjianController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
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))
|
$ujians = Ujian::whereHas('kelas', fn($q) => $q->where('kelas.id', $siswa->kelas_id))
|
||||||
->where('status_kirim', 'terkirim')
|
->where('status_kirim', 'terkirim')
|
||||||
->latest()
|
->latest()
|
||||||
|
|
@ -26,64 +25,89 @@ public function index()
|
||||||
$hasilSiswa = Hasil::where('siswa_id', $siswa->id)
|
$hasilSiswa = Hasil::where('siswa_id', $siswa->id)
|
||||||
->get()->keyBy('ujian_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)
|
public function submit(Request $request, $id)
|
||||||
{
|
{
|
||||||
$siswa = auth()->user()->siswa;
|
$siswa = auth()->user()->siswa;
|
||||||
|
|
||||||
$sudah = Hasil::where('siswa_id', $siswa->id)
|
$sudah = Hasil::where('siswa_id', $siswa->id)
|
||||||
->where('ujian_id', $id)
|
->where('ujian_id', $id)
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if ($sudah) {
|
if ($sudah) {
|
||||||
return redirect()->route('siswa.ujian.index')
|
return redirect()->route('siswa.ujian.index')
|
||||||
->with('warning', 'Kamu sudah mengerjakan ujian ini.');
|
->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'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit(Request $request, $id)
|
$ujian = Ujian::findOrFail($id);
|
||||||
{
|
$selesai = $ujian->selesai ? \Carbon\Carbon::parse($ujian->selesai) : null;
|
||||||
$siswa = auth()->user()->siswa;
|
|
||||||
|
|
||||||
// Cegah submit ulang
|
// ✅ FIX: Cegah submit jika waktu sudah habis
|
||||||
$sudah = Hasil::where('siswa_id', $siswa->id)
|
if ($selesai && now()->gt($selesai)) {
|
||||||
->where('ujian_id', $id)
|
return redirect()->route('siswa.ujian.index')
|
||||||
->exists();
|
->with('warning', 'Waktu ujian sudah berakhir, jawaban tidak dapat dikumpulkan.');
|
||||||
|
|
||||||
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'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
|
|
@ -2,156 +2,345 @@
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary: #4F46E5;
|
--primary: #4F46E5;
|
||||||
--primary-dark: #3730A3;
|
--primary-dark: #3730A3;
|
||||||
--success: #059669;
|
--success: #059669;
|
||||||
--success-light: #ECFDF5;
|
--success-light: #ECFDF5;
|
||||||
--danger: #DC2626;
|
--danger: #DC2626;
|
||||||
--danger-light: #FEF2F2;
|
--danger-light: #FEF2F2;
|
||||||
--warning: #D97706;
|
--warning: #D97706;
|
||||||
--warning-light: #FFFBEB;
|
--warning-light: #FFFBEB;
|
||||||
--info: #0284C7;
|
--info: #0284C7;
|
||||||
--info-light: #F0F9FF;
|
--info-light: #F0F9FF;
|
||||||
--bg: #F3F4F8;
|
--bg: #F3F4F8;
|
||||||
--surface: #FFFFFF;
|
--surface: #FFFFFF;
|
||||||
--border: #E5E7EB;
|
--border: #E5E7EB;
|
||||||
--text-dark: #111827;
|
--text-dark: #111827;
|
||||||
--text-mid: #374151;
|
--text-mid: #374151;
|
||||||
--text-soft: #6B7280;
|
--text-soft: #6B7280;
|
||||||
--radius-md: 10px;
|
--radius-md: 10px;
|
||||||
--radius-lg: 14px;
|
--radius-lg: 14px;
|
||||||
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .04);
|
||||||
}
|
}
|
||||||
|
|
||||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
* {
|
||||||
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
.sw-page {
|
||||||
|
padding: 28px 32px;
|
||||||
|
background: var(--bg);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; }
|
.sw-topbar {
|
||||||
.sw-topbar-left h3 { font-size: 22px; font-weight: 700; color: var(--text-dark); margin: 0; letter-spacing: -.3px; }
|
display: flex;
|
||||||
.sw-topbar-left p { font-size: 13px; color: var(--text-soft); margin: 2px 0 0; }
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: var(--radius-md); font-size: 13.5px; font-weight: 600; border: none; cursor: pointer; text-decoration: none; transition: all .18s ease; line-height: 1; }
|
.sw-topbar-left h3 {
|
||||||
.sw-btn-primary { background: var(--primary); color: #fff; }
|
font-size: 22px;
|
||||||
.sw-btn-primary:hover { background: var(--primary-dark); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(79,70,229,.35); }
|
font-weight: 700;
|
||||||
.sw-btn-warning { background: var(--warning-light); color: var(--warning); border: 1px solid #FDE68A; }
|
color: var(--text-dark);
|
||||||
.sw-btn-warning:hover { background: var(--warning); color: #fff; }
|
margin: 0;
|
||||||
.sw-btn-danger { background: var(--danger-light); color: var(--danger); border: 1px solid #FECACA; }
|
letter-spacing: -.3px;
|
||||||
.sw-btn-danger:hover { background: var(--danger); color: #fff; }
|
}
|
||||||
.sw-btn-sm { padding: 6px 12px; font-size: 12.5px; border-radius: 8px; }
|
|
||||||
|
|
||||||
.sw-alert { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-radius: var(--radius-md); font-size: 13.5px; font-weight: 500; margin-bottom: 20px; background: var(--success-light); color: var(--success); border: 1px solid #A7F3D0; }
|
.sw-topbar-left p {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
margin: 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-card { background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); border: 1px solid var(--border); }
|
.sw-btn {
|
||||||
.sw-table-card { overflow: hidden; }
|
display: inline-flex;
|
||||||
.sw-table-wrap { overflow-x: auto; }
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
padding: 9px 18px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 13.5px;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all .18s ease;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
table.sw-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
.sw-btn-primary {
|
||||||
table.sw-table thead tr { background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%); border-bottom: 2px solid #DDD6FE; }
|
background: var(--primary);
|
||||||
table.sw-table thead th { padding: 13px 16px; font-weight: 700; font-size: 12px; color: #5B21B6; text-transform: uppercase; letter-spacing: .6px; white-space: nowrap; border: none; }
|
color: #fff;
|
||||||
table.sw-table tbody tr { border-bottom: 1px solid var(--border); transition: background .15s; }
|
}
|
||||||
table.sw-table tbody tr:last-child { border-bottom: none; }
|
|
||||||
table.sw-table tbody tr:hover { background: #FAFAFF; }
|
|
||||||
table.sw-table td { padding: 13px 16px; color: var(--text-mid); border: none; vertical-align: middle; }
|
|
||||||
table.sw-table td.center { text-align: center; }
|
|
||||||
table.sw-table td.name-cell { font-weight: 600; color: var(--text-dark); }
|
|
||||||
|
|
||||||
.sw-badge { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 99px; font-size: 11.5px; font-weight: 600; letter-spacing: .1px; }
|
.sw-btn-primary:hover {
|
||||||
.sw-badge-info { background: var(--info-light); color: var(--info); }
|
background: var(--primary-dark);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(79, 70, 229, .35);
|
||||||
|
}
|
||||||
|
|
||||||
.sw-action-cell { display: flex; gap: 6px; align-items: center; justify-content: center; }
|
.sw-btn-warning {
|
||||||
|
background: var(--warning-light);
|
||||||
|
color: var(--warning);
|
||||||
|
border: 1px solid #FDE68A;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-no { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; background: #F3F4F6; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--text-soft); }
|
.sw-btn-warning:hover {
|
||||||
|
background: var(--warning);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-empty { padding: 60px 20px; text-align: center; color: var(--text-soft); }
|
.sw-btn-danger {
|
||||||
.sw-empty-icon { font-size: 40px; margin-bottom: 12px; opacity: .4; }
|
background: var(--danger-light);
|
||||||
.sw-empty p { font-size: 14px; margin: 0; }
|
color: var(--danger);
|
||||||
</style>
|
border: 1px solid #FECACA;
|
||||||
|
}
|
||||||
|
|
||||||
<div class="sw-page">
|
.sw-btn-danger:hover {
|
||||||
|
background: var(--danger);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
{{-- TOPBAR --}}
|
.sw-btn-sm {
|
||||||
<div class="sw-topbar">
|
padding: 6px 12px;
|
||||||
<div class="sw-topbar-left">
|
font-size: 12.5px;
|
||||||
<h3>Data Kelas</h3>
|
border-radius: 8px;
|
||||||
<p>Pengelolaan data kelas SMA Negeri 1 Bondowoso</p>
|
}
|
||||||
|
|
||||||
|
.sw-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 13px 16px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 13.5px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: var(--success-light);
|
||||||
|
color: var(--success);
|
||||||
|
border: 1px solid #A7F3D0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-card {
|
||||||
|
background: var(--surface);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-table-card {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table thead tr {
|
||||||
|
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||||
|
border-bottom: 2px solid #DDD6FE;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table thead th {
|
||||||
|
padding: 13px 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #5B21B6;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table tbody tr {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
transition: background .15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table tbody tr:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table tbody tr:hover {
|
||||||
|
background: #FAFAFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table td {
|
||||||
|
padding: 13px 16px;
|
||||||
|
color: var(--text-mid);
|
||||||
|
border: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table td.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.sw-table td.name-cell {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 99px;
|
||||||
|
font-size: 11.5px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: .1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-badge-info {
|
||||||
|
background: var(--info-light);
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-action-cell {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-no {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
background: #F3F4F6;
|
||||||
|
border-radius: 7px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty {
|
||||||
|
padding: 60px 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty-icon {
|
||||||
|
font-size: 40px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty p {
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="sw-page">
|
||||||
|
|
||||||
|
{{-- TOPBAR --}}
|
||||||
|
<div class="sw-topbar">
|
||||||
|
<div class="sw-topbar-left">
|
||||||
|
<h3>Data Kelas</h3>
|
||||||
|
<p>Pengelolaan data kelas SMA Negeri 1 Bondowoso</p>
|
||||||
|
</div>
|
||||||
|
<a href="{{ route('admin.kelas.create') }}" class="sw-btn sw-btn-primary">
|
||||||
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||||
|
<path d="M12 5v14M5 12h14" />
|
||||||
|
</svg>
|
||||||
|
Tambah Kelas
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ route('admin.kelas.create') }}" class="sw-btn sw-btn-primary">
|
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
|
|
||||||
Tambah Kelas
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- ALERT --}}
|
{{-- ALERT --}}
|
||||||
@if(session('success'))
|
@if(session('success'))
|
||||||
<div class="sw-alert">
|
<div class="sw-alert">
|
||||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||||
{{ session('success') }}
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
||||||
</div>
|
<polyline points="22 4 12 14.01 9 11.01" />
|
||||||
@endif
|
</svg>
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- TABLE --}}
|
{{-- TABLE --}}
|
||||||
<div class="sw-card sw-table-card">
|
<div class="sw-card sw-table-card">
|
||||||
<div class="sw-table-wrap">
|
<div class="sw-table-wrap">
|
||||||
<table class="sw-table">
|
<table class="sw-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="center" style="width:52px">No</th>
|
<th class="center" style="width:52px">No</th>
|
||||||
<th>Nama Kelas</th>
|
<th>Nama Kelas</th>
|
||||||
<th class="center">Jurusan</th>
|
<th class="center">Jurusan</th>
|
||||||
<th class="center" style="width:160px">Aksi</th>
|
<th class="center" style="width:160px">Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@forelse($kelas as $k)
|
@forelse($kelas as $k)
|
||||||
<tr>
|
<tr>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<span class="sw-no">{{ $loop->iteration }}</span>
|
<span class="sw-no">{{ $loop->iteration }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="name-cell">{{ $k->nama_kelas }}</td>
|
<td class="name-cell">{{ $k->nama_kelas }}</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<span class="sw-badge sw-badge-info">{{ $k->jurusan }}</span>
|
<span class="sw-badge sw-badge-info">{{ $k->jurusan }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="sw-action-cell">
|
<div class="sw-action-cell">
|
||||||
<a href="{{ route('admin.kelas.edit', $k->id) }}"
|
<a href="{{ route('admin.kelas.edit', $k->id) }}"
|
||||||
class="sw-btn sw-btn-warning sw-btn-sm">
|
class="sw-btn sw-btn-warning sw-btn-sm">
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
Edit
|
stroke-width="2.2">
|
||||||
</a>
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||||
<form action="{{ route('admin.kelas.destroy', $k->id) }}"
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||||
method="POST" class="d-inline">
|
</svg>
|
||||||
@csrf
|
Edit
|
||||||
@method('DELETE')
|
</a>
|
||||||
<button type="button"
|
<form action="{{ route('admin.kelas.destroy', $k->id) }}" method="POST"
|
||||||
class="sw-btn sw-btn-danger sw-btn-sm"
|
class="d-inline">
|
||||||
onclick="return confirm('Yakin hapus data kelas ini?')">
|
@csrf
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/></svg>
|
@method('DELETE')
|
||||||
Hapus
|
<button type="submit" class="sw-btn sw-btn-danger sw-btn-sm"
|
||||||
</button>
|
onclick="return confirm('Yakin hapus data kelas ini?')">
|
||||||
</form>
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
</div>
|
stroke="currentColor" stroke-width="2.2">
|
||||||
</td>
|
<polyline points="3 6 5 6 21 6" />
|
||||||
</tr>
|
<path d="M19 6l-1 14H6L5 6" />
|
||||||
@empty
|
<path d="M10 11v6M14 11v6" />
|
||||||
<tr>
|
</svg>
|
||||||
<td colspan="4">
|
Hapus
|
||||||
<div class="sw-empty">
|
</button>
|
||||||
<div class="sw-empty-icon">🏫</div>
|
</form>
|
||||||
<p>Data kelas belum tersedia</p>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
@empty
|
||||||
@endforelse
|
<tr>
|
||||||
</tbody>
|
<td colspan="4">
|
||||||
</table>
|
<div class="sw-empty">
|
||||||
|
<div class="sw-empty-icon">🏫</div>
|
||||||
|
<p>Data kelas belum tersedia</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -154,10 +154,10 @@ function dismissToast(){
|
||||||
<p class="page-subtitle">Rekap nilai tugas, UTS, UAS & hitung rata-rata siswa</p>
|
<p class="page-subtitle">Rekap nilai tugas, UTS, UAS & hitung rata-rata siswa</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ route('guru.nilai.input') }}" class="btn-primary">
|
<!-- <a href="{{ route('guru.nilai.input') }}" class="btn-primary">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
|
||||||
Input Nilai Manual
|
Input Nilai Manual
|
||||||
</a>
|
</a> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- INFO --}}
|
{{-- INFO --}}
|
||||||
|
|
@ -293,20 +293,23 @@ function dismissToast(){
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- FOOTER + TOMBOL KIRIM SEMUA KELAS --}}
|
{{-- FOOTER + TOMBOL KIRIM SEMUA KELAS --}}
|
||||||
<div class="table-footer">
|
<div class="table-footer">
|
||||||
<span>{{ $nilai->count() }} data nilai</span>
|
<span>{{ $nilai->count() }} data nilai</span>
|
||||||
<div style="display:flex;align-items:center;gap:10px;">
|
<div style="display:flex;align-items:center;gap:10px;">
|
||||||
<span>{{ now()->format('d M Y') }}</span>
|
<span>{{ now()->format('d M Y') }}</span>
|
||||||
<form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST" style="margin:0;">
|
{{-- ✅ Hanya tampil jika TIDAK ada filter kelas --}}
|
||||||
@csrf
|
@if(!request('kelas_id'))
|
||||||
<button type="submit" class="btn-violet" style="padding:7px 14px;font-size:12.5px;"
|
<form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST" style="margin:0;">
|
||||||
onclick="return confirm('Kirim nilai ke SEMUA kelas? Siswa di semua kelas akan bisa melihat nilai mereka.')">
|
@csrf
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/></svg>
|
<button type="submit" class="btn-violet" style="padding:7px 14px;font-size:12.5px;"
|
||||||
Kirim ke Semua Kelas
|
onclick="return confirm('Kirim nilai ke SEMUA kelas? Siswa di semua kelas akan bisa melihat nilai mereka.')">
|
||||||
</button>
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/></svg>
|
||||||
</form>
|
Kirim ke Semua Kelas
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</form>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,7 @@
|
||||||
<th class="center">Aksi</th>
|
<th class="center">Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<!-- <tbody>
|
<tbody>
|
||||||
@forelse($tugas as $t)
|
@forelse($tugas as $t)
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
||||||
|
|
@ -516,7 +516,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody> -->
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,219 +2,567 @@
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary: #4F46E5;
|
--primary: #4F46E5;
|
||||||
--primary-dark: #3730A3;
|
--primary-dark: #3730A3;
|
||||||
--primary-light: #EEF2FF;
|
--primary-light: #EEF2FF;
|
||||||
--success: #059669;
|
--success: #059669;
|
||||||
--success-light: #ECFDF5;
|
--success-light: #ECFDF5;
|
||||||
--info: #0284C7;
|
--info: #0284C7;
|
||||||
--info-light: #F0F9FF;
|
--info-light: #F0F9FF;
|
||||||
--bg: #F3F4F8;
|
--bg: #F3F4F8;
|
||||||
--surface: #FFFFFF;
|
--surface: #FFFFFF;
|
||||||
--border: #E5E7EB;
|
--border: #E5E7EB;
|
||||||
--text-dark: #111827;
|
--text-dark: #111827;
|
||||||
--text-mid: #374151;
|
--text-mid: #374151;
|
||||||
--text-soft: #6B7280;
|
--text-soft: #6B7280;
|
||||||
--radius-md: 10px;
|
--radius-md: 10px;
|
||||||
--radius-lg: 16px;
|
--radius-lg: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
* {
|
||||||
#content { padding: 0 !important; background: var(--bg) !important; }
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||||
#content-wrapper { background: var(--bg) !important; }
|
box-sizing: border-box;
|
||||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: calc(100vh - 70px); }
|
}
|
||||||
|
|
||||||
.sw-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 28px; flex-wrap: wrap; gap: 12px; }
|
#content {
|
||||||
.sw-topbar h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 3px; letter-spacing: -.3px; }
|
padding: 0 !important;
|
||||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
background: var(--bg) !important;
|
||||||
.sw-badge-ready { display: inline-flex; align-items: center; gap: 7px; background: var(--primary-light); border: 1px solid #C7D2FE; border-radius: 99px; padding: 7px 16px; font-size: 13px; font-weight: 600; color: var(--primary); }
|
}
|
||||||
|
|
||||||
.sw-alert-warning { display: flex; align-items: center; gap: 10px; background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 10px; padding: 12px 16px; margin-bottom: 20px; font-size: 13px; font-weight: 600; color: #92400E; }
|
#content-wrapper {
|
||||||
|
background: var(--bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.sw-hero-card { position: relative; border-radius: 22px; overflow: hidden; margin-bottom: 24px; box-shadow: 0 12px 40px rgba(79,70,229,.22); }
|
.sw-page {
|
||||||
.sw-hero-bg { position: absolute; inset: 0; background: linear-gradient(135deg, #4F46E5 0%, #0284C7 100%); }
|
padding: 28px 32px;
|
||||||
.sw-hero-blob1 { position: absolute; width: 260px; height: 260px; border-radius: 50%; background: rgba(255,255,255,.07); top: -80px; right: -60px; }
|
background: var(--bg);
|
||||||
.sw-hero-blob2 { position: absolute; width: 160px; height: 160px; border-radius: 50%; background: rgba(255,255,255,.05); bottom: -50px; left: 40px; }
|
min-height: calc(100vh - 70px);
|
||||||
.sw-hero-inner { position: relative; z-index: 2; padding: 36px 40px; display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
|
}
|
||||||
.sw-hero-eyebrow { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.18); border-radius: 99px; padding: 4px 12px; font-size: 11.5px; font-weight: 700; color: rgba(255,255,255,.9); letter-spacing: .4px; text-transform: uppercase; margin-bottom: 12px; }
|
|
||||||
.sw-hero-title { font-size: 24px; font-weight: 800; color: #fff; margin-bottom: 14px; letter-spacing: -.4px; line-height: 1.3; }
|
|
||||||
.sw-hero-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 22px; }
|
|
||||||
.sw-hero-chip { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.2); border-radius: 8px; padding: 5px 12px; font-size: 12px; font-weight: 600; color: #fff; }
|
|
||||||
.sw-btn-start { display: inline-flex; align-items: center; gap: 8px; background: #fff; color: var(--primary); border: none; border-radius: 12px; padding: 12px 26px; font-size: 14px; font-weight: 800; cursor: pointer; text-decoration: none; transition: all .18s; font-family: 'Plus Jakarta Sans', sans-serif; box-shadow: 0 4px 16px rgba(0,0,0,.12); }
|
|
||||||
.sw-btn-start:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,.18); color: var(--primary-dark); text-decoration: none; }
|
|
||||||
.sw-btn-done { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,.2); color: rgba(255,255,255,.75); border: 1px solid rgba(255,255,255,.3); border-radius: 12px; padding: 12px 26px; font-size: 14px; font-weight: 700; cursor: not-allowed; }
|
|
||||||
.sw-hero-deco { width: 100px; height: 100px; border-radius: 24px; background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2); display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: #fff; }
|
|
||||||
|
|
||||||
.sw-stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px; }
|
.sw-topbar {
|
||||||
.sw-stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 20px 22px; display: flex; align-items: center; gap: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.04); }
|
display: flex;
|
||||||
.sw-stat-icon { width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
align-items: center;
|
||||||
.si-indigo { background: var(--primary-light); color: var(--primary); }
|
justify-content: space-between;
|
||||||
.si-blue { background: var(--info-light); color: var(--info); }
|
margin-bottom: 28px;
|
||||||
.si-green { background: var(--success-light); color: var(--success); }
|
flex-wrap: wrap;
|
||||||
.sw-stat-label { font-size: 12px; font-weight: 600; color: var(--text-soft); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 3px; }
|
gap: 12px;
|
||||||
.sw-stat-value { font-size: 22px; font-weight: 800; color: var(--text-dark); line-height: 1; }
|
}
|
||||||
|
|
||||||
.sw-empty { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; padding: 64px 24px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,.04); margin-bottom: 24px; }
|
.sw-topbar h3 {
|
||||||
.sw-empty-ring { width: 80px; height: 80px; border-radius: 50%; background: var(--primary-light); border: 2px solid #C7D2FE; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; color: var(--primary); }
|
font-size: 21px;
|
||||||
.sw-empty h5 { font-size: 17px; font-weight: 700; color: var(--text-dark); margin-bottom: 8px; }
|
font-weight: 800;
|
||||||
.sw-empty p { font-size: 13.5px; color: var(--text-soft); margin: 0; }
|
color: var(--text-dark);
|
||||||
|
margin: 0 0 3px;
|
||||||
|
letter-spacing: -.3px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
.sw-topbar p {
|
||||||
.sw-page { padding: 16px; }
|
font-size: 13px;
|
||||||
.sw-hero-inner { padding: 24px; }
|
color: var(--text-soft);
|
||||||
.sw-hero-deco { display: none; }
|
margin: 0;
|
||||||
.sw-hero-title { font-size: 19px; }
|
}
|
||||||
.sw-stats-row { grid-template-columns: 1fr; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="sw-page">
|
.sw-badge-ready {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
background: var(--primary-light);
|
||||||
|
border: 1px solid #C7D2FE;
|
||||||
|
border-radius: 99px;
|
||||||
|
padding: 7px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
{{-- TOPBAR --}}
|
.sw-alert-warning {
|
||||||
<div class="sw-topbar">
|
display: flex;
|
||||||
<div>
|
align-items: center;
|
||||||
<h3>Ujian Tersedia</h3>
|
gap: 10px;
|
||||||
<p>Daftar ujian yang dapat Anda kerjakan sekarang</p>
|
background: #FFFBEB;
|
||||||
</div>
|
border: 1px solid #FDE68A;
|
||||||
<div class="sw-badge-ready">
|
border-radius: 10px;
|
||||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
padding: 12px 16px;
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
margin-bottom: 20px;
|
||||||
</svg>
|
font-size: 13px;
|
||||||
Siap mengerjakan ujian hari ini?
|
font-weight: 600;
|
||||||
</div>
|
color: #92400E;
|
||||||
</div>
|
}
|
||||||
|
|
||||||
{{-- ALERT WARNING --}}
|
.sw-hero-card {
|
||||||
@if(session('warning'))
|
position: relative;
|
||||||
<div class="sw-alert-warning">
|
border-radius: 22px;
|
||||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
overflow: hidden;
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/>
|
margin-bottom: 24px;
|
||||||
</svg>
|
box-shadow: 0 12px 40px rgba(79, 70, 229, .22);
|
||||||
{{ session('warning') }}
|
}
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{{-- DAFTAR UJIAN --}}
|
.sw-hero-bg {
|
||||||
@forelse($ujians as $u)
|
position: absolute;
|
||||||
@php $dikerjakan = in_array($u->id, $sudahDikerjakan); @endphp
|
inset: 0;
|
||||||
|
background: linear-gradient(135deg, #4F46E5 0%, #0284C7 100%);
|
||||||
|
}
|
||||||
|
|
||||||
<div class="sw-hero-card" style="{{ $dikerjakan ? 'opacity:0.85;box-shadow:0 8px 24px rgba(0,0,0,.1)' : '' }}">
|
.sw-hero-blob1 {
|
||||||
<div class="sw-hero-bg" style="{{ $dikerjakan ? 'background:linear-gradient(135deg,#374151 0%,#6B7280 100%)' : '' }}"></div>
|
position: absolute;
|
||||||
<div class="sw-hero-blob1"></div>
|
width: 260px;
|
||||||
<div class="sw-hero-blob2"></div>
|
height: 260px;
|
||||||
<div class="sw-hero-inner">
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, .07);
|
||||||
|
top: -80px;
|
||||||
|
right: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-blob2 {
|
||||||
|
position: absolute;
|
||||||
|
width: 160px;
|
||||||
|
height: 160px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, .05);
|
||||||
|
bottom: -50px;
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
padding: 36px 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-eyebrow {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: rgba(255, 255, 255, .18);
|
||||||
|
border-radius: 99px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 11.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255, 255, 255, .9);
|
||||||
|
letter-spacing: .4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
letter-spacing: -.4px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-chips {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: rgba(255, 255, 255, .15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .2);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-btn-start {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: #fff;
|
||||||
|
color: var(--primary);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 26px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all .18s;
|
||||||
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-btn-start:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
|
||||||
|
color: var(--primary-dark);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-btn-done {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: rgba(255, 255, 255, .2);
|
||||||
|
color: rgba(255, 255, 255, .75);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .3);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 26px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-deco {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 24px;
|
||||||
|
background: rgba(255, 255, 255, .12);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stats-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stat-card {
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stat-icon {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.si-indigo {
|
||||||
|
background: var(--primary-light);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.si-blue {
|
||||||
|
background: var(--info-light);
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.si-green {
|
||||||
|
background: var(--success-light);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stat-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-soft);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .4px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stat-value {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text-dark);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty {
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 64px 24px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty-ring {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary-light);
|
||||||
|
border: 2px solid #C7D2FE;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 20px;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty h5 {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-dark);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-empty p {
|
||||||
|
font-size: 13.5px;
|
||||||
|
color: var(--text-soft);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sw-page {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-inner {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-deco {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-hero-title {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-stats-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="sw-page">
|
||||||
|
|
||||||
|
{{-- TOPBAR --}}
|
||||||
|
<div class="sw-topbar">
|
||||||
<div>
|
<div>
|
||||||
{{-- EYEBROW --}}
|
<h3>Ujian Tersedia</h3>
|
||||||
<div class="sw-hero-eyebrow">
|
<p>Daftar ujian yang dapat Anda kerjakan sekarang</p>
|
||||||
@if($dikerjakan)
|
</div>
|
||||||
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
<div class="sw-badge-ready">
|
||||||
Sudah Dikerjakan
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||||
@else
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
Ujian Aktif
|
</svg>
|
||||||
@endif
|
Siap mengerjakan ujian hari ini?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- ALERT WARNING --}}
|
||||||
|
@if(session('warning'))
|
||||||
|
<div class="sw-alert-warning">
|
||||||
|
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
|
||||||
|
</svg>
|
||||||
|
{{ session('warning') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- DAFTAR UJIAN --}}
|
||||||
|
@forelse($ujians as $u)
|
||||||
|
@php $dikerjakan = in_array($u->id, $sudahDikerjakan); @endphp
|
||||||
|
|
||||||
|
<div class="sw-hero-card" style="{{ $dikerjakan ? 'opacity:0.85;box-shadow:0 8px 24px rgba(0,0,0,.1)' : '' }}">
|
||||||
|
<div class="sw-hero-bg"
|
||||||
|
style="{{ $dikerjakan ? 'background:linear-gradient(135deg,#374151 0%,#6B7280 100%)' : '' }}"></div>
|
||||||
|
<div class="sw-hero-blob1"></div>
|
||||||
|
<div class="sw-hero-blob2"></div>
|
||||||
|
<div class="sw-hero-inner">
|
||||||
|
<div>
|
||||||
|
{{-- EYEBROW --}}
|
||||||
|
<div class="sw-hero-eyebrow">
|
||||||
|
@if($dikerjakan)
|
||||||
|
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2.5">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
Sudah Dikerjakan
|
||||||
|
@else
|
||||||
|
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2.5">
|
||||||
|
<path
|
||||||
|
d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
|
||||||
|
</svg>
|
||||||
|
Ujian Aktif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- JUDUL --}}
|
||||||
|
<div class="sw-hero-title">{{ $u->judul }}</div>
|
||||||
|
|
||||||
|
{{-- CHIPS --}}
|
||||||
|
<div class="sw-hero-chips">
|
||||||
|
<span class="sw-hero-chip">
|
||||||
|
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<polyline points="12 6 12 12 16 14" />
|
||||||
|
</svg>
|
||||||
|
{{ $u->durasi }} menit
|
||||||
|
</span>
|
||||||
|
{{-- Ganti chip jenis yang ada di sw-hero-chips --}}
|
||||||
|
@if($u->jenis)
|
||||||
|
<span class="sw-hero-chip"
|
||||||
|
style="{{ $u->jenis == 'UTS' ? 'background:rgba(2,132,199,.25);border-color:rgba(2,132,199,.4);' : 'background:rgba(217,119,6,.25);border-color:rgba(217,119,6,.4);' }} font-weight:800;">
|
||||||
|
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||||
|
<polyline points="14 2 14 8 20 8" />
|
||||||
|
</svg>
|
||||||
|
{{ $u->jenis }} — {{ $u->jenis == 'UTS' ? 'Ujian Tengah Semester' : 'Ujian Akhir Semester' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if($u->mulai)
|
||||||
|
<span class="sw-hero-chip">
|
||||||
|
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2">
|
||||||
|
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||||||
|
<line x1="3" y1="10" x2="21" y2="10" />
|
||||||
|
</svg>
|
||||||
|
{{ $u->mulai }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
{{-- CHIP NILAI (muncul kalau sudah dikerjakan) --}}
|
||||||
|
@if($dikerjakan && isset($hasilSiswa[$u->id]))
|
||||||
|
<span class="sw-hero-chip" style="background:rgba(255,255,255,.3);font-weight:800;font-size:13px;">
|
||||||
|
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2">
|
||||||
|
<path
|
||||||
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10" />
|
||||||
|
</svg>
|
||||||
|
Nilai: {{ $hasilSiswa[$u->id]->nilai }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- TOMBOL --}}
|
||||||
|
@php $berakhir = in_array($u->id, $sudahBerakhir); @endphp
|
||||||
|
@if($dikerjakan || $berakhir)
|
||||||
|
<span class="sw-btn-done"
|
||||||
|
style="{{ $berakhir && !$dikerjakan ? 'background:rgba(220,38,38,.2);border-color:rgba(220,38,38,.3);' : '' }}">
|
||||||
|
@if($berakhir && !$dikerjakan)
|
||||||
|
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2.5">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<line x1="12" y1="8" x2="12" y2="12" />
|
||||||
|
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||||||
|
</svg>
|
||||||
|
Waktu Ujian Sudah Berakhir
|
||||||
|
@else
|
||||||
|
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2.5">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
Sudah Selesai Dikerjakan
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="sw-btn-start">
|
||||||
|
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
stroke-width="2.5">
|
||||||
|
<polygon points="5 3 19 12 5 21 5 3" />
|
||||||
|
</svg>
|
||||||
|
Mulai Ujian Sekarang
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- DECO ICON --}}
|
||||||
|
<div class="sw-hero-deco">
|
||||||
|
@if($dikerjakan)
|
||||||
|
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
@else
|
||||||
|
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||||
|
<polyline points="14 2 14 8 20 8" />
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13" />
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17" />
|
||||||
|
</svg>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- JUDUL --}}
|
@empty
|
||||||
<div class="sw-hero-title">{{ $u->judul }}</div>
|
<div class="sw-empty">
|
||||||
|
<div class="sw-empty-ring">
|
||||||
{{-- CHIPS --}}
|
<svg width="32" height="32" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||||
<div class="sw-hero-chips">
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||||
<span class="sw-hero-chip">
|
<polyline points="14 2 14 8 20 8" />
|
||||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
<line x1="16" y1="13" x2="8" y2="13" />
|
||||||
{{ $u->durasi }} menit
|
<line x1="16" y1="17" x2="8" y2="17" />
|
||||||
</span>
|
</svg>
|
||||||
{{-- Ganti chip jenis yang ada di sw-hero-chips --}}
|
|
||||||
@if($u->jenis)
|
|
||||||
<span class="sw-hero-chip" style="{{ $u->jenis == 'UTS' ? 'background:rgba(2,132,199,.25);border-color:rgba(2,132,199,.4);' : 'background:rgba(217,119,6,.25);border-color:rgba(217,119,6,.4);' }} font-weight:800;">
|
|
||||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
||||||
{{ $u->jenis }} — {{ $u->jenis == 'UTS' ? 'Ujian Tengah Semester' : 'Ujian Akhir Semester' }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
@if($u->mulai)
|
|
||||||
<span class="sw-hero-chip">
|
|
||||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
|
||||||
{{ $u->mulai }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
{{-- CHIP NILAI (muncul kalau sudah dikerjakan) --}}
|
|
||||||
@if($dikerjakan && isset($hasilSiswa[$u->id]))
|
|
||||||
<span class="sw-hero-chip" style="background:rgba(255,255,255,.3);font-weight:800;font-size:13px;">
|
|
||||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10"/></svg>
|
|
||||||
Nilai: {{ $hasilSiswa[$u->id]->nilai }}
|
|
||||||
</span>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
<h5>Tidak Ada Ujian</h5>
|
||||||
{{-- TOMBOL --}}
|
<p>Saat ini belum ada ujian yang tersedia. Silakan cek kembali nanti.</p>
|
||||||
@if($dikerjakan)
|
|
||||||
<span class="sw-btn-done">
|
|
||||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
||||||
Sudah Selesai Dikerjakan
|
|
||||||
</span>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="sw-btn-start">
|
|
||||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
||||||
Mulai Ujian Sekarang
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
@endforelse
|
||||||
|
|
||||||
{{-- DECO ICON --}}
|
{{-- MINI INFO --}}
|
||||||
<div class="sw-hero-deco">
|
<div class="sw-stats-row">
|
||||||
@if($dikerjakan)
|
<div class="sw-stat-card">
|
||||||
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
<div class="sw-stat-icon si-indigo">
|
||||||
@else
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
<path d="M9 11l3 3L22 4" />
|
||||||
@endif
|
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="sw-stat-label">Total Ujian</div>
|
||||||
|
<div class="sw-stat-value">{{ $ujians->count() }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sw-stat-card">
|
||||||
|
<div class="sw-stat-icon si-blue">
|
||||||
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<polyline points="12 6 12 12 16 14" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="sw-stat-label">Sudah Dikerjakan</div>
|
||||||
|
<div class="sw-stat-value" style="color:var(--info);">{{ count($sudahDikerjakan) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sw-stat-card">
|
||||||
|
<div class="sw-stat-icon si-green">
|
||||||
|
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M5 12.55a11 11 0 0 1 14.08 0" />
|
||||||
|
<path d="M1.42 9a16 16 0 0 1 21.16 0" />
|
||||||
|
<path d="M8.53 16.11a6 6 0 0 1 6.95 0" />
|
||||||
|
<line x1="12" y1="20" x2="12.01" y2="20" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="sw-stat-label">Mode</div>
|
||||||
|
<div class="sw-stat-value" style="font-size:16px;color:var(--success);">Online</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@empty
|
|
||||||
<div class="sw-empty">
|
|
||||||
<div class="sw-empty-ring">
|
|
||||||
<svg width="32" height="32" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
|
||||||
</div>
|
|
||||||
<h5>Tidak Ada Ujian</h5>
|
|
||||||
<p>Saat ini belum ada ujian yang tersedia. Silakan cek kembali nanti.</p>
|
|
||||||
</div>
|
|
||||||
@endforelse
|
|
||||||
|
|
||||||
{{-- MINI INFO --}}
|
|
||||||
<div class="sw-stats-row">
|
|
||||||
<div class="sw-stat-card">
|
|
||||||
<div class="sw-stat-icon si-indigo">
|
|
||||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="sw-stat-label">Total Ujian</div>
|
|
||||||
<div class="sw-stat-value">{{ $ujians->count() }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sw-stat-card">
|
|
||||||
<div class="sw-stat-icon si-blue">
|
|
||||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="sw-stat-label">Sudah Dikerjakan</div>
|
|
||||||
<div class="sw-stat-value" style="color:var(--info);">{{ count($sudahDikerjakan) }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sw-stat-card">
|
|
||||||
<div class="sw-stat-icon si-green">
|
|
||||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="sw-stat-label">Mode</div>
|
|
||||||
<div class="sw-stat-value" style="font-size:16px;color:var(--success);">Online</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
| OTP LOGIN
|
| OTP LOGIN
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
// Route ini pakai OtpController, BUKAN AuthController
|
||||||
Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
||||||
Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
|
Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue