perbaikan

perbaikan bismillah
This commit is contained in:
Ryfandii 2026-05-21 01:25:29 +07:00
parent 21f0229058
commit 7e3718f6a3
11 changed files with 1082 additions and 457 deletions

View File

@ -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');
} }
} }

View File

@ -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,10 +46,18 @@ 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!');
@ -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]);
} }

View File

@ -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([ $request->validate([
'judul' => 'required', 'judul' => 'required',
'kelas_id' => 'required', 'kelas_id' => 'required',
'deadline' => '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,
]); ];
// ✅ 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') return redirect()->route('guru.tugas.index')
->with('success', 'Tugas berhasil diupdate'); ->with('success', 'Tugas berhasil diupdate');
} }
public function download($id) public function download($id)
{ {
$tugas = Tugas::findOrFail($id); $tugas = Tugas::findOrFail($id);

View File

@ -14,7 +14,6 @@ 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,11 +25,16 @@ 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) public function kerjakan($id)
{ {
$siswa = auth()->user()->siswa; $siswa = auth()->user()->siswa;
$sudah = Hasil::where('siswa_id', $siswa->id) $sudah = Hasil::where('siswa_id', $siswa->id)
@ -43,16 +47,30 @@ public function kerjakan($id)
} }
$ujian = Ujian::findOrFail($id); $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(); $soals = Soal::where('ujian_id', $id)->get();
return view('siswa.ujian.kerjakan', compact('ujian', 'soals')); return view('siswa.ujian.kerjakan', compact('ujian', 'soals'));
} }
public function submit(Request $request, $id) public function submit(Request $request, $id)
{ {
$siswa = auth()->user()->siswa; $siswa = auth()->user()->siswa;
// Cegah submit ulang
$sudah = Hasil::where('siswa_id', $siswa->id) $sudah = Hasil::where('siswa_id', $siswa->id)
->where('ujian_id', $id) ->where('ujian_id', $id)
->exists(); ->exists();
@ -62,9 +80,16 @@ public function submit(Request $request, $id)
->with('warning', 'Kamu sudah mengerjakan ujian ini.'); ->with('warning', 'Kamu sudah mengerjakan ujian ini.');
} }
$soals = Soal::where('ujian_id', $id)->get();
$ujian = Ujian::findOrFail($id); $ujian = Ujian::findOrFail($id);
$selesai = $ujian->selesai ? \Carbon\Carbon::parse($ujian->selesai) : null;
// ✅ 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; $benar = 0;
$total = $soals->count(); $total = $soals->count();
@ -77,7 +102,6 @@ public function submit(Request $request, $id)
$nilai = round(($benar / $total) * 100); $nilai = round(($benar / $total) * 100);
// Simpan ke tabel hasils (hanya kolom yang ada)
Hasil::create([ Hasil::create([
'siswa_id' => $siswa->id, 'siswa_id' => $siswa->id,
'ujian_id' => $id, 'ujian_id' => $id,
@ -85,5 +109,5 @@ public function submit(Request $request, $id)
]); ]);
return view('siswa.ujian.hasil', compact('nilai', 'benar', 'total', 'ujian')); 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

View File

@ -2,10 +2,10 @@
@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;
@ -24,55 +24,231 @@
--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;
}
.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-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 --}} {{-- TOPBAR --}}
<div class="sw-topbar"> <div class="sw-topbar">
@ -81,7 +257,9 @@
<p>Pengelolaan data kelas SMA Negeri 1 Bondowoso</p> <p>Pengelolaan data kelas SMA Negeri 1 Bondowoso</p>
</div> </div>
<a href="{{ route('admin.kelas.create') }}" class="sw-btn sw-btn-primary"> <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> <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 Tambah Kelas
</a> </a>
</div> </div>
@ -89,7 +267,10 @@
{{-- 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">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
{{ session('success') }} {{ session('success') }}
</div> </div>
@endif @endif
@ -120,17 +301,25 @@
<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"
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>
Edit Edit
</a> </a>
<form action="{{ route('admin.kelas.destroy', $k->id) }}" <form action="{{ route('admin.kelas.destroy', $k->id) }}" method="POST"
method="POST" class="d-inline"> class="d-inline">
@csrf @csrf
@method('DELETE') @method('DELETE')
<button type="button" <button type="submit" class="sw-btn sw-btn-danger sw-btn-sm"
class="sw-btn sw-btn-danger sw-btn-sm"
onclick="return confirm('Yakin hapus data kelas ini?')"> onclick="return confirm('Yakin hapus data kelas ini?')">
<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> <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>
Hapus Hapus
</button> </button>
</form> </form>
@ -152,6 +341,6 @@ class="sw-btn sw-btn-danger sw-btn-sm"
</div> </div>
</div> </div>
</div> </div>
@endsection @endsection

View File

@ -154,10 +154,10 @@ function dismissToast(){
<p class="page-subtitle">Rekap nilai tugas, UTS, UAS &amp; hitung rata-rata siswa</p> <p class="page-subtitle">Rekap nilai tugas, UTS, UAS &amp; 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 --}}
@ -297,6 +297,8 @@ function dismissToast(){
<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>
{{-- Hanya tampil jika TIDAK ada filter kelas --}}
@if(!request('kelas_id'))
<form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST" style="margin:0;"> <form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST" style="margin:0;">
@csrf @csrf
<button type="submit" class="btn-violet" style="padding:7px 14px;font-size:12.5px;" <button type="submit" class="btn-violet" style="padding:7px 14px;font-size:12.5px;"
@ -305,8 +307,9 @@ function dismissToast(){
Kirim ke Semua Kelas Kirim ke Semua Kelas
</button> </button>
</form> </form>
@endif
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -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>

View File

@ -2,10 +2,10 @@
@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;
@ -21,58 +21,338 @@
--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);
}
.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;
}
.sw-hero-card {
position: relative;
border-radius: 22px;
overflow: hidden;
margin-bottom: 24px;
box-shadow: 0 12px 40px rgba(79, 70, 229, .22);
}
.sw-hero-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #4F46E5 0%, #0284C7 100%);
}
.sw-hero-blob1 {
position: absolute;
width: 260px;
height: 260px;
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 --}} {{-- TOPBAR --}}
<div class="sw-topbar"> <div class="sw-topbar">
@ -82,7 +362,8 @@
</div> </div>
<div class="sw-badge-ready"> <div class="sw-badge-ready">
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"> <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/> <path stroke-linecap="round" stroke-linejoin="round"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg> </svg>
Siap mengerjakan ujian hari ini? Siap mengerjakan ujian hari ini?
</div> </div>
@ -92,7 +373,8 @@
@if(session('warning')) @if(session('warning'))
<div class="sw-alert-warning"> <div class="sw-alert-warning">
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2"> <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"/> <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> </svg>
{{ session('warning') }} {{ session('warning') }}
</div> </div>
@ -103,7 +385,8 @@
@php $dikerjakan = in_array($u->id, $sudahDikerjakan); @endphp @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-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-bg"
style="{{ $dikerjakan ? 'background:linear-gradient(135deg,#374151 0%,#6B7280 100%)' : '' }}"></div>
<div class="sw-hero-blob1"></div> <div class="sw-hero-blob1"></div>
<div class="sw-hero-blob2"></div> <div class="sw-hero-blob2"></div>
<div class="sw-hero-inner"> <div class="sw-hero-inner">
@ -111,10 +394,17 @@
{{-- EYEBROW --}} {{-- EYEBROW --}}
<div class="sw-hero-eyebrow"> <div class="sw-hero-eyebrow">
@if($dikerjakan) @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> <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 Sudah Dikerjakan
@else @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> <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 Ujian Aktif
@endif @endif
</div> </div>
@ -125,40 +415,75 @@
{{-- CHIPS --}} {{-- CHIPS --}}
<div class="sw-hero-chips"> <div class="sw-hero-chips">
<span class="sw-hero-chip"> <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> <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 {{ $u->durasi }} menit
</span> </span>
{{-- Ganti chip jenis yang ada di sw-hero-chips --}} {{-- Ganti chip jenis yang ada di sw-hero-chips --}}
@if($u->jenis) @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;"> <span class="sw-hero-chip"
<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> 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' }} {{ $u->jenis }} {{ $u->jenis == 'UTS' ? 'Ujian Tengah Semester' : 'Ujian Akhir Semester' }}
</span> </span>
@endif @endif
@if($u->mulai) @if($u->mulai)
<span class="sw-hero-chip"> <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> <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 }} {{ $u->mulai }}
</span> </span>
@endif @endif
{{-- CHIP NILAI (muncul kalau sudah dikerjakan) --}} {{-- CHIP NILAI (muncul kalau sudah dikerjakan) --}}
@if($dikerjakan && isset($hasilSiswa[$u->id])) @if($dikerjakan && isset($hasilSiswa[$u->id]))
<span class="sw-hero-chip" style="background:rgba(255,255,255,.3);font-weight:800;font-size:13px;"> <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> <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 }} Nilai: {{ $hasilSiswa[$u->id]->nilai }}
</span> </span>
@endif @endif
</div> </div>
{{-- TOMBOL --}} {{-- TOMBOL --}}
@if($dikerjakan) @php $berakhir = in_array($u->id, $sudahBerakhir); @endphp
<span class="sw-btn-done"> @if($dikerjakan || $berakhir)
<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> <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 Sudah Selesai Dikerjakan
@endif
</span> </span>
@else @else
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="sw-btn-start"> <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> <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 Mulai Ujian Sekarang
</a> </a>
@endif @endif
@ -167,9 +492,16 @@
{{-- DECO ICON --}} {{-- DECO ICON --}}
<div class="sw-hero-deco"> <div class="sw-hero-deco">
@if($dikerjakan) @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> <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 @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> <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 @endif
</div> </div>
</div> </div>
@ -178,7 +510,12 @@
@empty @empty
<div class="sw-empty"> <div class="sw-empty">
<div class="sw-empty-ring"> <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> <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> </div>
<h5>Tidak Ada Ujian</h5> <h5>Tidak Ada Ujian</h5>
<p>Saat ini belum ada ujian yang tersedia. Silakan cek kembali nanti.</p> <p>Saat ini belum ada ujian yang tersedia. Silakan cek kembali nanti.</p>
@ -189,7 +526,10 @@
<div class="sw-stats-row"> <div class="sw-stats-row">
<div class="sw-stat-card"> <div class="sw-stat-card">
<div class="sw-stat-icon si-indigo"> <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> <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> <div>
<div class="sw-stat-label">Total Ujian</div> <div class="sw-stat-label">Total Ujian</div>
@ -198,7 +538,10 @@
</div> </div>
<div class="sw-stat-card"> <div class="sw-stat-card">
<div class="sw-stat-icon si-blue"> <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> <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> <div>
<div class="sw-stat-label">Sudah Dikerjakan</div> <div class="sw-stat-label">Sudah Dikerjakan</div>
@ -207,7 +550,12 @@
</div> </div>
<div class="sw-stat-card"> <div class="sw-stat-card">
<div class="sw-stat-icon si-green"> <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> <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> <div>
<div class="sw-stat-label">Mode</div> <div class="sw-stat-label">Mode</div>
@ -216,5 +564,5 @@
</div> </div>
</div> </div>
</div> </div>
@endsection @endsection

View File

@ -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');