bismillah gak ada yang error

bismillah
This commit is contained in:
Ryfandii 2026-05-03 09:21:55 +07:00
parent 4274e67e10
commit c0d7a893cf
73 changed files with 4268 additions and 450 deletions

View File

@ -29,35 +29,57 @@ public function login(Request $request)
if (Auth::attempt($credentials)) { if (Auth::attempt($credentials)) {
$request->session()->regenerate();
$user = Auth::user(); $user = Auth::user();
// ================= WA ================= // 🔥 HANYA JIKA PASSWORD DEFAULT
$telepon = $user->getTeleponLengkap(); if ($user->is_default_password) {
if ($telepon) { // ================= EMAIL =================
$no = preg_replace('/^0/', '62', $telepon); Mail::to($user->email)->send(new LoginNotification($user));
$pesan = "🔐 LOGIN BERHASIL\n"; // ================= WHATSAPP =================
$pesan .= "User: {$user->name}\n"; $telepon = $user->getTeleponLengkap();
$pesan .= "Email: {$user->email}\n";
$pesan .= "Waktu: " . now();
Http::asForm()->withHeaders([ if ($telepon) {
'Authorization' => env('FONNTE_TOKEN')
])->post('https://api.fonnte.com/send', [ // normalisasi nomor
'target' => $no, $no = preg_replace('/[^0-9]/', '', $telepon);
'message' => $pesan, if (substr($no, 0, 1) == '0') {
]); $no = '62' . substr($no, 1);
}
$pesan = "🔐 LOGIN PERTAMA\n";
$pesan .= "Nama: {$user->name}\n";
$pesan .= "Email: {$user->email}\n";
$pesan .= "Gunakan password default\n";
$pesan .= "Segera ganti password!\n";
$pesan .= "Waktu: " . now();
Http::asForm()->withHeaders([
'Authorization' => env('FONNTE_TOKEN')
])->post('https://api.fonnte.com/send', [
'target' => $no,
'message' => $pesan,
]);
}
} }
// ================= REDIRECT ROLE ================= // 🔥 REDIRECT ROLE
if ($user->role == 'admin') { if ($user->role == 'admin') {
return redirect('/admin'); return redirect()->route('admin.dashboard');
} elseif ($user->role == 'guru') {
return redirect('/guru');
} else {
return redirect('/siswa');
} }
if ($user->role == 'guru') {
return redirect()->route('guru.dashboard');
}
if ($user->role == 'siswa') {
return redirect()->route('siswa.dashboard');
}
return redirect('/login');
} }
return back()->with('error', 'Email atau password salah'); return back()->with('error', 'Email atau password salah');

View File

@ -19,7 +19,7 @@ public function dashboard()
// ================= INDEX ================= // ================= INDEX =================
public function index() public function index()
{ {
$guru = Guru::with('user')->get(); $guru = Guru::with(['user', 'mapel'])->get();
return view('admin.guru.index', compact('guru')); return view('admin.guru.index', compact('guru'));
} }
@ -32,42 +32,45 @@ public function create()
// ================= STORE ================= // ================= STORE =================
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([ $request->validate([
'nama' => 'required', 'nama' => 'required',
'nip' => 'required|unique:gurus,nip', 'nip' => 'required|unique:gurus,nip',
'mapel_id' => 'required', 'mapel_id' => 'required',
'alamat' => 'required', 'alamat' => 'required',
'telepon' => 'required', 'telepon' => 'required',
'email' => 'required|email|unique:users,email' 'email' => 'required|email|unique:users,email'
]); ]);
// 🔥 SIMPAN GURU // 🔥 CEK JIKA SUDAH ADA (ANTI DOUBLE INSERT)
$guru = Guru::create([ if (Guru::where('nip', $request->nip)->exists()) {
'nama' => $request->nama, return back()->withErrors(['nip' => 'NIP sudah ada'])->withInput();
'nip' => $request->nip,
'mapel' => MataPelajaran::find($request->mapel_id)->nama_mapel,
'alamat' => $request->alamat,
'telepon' => $request->telepon,
'status' => 'aktif'
]);
// 🔥 SIMPAN USER (FIX TELEPON MASUK)
$user = User::create([
'name' => $request->nama,
'email' => $request->email,
'password' => Hash::make('12345678'),
'role' => 'guru',
'guru_id' => $guru->id,
'mapel_id' => $request->mapel_id,
'telepon' => $request->telepon, // ✅ INI YANG PENTING
'is_default_password' => true
]);
return redirect()->route('admin.guru.index')
->with('success', 'Guru berhasil ditambahkan');
} }
$guru = Guru::create([
'nama' => $request->nama,
'nip' => $request->nip,
'mapel_id' => $request->mapel_id,
'alamat' => $request->alamat,
'telepon' => $request->telepon,
'status' => 'aktif'
]);
User::create([
'name' => $request->nama,
'email' => $request->email,
'password' => Hash::make('12345678'),
'role' => 'guru',
'guru_id' => $guru->id,
'mapel_id' => $request->mapel_id,
'telepon' => $request->telepon,
'is_default_password' => true
]);
return redirect()->route('admin.guru.index')
->with('success', 'Guru berhasil ditambahkan');
}
// ================= EDIT ================= // ================= EDIT =================
public function edit(Guru $guru) public function edit(Guru $guru)
{ {
@ -86,11 +89,10 @@ public function update(Request $request, Guru $guru)
'telepon' => 'required', 'telepon' => 'required',
]); ]);
// 🔥 UPDATE GURU $guru->update([
$guru->update([
'nama' => $request->nama, 'nama' => $request->nama,
'nip' => $request->nip, 'nip' => $request->nip,
'mapel' => MataPelajaran::find($request->mapel_id)->nama_mapel, 'mapel_id' => $request->mapel_id, // 🔥 FIX
'alamat' => $request->alamat, 'alamat' => $request->alamat,
'telepon' => $request->telepon, 'telepon' => $request->telepon,
]); ]);

View File

@ -8,6 +8,7 @@
use App\Models\Kelas; use App\Models\Kelas;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use App\Services\OtpService;
class SiswaController extends Controller class SiswaController extends Controller
{ {
@ -37,16 +38,24 @@ public function create()
// ================= STORE ================= // ================= STORE =================
public function store(Request $request) public function store(Request $request)
{ {
$request->validate([
'nama' => 'required', \Log::info('STORE DIPANGGIL');
'jenis_kelamin' => 'required',
'nama_ortu' => 'required', $request->validate([
'nis' => 'required|unique:siswa,nis', 'nama' => 'required',
'kelas_id' => 'required', 'jenis_kelamin' => 'required',
'alamat' => 'required', 'nama_ortu' => 'required',
'telepon' => 'required', 'nis' => 'required|unique:siswa,nis',
'email' => 'required|email|unique:users,email', 'kelas_id' => 'required',
]); 'alamat' => 'required',
'telepon' => 'required',
'email' => 'required|email|unique:users,email',
]);
// 2. CEK TAMBAHAN (ANTI DOUBLE SUBMIT)
if (User::where('email', $request->email)->exists()) {
return back()->with('error', 'Email sudah digunakan!');
}
$siswa = Siswa::create([ $siswa = Siswa::create([
'nama' => $request->nama, 'nama' => $request->nama,
@ -60,7 +69,7 @@ public function store(Request $request)
]); ]);
// simpan user login // simpan user login
User::create([ $user = User::create([
'name' => $request->nama, 'name' => $request->nama,
'email' => $request->email, 'email' => $request->email,
'password' => Hash::make('12345678'), // 🔥 default 'password' => Hash::make('12345678'), // 🔥 default
@ -69,6 +78,9 @@ public function store(Request $request)
'is_default_password' => true // 🔥 WAJIB 'is_default_password' => true // 🔥 WAJIB
]); ]);
/** @var \App\Models\User $user */
app(OtpService::class)->send($user);
return redirect()->route('admin.siswa.index') return redirect()->route('admin.siswa.index')
->with('success', 'Siswa + akun login berhasil dibuat'); ->with('success', 'Siswa + akun login berhasil dibuat');
} }
@ -120,11 +132,18 @@ public function destroy(Siswa $siswa)
} }
// ================= NONAKTIF ================= // ================= NONAKTIF =================
public function nonaktif($id) public function nonaktif(Request $request, $id)
{ {
$request->validate([
'alasan' => 'required|min:5'
]);
$siswa = Siswa::findOrFail($id); $siswa = Siswa::findOrFail($id);
$siswa->status = 'nonaktif';
$siswa->save(); $siswa->update([
'status' => 'nonaktif',
'alasan_nonaktif' => $request->alasan
]);
return back()->with('success', 'Siswa dinonaktifkan'); return back()->with('success', 'Siswa dinonaktifkan');
} }

View File

@ -0,0 +1,99 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class OtpController extends Controller
{
// ================= KIRIM OTP =================
public function sendOtp(Request $request)
{
$user = User::where('email', $request->email)->first();
if (!$user) {
return back()->with('error', 'Email tidak ditemukan');
}
app(\App\Services\OtpService::class)->send($user);
return back()->with('success', 'OTP berhasil dikirim');
}
// ================= LOGIN =================
public function loginOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
'otp' => 'nullable|digits:6'
]);
$user = \App\Models\User::where('email', $request->email)->first();
if (!$user) {
return back()->with('error', 'User tidak ditemukan');
}
// 🔐 CEK PASSWORD
if (!\Hash::check($request->password, $user->password)) {
return back()->with('error', 'Password salah');
}
// =========================
// 🔥 KHUSUS GURU & SISWA WAJIB OTP
// =========================
if (in_array($user->role, ['guru', 'siswa'])) {
if (!$request->otp) {
return back()->with('error', 'OTP wajib diisi');
}
if (!$user->otp) {
return back()->with('error', 'OTP belum diminta');
}
if ((string)$request->otp !== (string)$user->otp) {
return back()->with('error', 'OTP salah');
}
if (!$user->otp_expired_at || now()->gt($user->otp_expired_at)) {
return back()->with('error', 'OTP sudah kadaluarsa');
}
}
// =========================
// ✅ LOGIN
// =========================
\Auth::login($user);
// 🔥 HAPUS OTP SETELAH DIPAKAI (hanya untuk guru & siswa)
if (in_array($user->role, ['guru', 'siswa'])) {
$user->update([
'otp' => null,
'otp_expired_at' => null
]);
}
// =========================
// 🔁 REDIRECT ROLE
// =========================
if ($user->role == 'admin') {
return redirect()->route('admin.dashboard');
}
if ($user->role == 'guru') {
return redirect()->route('guru.dashboard');
}
if ($user->role == 'siswa') {
return redirect()->route('siswa.dashboard');
}
return redirect('/');
}
}

View File

@ -1,14 +1,130 @@
<?php <?php
namespace App\Http\Controllers\guru; namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Absensi;
use App\Models\AbsensiDetail;
use App\Models\Kelas;
use App\Models\MataPelajaran;
use App\Models\Siswa;
use Carbon\Carbon;
class AbsensiController extends Controller class AbsensiController extends Controller
{ {
public function index() public function index(Request $request)
{ {
return view('guru.absensi'); $kelas = Kelas::all();
$mapel = MataPelajaran::find(auth()->user()->guru->mapel_id);
// 🔥 VALIDASI DI SINI
if (!$mapel) {
return back()->with('error', 'Mapel guru belum diatur!');
} }
$siswa = null;
$kelasAktif = null;
$riwayat = null;
if ($request->kelas_id && $request->mapel_id) {
$kelasAktif = Kelas::findOrFail($request->kelas_id);
$siswa = Siswa::where('kelas_id', $request->kelas_id)->get();
// 🔥 ambil absensi hari ini
$absensi = Absensi::where('kelas_id', $request->kelas_id)
->where('mapel_id', $request->mapel_id)
->whereDate('tanggal', now())
->first();
if ($absensi) {
$riwayat = AbsensiDetail::with('siswa')
->where('absensi_id', $absensi->id)
->get();
}
}
return view('guru.absensi.index', compact(
'kelas',
'mapel',
'siswa',
'kelasAktif',
'riwayat'
));
}
public function buka(Request $request)
{
$request->validate([
'kelas_id' => 'required',
'mapel_id' => 'required'
]);
$absensi = Absensi::create([
'guru_id' => auth()->user()->guru->id,
'kelas_id' => $request->kelas_id,
'mapel_id' => $request->mapel_id,
'tanggal' => now(),
]);
return redirect()->route('guru.absensi.kelola', $absensi->id);
}
public function kelola($id)
{
$absensi = Absensi::findOrFail($id);
$siswa = Siswa::where('kelas_id', $absensi->kelas_id)->get();
return view('guru.absensi.kelola', compact('absensi', 'siswa'));
}
public function simpan(Request $request)
{
$request->validate([
'kelas_id' => 'required',
'mapel_id' => 'required',
'absensi' => 'required|array'
]);
// 🔥 CEK / BUAT absensi hari ini
$absensi = Absensi::firstOrCreate([
'kelas_id' => $request->kelas_id,
'mapel_id' => $request->mapel_id,
'tanggal' => now()->toDateString(),
], [
'guru_id' => auth()->user()->guru->id
]);
// 🔥 SIMPAN DETAIL
foreach ($request->absensi as $siswaId => $data) {
AbsensiDetail::updateOrCreate(
[
'absensi_id' => $absensi->id,
'siswa_id' => $siswaId
],
[
'status' => $data['status'],
'keterangan' => $data['keterangan'] ?? null
]
);
}
return redirect()->back()->with('success', 'Absensi berhasil disimpan');
}
public function tutup($id)
{
Absensi::findOrFail($id)->update(['dibuka' => false]);
return back()->with('success', 'Absensi ditutup');
}
public function delete($id)
{
AbsensiDetail::findOrFail($id)->delete();
return back()->with('success', 'Data dihapus');
}
} }

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\Jadwal;
use Illuminate\Http\Request;
class MapelController extends Controller
{
public function getByKelas($id)
{
try {
$mapel = Jadwal::where('kelas_id', $id)
->join('mata_pelajarans', 'jadwals.mata_pelajaran_id', '=', 'mata_pelajarans.id')
->select('mata_pelajarans.id', 'mata_pelajarans.nama_mapel')
->distinct()
->get();
return response()->json($mapel);
} catch (\Exception $e) {
return response()->json([
'error' => $e->getMessage()
], 500);
}
}
}

View File

@ -0,0 +1,175 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Tugas;
use App\Models\Kelas;
use App\Models\MataPelajaran;
use App\Models\PengumpulanTugas;
use App\Models\Jadwal; // 🔥 WAJIB
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
use App\Models\Siswa;
class TugasController extends Controller
{
public function index()
{
$guru = auth()->user()->guru;
$tugas = Tugas::with(['kelas', 'mapel'])
->withCount('pengumpulan') // 🔥 hitung jumlah yang kumpul
->where('guru_id', $guru->id)
->get();
// 🔥 tambahkan total siswa per kelas
foreach ($tugas as $t) {
$t->total_siswa = Siswa::where('kelas_id', $t->kelas_id)->count();
}
return view('guru.tugas.index', compact('tugas'));
}
public function create()
{
$kelas = Kelas::all();
$guru = auth()->user()->guru;
$mapel = $guru->mapelRel;
return view('guru.tugas.create', compact('kelas', 'mapel'));
}
public function store(Request $request)
{
$guru = auth()->user()->guru;
$request->validate([
'kelas_id' => 'required',
'judul' => 'required',
'deadline' => 'required',
'file' => 'nullable|mimes:pdf,doc,docx,ppt,pptx|max:2048'
]);
$filePath = null;
// 🔥 INI BAGIAN PALING PENTING
if ($request->hasFile('file')) {
$filePath = $request->file('file')->store('tugas_file', 'public');
}
Tugas::create([
'guru_id' => $guru->id,
'kelas_id' => $request->kelas_id,
'mapel_id' => $guru->mapel_id,
'judul' => $request->judul,
'deskripsi' => $request->deskripsi,
'file' => $filePath,
'deadline' => str_replace('T', ' ', $request->deadline),
]);
return redirect()->route('guru.tugas.index')
->with('success', 'Tugas berhasil dibuat');
}
public function getMapelByKelas($id)
{
$guruId = auth()->user()->guru->id;
$mapel = Jadwal::where('kelas_id', $id)
->where('guru_id', $guruId)
->join('mata_pelajarans', 'jadwals.mata_pelajaran_id', '=', 'mata_pelajarans.id')
->select('mata_pelajarans.id', 'mata_pelajarans.nama_mapel')
->distinct()
->get();
return response()->json($mapel);
}
public function show($id)
{
$tugas = Tugas::with(['kelas', 'mapel'])->findOrFail($id);
return view('guru.tugas.show', compact('tugas'));
}
public function edit($id)
{
$tugas = Tugas::findOrFail($id);
$kelas = Kelas::all();
return view('guru.tugas.edit', compact('tugas', 'kelas'));
}
public function update(Request $request, $id)
{
$request->validate([
'judul' => 'required',
'kelas_id' => 'required',
'deadline' => 'required'
]);
$tugas = Tugas::findOrFail($id);
$tugas->update([
'judul' => $request->judul,
'kelas_id' => $request->kelas_id,
'deskripsi' => $request->deskripsi,
'deadline' => $request->deadline,
]);
return redirect()->route('guru.tugas.index')
->with('success', 'Tugas berhasil diupdate');
}
public function download($id)
{
$tugas = Tugas::findOrFail($id);
if (!$tugas->file) {
abort(404, 'File tidak ditemukan');
}
$path = storage_path('app/public/' . $tugas->file);
if (!file_exists($path)) {
abort(404, 'File tidak ada di storage');
}
return response()->download($path);
}
public function pengumpulan($id)
{
$tugas = Tugas::with(['pengumpulan.siswa'])->findOrFail($id);
$deadline = Carbon::parse($tugas->deadline);
foreach ($tugas->pengumpulan as $p) {
$waktu = Carbon::parse($p->created_at);
if ($waktu->gt($deadline)) {
$p->status = 'telat';
} else {
$p->status = 'tepat';
}
}
return view('guru.tugas.pengumpulan', compact('tugas'));
}
public function nilai(Request $request, $id)
{
$request->validate([
'nilai' => 'required|integer|min:0|max:100',
'komentar' => 'nullable'
]);
$pengumpulan = \App\Models\PengumpulanTugas::findOrFail($id);
$pengumpulan->update([
'nilai' => $request->nilai,
'komentar' => $request->komentar
]);
return back()->with('success', 'Nilai berhasil disimpan');
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Ujian;
use App\Models\Soal;
use Illuminate\Support\Facades\Auth;
class UjianController extends Controller
{
public function index()
{
$data = Ujian::where('guru_id', auth()->id())->latest()->get();
return view('guru.ujian.index', compact('data'));
}
public function create()
{
$user = Auth::user();
// safety check
if (!$user) {
abort(403, 'Belum login');
}
$mapel = $user->mapel; // ✔ relasi sudah benar
return view('guru.ujian.create', compact('mapel'));
}
public function store(Request $request)
{
$request->validate([
'judul' => 'required',
'soal' => 'required|array'
]);
// 🔥 SIMPAN UJIAN
$ujian = Ujian::create([
'judul' => $request->judul,
'durasi' => $request->durasi,
'mulai' => $request->mulai,
'selesai' => $request->selesai,
'guru_id' => auth()->user()->guru_id
]);
// 🔥 SIMPAN SOAL
foreach ($request->soal as $s) {
Soal::create([
'ujian_id' => $ujian->id,
'pertanyaan' => $s['pertanyaan'],
'a' => $s['a'],
'b' => $s['b'],
'c' => $s['c'],
'd' => $s['d'],
'jawaban_benar' => $s['jawaban'],
]);
}
return redirect()->route('guru.ujian.index')
->with('success', 'Ujian + soal berhasil dibuat');
}
public function soal($id)
{
$ujian = Ujian::findOrFail($id);
return view('guru.ujian.soal', compact('ujian'));
}
public function storeSoal(Request $request, $id)
{
$request->validate([
'soal' => 'required|array',
'soal.*.pertanyaan' => 'required|string',
'soal.*.a' => 'required|string',
'soal.*.b' => 'required|string',
'soal.*.c' => 'required|string',
'soal.*.d' => 'required|string',
'soal.*.jawaban' => 'required|in:a,b,c,d',
]);
foreach ($request->soal as $s) {
Soal::create([
'ujian_id' => $id,
'pertanyaan' => $s['pertanyaan'],
'a' => $s['a'],
'b' => $s['b'],
'c' => $s['c'],
'd' => $s['d'],
'jawaban_benar' => $s['jawaban'],
]);
}
return back()->with('success','Soal disimpan');
}
}

View File

@ -1,17 +1,82 @@
<?php <?php
namespace App\Http\Controllers\Siswa; namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Absensi; use App\Models\Absensi;
use Illuminate\Support\Facades\Auth; use App\Models\AbsensiDetail;
use Carbon\Carbon;
class AbsensiController extends Controller class AbsensiController extends Controller
{ {
public function index() // 🔹 LIST ABSENSI AKTIF
{ public function index()
$user = Auth::user(); {
$siswa = auth()->user()->siswa;
$absensi = Absensi::where('siswa_id', $user->siswa_id)->get(); $absensis = Absensi::with(['mapel', 'guru'])
->where('kelas_id', $siswa->kelas_id)
->where('dibuka', true)
->where(function ($q) {
$q->whereNull('waktu_selesai')
->orWhere('waktu_selesai', '>', now());
})
->latest()
->get();
return view('siswa.absensi.index', compact('absensi')); // 🔥 GROUP BY MAPEL (INI YANG KURANG)
} $groupedAbsensi = $absensis->groupBy(function ($item) {
return $item->mapel->nama_mapel ?? 'Tanpa Mapel';
});
// 🔥 CEK SUDAH ABSEN
$sudahAbsen = AbsensiDetail::where('siswa_id', $siswa->id)
->pluck('absensi_id')
->toArray();
return view('siswa.absensi.index', compact('groupedAbsensi', 'sudahAbsen'));
}
// 🔹 SCAN QR
public function scan($token)
{
$absensi = Absensi::where('token', $token)->firstOrFail();
return view('siswa.absensi.scan', compact('absensi'));
}
// 🔹 SUBMIT HADIR
public function hadir(Request $request)
{
$request->validate([
'absensi_id' => 'required|exists:absensi,id'
]);
$absensi = Absensi::findOrFail($request->absensi_id);
// 🔥 VALIDASI WAKTU
if ($absensi->waktu_selesai && now()->greaterThan($absensi->waktu_selesai)) {
return back()->with('error', 'Waktu absensi sudah habis!');
}
$siswa = auth()->user()->siswa;
$cek = AbsensiDetail::where('absensi_id', $absensi->id)
->where('siswa_id', $siswa->id)
->exists();
if ($cek) {
return back()->with('error', 'Kamu sudah absen!');
}
AbsensiDetail::create([
'absensi_id' => $absensi->id,
'siswa_id' => $siswa->id,
'status' => 'hadir',
'waktu_absen' => now()
]);
return back()->with('success', 'Absensi berhasil!');
}
} }

View File

@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Tugas;
use App\Models\PengumpulanTugas;
use Carbon\Carbon;
class TugasController extends Controller
{
public function index()
{
$siswa = auth()->user()->siswa;
$tugas = Tugas::with('mapel')
->where('kelas_id', $siswa->kelas_id)
->get();
foreach ($tugas as $t) {
$pengumpulan = \App\Models\PengumpulanTugas::where('tugas_id', $t->id)
->where('siswa_id', $siswa->id)
->first();
$deadline = \Carbon\Carbon::parse($t->deadline);
$t->waktu_kumpul = null;
$t->nilai = null;
$t->komentar = null;
$t->status = 'belum';
if ($pengumpulan) {
$t->waktu_kumpul = $pengumpulan->created_at;
$t->nilai = $pengumpulan->nilai;
$t->komentar = $pengumpulan->komentar;
if ($pengumpulan->created_at > $deadline) {
$t->status = 'telat';
} else {
$t->status = 'tepat';
}
} else {
if (now()->gt($deadline)) {
$t->status = 'lewat';
}
}
}
return view('siswa.tugas.index', compact('tugas'));
}
public function kumpul($id)
{
$tugas = Tugas::findOrFail($id);
if (now()->gt($tugas->deadline)) {
return redirect()->route('siswa.tugas.index')
->with('error', 'Deadline sudah lewat, tidak bisa mengumpulkan tugas');
}
return view('siswa.tugas.kumpul', compact('tugas'));
}
public function store(Request $request, $id)
{
$tugas = Tugas::findOrFail($id);
// 🔥 BLOCK UTAMA
if (now()->gt($tugas->deadline)) {
return redirect()->route('siswa.tugas.index')
->with('error', 'Tugas sudah melewati deadline');
}
$request->validate([
'file' => 'required|mimes:pdf,doc,docx|max:2048'
]);
$filePath = $request->file('file')->store('jawaban_tugas', 'public');
PengumpulanTugas::create([
'tugas_id' => $id,
'siswa_id' => auth()->user()->siswa->id,
'file' => $filePath
]);
return redirect()->route('siswa.tugas.index')
->with('success', 'Tugas berhasil dikumpulkan');
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Ujian;
use App\Models\Soal;
use App\Models\Jawaban;
use App\Models\Hasil;
class UjianController extends Controller
{
public function index()
{
$ujians = Ujian::all();
return view('siswa.ujian.index', compact('ujians'));
}
public function kerjakan($id)
{
$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)
{
$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 = ($benar / $total) * 100;
return view('siswa.ujian.hasil', [
'nilai' => $nilai,
'benar' => $benar,
'total' => $total
]);
}
}

45
app/Models/Absensi.php Normal file
View File

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Absensi extends Model
{
use HasFactory;
protected $table = 'absensi';
protected $fillable = [
'guru_id',
'kelas_id',
'tanggal',
'token',
'dibuka',
'mapel_id',
'waktu_selesai'// 🔥 WAJIB TAMBAHKAN
];
// 🔥 RELASI
public function detail()
{
return $this->hasMany(AbsensiDetail::class);
}
public function guru()
{
return $this->belongsTo(Guru::class);
}
public function kelas()
{
return $this->belongsTo(Kelas::class);
}
public function mapel()
{
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class AbsensiDetail extends Model
{
use HasFactory;
protected $table = 'absensi_detail';
protected $fillable = [
'absensi_id', // 🔥 WAJIB
'siswa_id', // 🔥 WAJIB
'status', // 🔥 hadir / izin / alpha
'waktu_absen' // 🔥 timestamp
];
public function absensi()
{
return $this->belongsTo(Absensi::class);
}
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

View File

@ -10,7 +10,7 @@ class Guru extends Model
use HasFactory; use HasFactory;
protected $table = 'gurus'; // nama tabel protected $table = 'gurus'; // nama tabel
protected $fillable = ['nama', 'nip', 'mapel','alamat','telepon','status']; protected $fillable = ['nama', 'nip','alamat','telepon','status', 'mapel_id'];
public function user() public function user()
{ {
return $this->hasOne(User::class, 'guru_id'); return $this->hasOne(User::class, 'guru_id');
@ -20,5 +20,15 @@ public function jadwal()
{ {
return $this->hasMany(\App\Models\Jadwal::class); return $this->hasMany(\App\Models\Jadwal::class);
} }
public function mapel()
{
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
}
public function mapelRel()
{
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
}
} }

16
app/Models/Hasil.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Hasil extends Model
{
protected $fillable = ['user_id','ujian_id','nilai'];
public function ujian()
{
return $this->belongsTo(Ujian::class);
}
}

View File

@ -2,7 +2,7 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; // 🔥 INI WAJIB! use Illuminate\Database\Eloquent\Model;
class Jadwal extends Model class Jadwal extends Model
{ {
@ -25,8 +25,8 @@ public function mapel()
return $this->belongsTo(MataPelajaran::class, 'mata_pelajaran_id'); return $this->belongsTo(MataPelajaran::class, 'mata_pelajaran_id');
} }
public function guru() public function guru()
{ {
return $this->belongsTo(Guru::class); return $this->belongsTo(Guru::class, 'guru_id');
} }
} }

16
app/Models/Jawaban.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Jawaban extends Model
{
protected $fillable = ['user_id','soal_id','jawaban'];
public function soal()
{
return $this->belongsTo(Soal::class);
}
}

View File

@ -13,4 +13,9 @@ class MataPelajaran extends Model
'kode_mapel', 'kode_mapel',
'jam_pelajaran' 'jam_pelajaran'
]; ];
public function jadwal()
{
return $this->hasMany(Jadwal::class, 'mata_pelajaran_id');
}
} }

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Tugas;
use App\Models\Siswa;
class PengumpulanTugas extends Model
{
protected $table = 'pengumpulan_tugas';
protected $fillable = [
'tugas_id',
'siswa_id',
'file',
'nilai',
'komentar'
];
// RELASI
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
public function tugas()
{
return $this->belongsTo(Tugas::class);
}
}

View File

@ -16,7 +16,8 @@ class Siswa extends Model
'kelas_id', 'kelas_id',
'alamat', 'alamat',
'telepon', 'telepon',
'status' 'status',
'alasan_nonaktif'
]; ];
public $timestamps = true; public $timestamps = true;

18
app/Models/Soal.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Soal extends Model
{
protected $fillable = [
'ujian_id','pertanyaan','a','b','c','d','jawaban_benar'
];
public function ujian()
{
return $this->belongsTo(Ujian::class);
}
}

43
app/Models/Tugas.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Guru;
use App\Models\Kelas;
use App\Models\MataPelajaran;
use App\Models\PengumpulanTugas;
class Tugas extends Model
{
protected $fillable = [
'guru_id',
'kelas_id',
'mapel_id',
'judul',
'deskripsi',
'file', // 🔥 WAJIB ADA
'deadline'
];
// RELASI
public function kelas()
{
return $this->belongsTo(Kelas::class, 'kelas_id'); // 🔥 WAJIB ADA INI
}
public function mapel()
{
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
}
public function guru()
{
return $this->belongsTo(Guru::class, 'guru_id');
}
public function pengumpulan()
{
return $this->hasMany(\App\Models\PengumpulanTugas::class, 'tugas_id');
}
}

20
app/Models/Ujian.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Ujian extends Model
{ protected $fillable = ['judul','durasi','mulai','selesai','guru_id'];
public function soals()
{
return $this->hasMany(Soal::class);
}
public function guru()
{
return $this->belongsTo(User::class, 'guru_id');
}
}

View File

@ -27,6 +27,8 @@ class User extends Authenticatable
'siswa_id', 'siswa_id',
'mapel_id', 'mapel_id',
'photo', 'photo',
'otp',
'otp_expired_at',
'telepon', // 🔥 WAJIB TAMBAH INI 'telepon', // 🔥 WAJIB TAMBAH INI
'is_default_password' 'is_default_password'
]; ];

View File

@ -0,0 +1,72 @@
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class OtpService
{
public function send(User $user)
{
$otp = rand(100000, 999999);
// simpan OTP
$user->update([
'otp' => $otp,
'otp_expired_at' => now()->addMinutes(5)
]);
// ================= EMAIL =================
\Mail::raw("Kode OTP kamu: $otp", function ($msg) use ($user) {
$msg->to($user->email)
->subject('Kode OTP Login');
});
// ================= AMBIL TELEPON =================
$telepon = null;
if ($user->siswa && $user->siswa->telepon) {
$telepon = $user->siswa->telepon;
} elseif ($user->guru && $user->guru->telepon) {
$telepon = $user->guru->telepon;
} elseif ($user->telepon) {
$telepon = $user->telepon;
}
// ================= KIRIM WA =================
if ($telepon) {
$this->sendWA($telepon, $otp);
} else {
\Log::error('NO HP TIDAK ADA UNTUK USER', [
'user_id' => $user->id,
'role' => $user->role
]);
}
}
private function sendWA($no, $otp)
{
$no = preg_replace('/[^0-9]/', '', $no);
if (substr($no, 0, 1) === '0') {
$no = '62' . substr($no, 1);
}
$token = env('FONNTE_TOKEN');
$response = Http::asForm()->withHeaders([
'Authorization' => $token
])->post('https://api.fonnte.com/send', [
'target' => $no,
'message' => "Kode OTP kamu: $otp"
]);
Log::info('WA RESULT', [
'nomor' => $no,
'response' => $response->json()
]);
}
}

View File

@ -8,7 +8,8 @@
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"laravel/framework": "^11.31", "laravel/framework": "^11.31",
"laravel/tinker": "^2.9" "laravel/tinker": "^2.9",
"simplesoftwareio/simple-qrcode": "^4.2"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",

192
composer.lock generated
View File

@ -4,8 +4,68 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d6c083b04e73afd8ea1cc978bddab18d", "content-hash": "ba0b871c4e08ecaa364989ce5e6f5138",
"packages": [ "packages": [
{
"name": "bacon/bacon-qr-code",
"version": "2.0.8",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"dasprid/enum": "^1.0.3",
"ext-iconv": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phly/keep-a-changelog": "^2.1",
"phpunit/phpunit": "^7 | ^8 | ^9",
"spatie/phpunit-snapshot-assertions": "^4.2.9",
"squizlabs/php_codesniffer": "^3.4"
},
"suggest": {
"ext-imagick": "to generate QR code images"
},
"type": "library",
"autoload": {
"psr-4": {
"BaconQrCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "BaconQrCode is a QR code generator for PHP.",
"homepage": "https://github.com/Bacon/BaconQrCode",
"support": {
"issues": "https://github.com/Bacon/BaconQrCode/issues",
"source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
},
"time": "2022-12-07T17:46:57+00:00"
},
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.12.3", "version": "0.12.3",
@ -135,6 +195,62 @@
], ],
"time": "2024-02-09T16:56:22+00:00" "time": "2024-02-09T16:56:22+00:00"
}, },
{
"name": "dasprid/enum",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
"reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=7.1 <9.0"
},
"require-dev": {
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "*"
},
"type": "library",
"autoload": {
"psr-4": {
"DASPRiD\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "PHP 7.1 enum implementation",
"keywords": [
"enum",
"map"
],
"support": {
"issues": "https://github.com/DASPRiD/Enum/issues",
"source": "https://github.com/DASPRiD/Enum/tree/1.0.7"
},
"time": "2025-09-16T12:23:56+00:00"
},
{ {
"name": "dflydev/dot-access-data", "name": "dflydev/dot-access-data",
"version": "v3.0.3", "version": "v3.0.3",
@ -3272,6 +3388,80 @@
}, },
"time": "2025-06-25T14:20:11+00:00" "time": "2025-06-25T14:20:11+00:00"
}, },
{
"name": "simplesoftwareio/simple-qrcode",
"version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/SimpleSoftwareIO/simple-qrcode.git",
"reference": "916db7948ca6772d54bb617259c768c9cdc8d537"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SimpleSoftwareIO/simple-qrcode/zipball/916db7948ca6772d54bb617259c768c9cdc8d537",
"reference": "916db7948ca6772d54bb617259c768c9cdc8d537",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"bacon/bacon-qr-code": "^2.0",
"ext-gd": "*",
"php": ">=7.2|^8.0"
},
"require-dev": {
"mockery/mockery": "~1",
"phpunit/phpunit": "~9"
},
"suggest": {
"ext-imagick": "Allows the generation of PNG QrCodes.",
"illuminate/support": "Allows for use within Laravel."
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"QrCode": "SimpleSoftwareIO\\QrCode\\Facades\\QrCode"
},
"providers": [
"SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"SimpleSoftwareIO\\QrCode\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Simple Software LLC",
"email": "support@simplesoftware.io"
}
],
"description": "Simple QrCode is a QR code generator made for Laravel.",
"homepage": "https://www.simplesoftware.io/#/docs/simple-qrcode",
"keywords": [
"Simple",
"generator",
"laravel",
"qrcode",
"wrapper"
],
"support": {
"issues": "https://github.com/SimpleSoftwareIO/simple-qrcode/issues",
"source": "https://github.com/SimpleSoftwareIO/simple-qrcode/tree/4.2.0"
},
"time": "2021-02-08T20:43:55+00:00"
},
{ {
"name": "symfony/clock", "name": "symfony/clock",
"version": "v7.3.0", "version": "v7.3.0",

View File

@ -65,7 +65,7 @@
| |
*/ */
'timezone' => env('APP_TIMEZONE', 'UTC'), 'timezone' => 'Asia/Jakarta',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Hasil>
*/
class HasilFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Jawaban>
*/
class JawabanFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Soal>
*/
class SoalFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Ujian>
*/
class UjianFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tugas', function (Blueprint $table) {
$table->id();
$table->foreignId('guru_id')->constrained()->cascadeOnDelete();
$table->foreignId('kelas_id')->constrained()->cascadeOnDelete();
$table->foreignId('mapel_id')->constrained()->cascadeOnDelete();
$table->string('judul');
$table->text('deskripsi');
$table->date('deadline');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tugas');
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('pengumpulan_tugas', function (Blueprint $table) {
$table->id();
$table->foreignId('tugas_id')->constrained()->cascadeOnDelete();
$table->foreign('siswa_id')->references('id')->on('siswa')->onDelete('cascade');
$table->string('file')->nullable();
$table->integer('nilai')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('pengumpulan_tugas');
}
};

View File

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('gurus', function (Blueprint $table) {
$table->string('mapel')->nullable();
});
}
public function down()
{
Schema::table('gurus', function (Blueprint $table) {
$table->dropColumn('mapel');
});
}
};

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('tugas', function (Blueprint $table) {
$table->string('file')->nullable();
});
}
public function down()
{
Schema::table('tugas', function (Blueprint $table) {
$table->dropColumn('file');
});
}
};

View File

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('tugas', function (Blueprint $table) {
$table->dateTime('deadline')->change();
});
}
public function down()
{
Schema::table('tugas', function (Blueprint $table) {
$table->date('deadline')->change();
});
}
};

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('pengumpulan_tugas', function (Blueprint $table) {
if (!Schema::hasColumn('pengumpulan_tugas', 'komentar')) {
$table->text('komentar')->nullable();
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('pengumpulan_tugas', function (Blueprint $table) {
//
});
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('absensi', function (Blueprint $table) {
$table->id();
$table->foreignId('guru_id')->constrained()->cascadeOnDelete();
$table->foreignId('kelas_id')->constrained()->cascadeOnDelete();
$table->foreignId('mapel_id')->constrained()->cascadeOnDelete();
$table->date('tanggal');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('absensi');
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('absensi_detail', function (Blueprint $table) {
$table->id();
$table->foreignId('absensi_id')->constrained('absensi')->cascadeOnDelete();
$table->foreignId('siswa_id')->constrained()->cascadeOnDelete();
$table->enum('status', ['hadir', 'izin', 'sakit', 'alpha'])->default('hadir');
$table->text('keterangan')->nullable(); // 🔥 WAJIB
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('absensi_detail');
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('siswa', function (Blueprint $table) {
$table->text('alasan_nonaktif')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('siswa', function (Blueprint $table) {
//
});
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jawabans', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jawabans');
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('ujians', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('ujians');
}
};

View File

@ -0,0 +1,23 @@
<?php
// database/migrations/xxxx_xx_xx_create_hasils_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('hasils', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('ujian_id')->constrained('ujians')->cascadeOnDelete();
$table->unsignedInteger('nilai');
$table->timestamps();
$table->unique(['user_id','ujian_id']); // 1 hasil per ujian
});
}
public function down(): void {
Schema::dropIfExists('hasils');
}
};

View File

@ -0,0 +1,23 @@
<?php
// database/migrations/xxxx_xx_xx_create_jawabans_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('jawabans', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('soal_id')->constrained('soals')->cascadeOnDelete();
$table->enum('jawaban', ['a','b','c','d'])->nullable();
$table->timestamps();
$table->unique(['user_id','soal_id']); // 1 siswa 1 jawaban/soal
});
}
public function down(): void {
Schema::dropIfExists('jawabans');
}
};

View File

@ -0,0 +1,25 @@
<?php
// database/migrations/xxxx_xx_xx_create_soals_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('soals', function (Blueprint $table) {
$table->id();
$table->foreignId('ujian_id')->constrained('ujians')->cascadeOnDelete();
$table->text('pertanyaan');
$table->string('a');
$table->string('b');
$table->string('c');
$table->string('d');
$table->enum('jawaban_benar', ['a','b','c','d']);
$table->timestamps();
});
}
public function down(): void {
Schema::dropIfExists('soals');
}
};

View File

@ -0,0 +1,23 @@
<?php
// database/migrations/xxxx_xx_xx_create_ujians_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('ujians', function (Blueprint $table) {
$table->id();
$table->string('judul');
$table->foreignId('guru_id')->constrained('users')->cascadeOnDelete();
$table->unsignedInteger('durasi')->nullable(); // menit
$table->timestamp('mulai')->nullable();
$table->timestamp('selesai')->nullable();
$table->timestamps();
});
}
public function down(): void {
Schema::dropIfExists('ujians');
}
};

View File

@ -82,7 +82,7 @@
Kembali Kembali
</a> </a>
<button class="btn btn-success"> <button type="submit" class="btn btn-success" onclick="this.disabled=true; this.form.submit();">
Simpan Simpan
</button> </button>
</div> </div>

View File

@ -204,7 +204,7 @@
<td> <td>
<span class="badge badge-mapel"> <span class="badge badge-mapel">
{{ $g->mapel }} {{ $g->mapel->nama_mapel ?? '-' }}
</span> </span>
</td> </td>

View File

@ -89,9 +89,10 @@
Kembali Kembali
</a> </a>
<button class="btn btn-success"> <button type="submit" class="btn btn-success"
Simpan onclick="this.disabled=true; this.innerText='Menyimpan...'; this.form.submit();">
</button> Simpan
</button>
</div> </div>
</form> </form>

View File

@ -251,10 +251,18 @@ class="btn btn-warning btn-sm">
</a> </a>
@if($item->status == 'aktif') @if($item->status == 'aktif')
<a href="{{ route('admin.siswa.nonaktif', $item->id) }}" <form action="{{ route('admin.siswa.nonaktif', $item->id) }}" method="POST" class="d-flex gap-1">
class="btn btn-danger btn-sm"> @csrf
Nonaktif
</a> <input type="text" name="alasan"
placeholder="Alasan..."
required
style="width:120px; font-size:12px;">
<button class="btn btn-danger btn-sm">
Nonaktif
</button>
</form>
@else @else
<a href="{{ route('admin.siswa.aktifkan', $item->id) }}" <a href="{{ route('admin.siswa.aktifkan', $item->id) }}"
class="btn btn-success btn-sm"> class="btn btn-success btn-sm">

View File

@ -242,19 +242,33 @@
</div> </div>
@endif @endif
<form method="POST" action="/login"> {{-- FORM KIRIM OTP --}}
<form method="POST" action="{{ route('send.otp') }}">
@csrf @csrf
<input type="email" name="email" placeholder="Enter Email" required> <input type="email" name="email" placeholder="Email untuk OTP" required>
<input type="password" name="password" placeholder="Enter Password" required>
<button type="submit" class="btn-login">Login</button> <button type="submit" style="margin-bottom:10px;">
🔑 Dapatkan OTP
<div class="forgot"> </button>
<a href="/forgot-password">Forgot Password?</a>
</div>
</form> </form>
{{-- FORM LOGIN --}}
<form method="POST" action="{{ route('login.otp') }}">
@csrf
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<input type="text" name="otp" placeholder="Masukkan OTP">
<button type="submit">Login</button>
</form>
<div style="margin-top:10px;">
<a href="{{ route('password.request') }}" style="font-size:14px; color:#fff;">
Lupa Password?
</a>
</div>
</div> </div>
</div> </div>

View File

@ -15,14 +15,20 @@
<div class="alert alert-success">{{ session('success') }}</div> <div class="alert alert-success">{{ session('success') }}</div>
@endif @endif
<form method="POST" action="{{ route('reset.password') }}"> <form method="POST" action="{{ route('password.update') }}">
@csrf @csrf
<input type="password" name="password" class="form-control mb-2" placeholder="Password baru" required> <input type="hidden" name="email" value="{{ request('email') }}">
<input type="password" name="password_confirmation" class="form-control mb-3" placeholder="Konfirmasi password" required> <div>
<input type="password" name="password" placeholder="Password baru" required>
</div>
<button class="btn btn-warning w-100">Reset Password</button> <div>
<input type="password" name="password_confirmation" placeholder="Konfirmasi password" required>
</div>
<button type="submit">Reset Password</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -63,20 +63,7 @@
</a> </a>
</li> </li>
<!-- <li class="nav-item">
<a href="{{ route('admin.jadwal.grid') }}" class="nav-link">
<i class="fas fa-th"></i>
<span>Jadwal Grid 🔥</span>
</a>
</li> -->
<!-- <li class="nav-item">
<a href="#" class="nav-link text-danger"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
<i class="fas fa-sign-out-alt"></i>
<span>Logout</span>
</a>
</li> -->
</ul> </ul>
@ -84,13 +71,13 @@
{{-- ================= GURU (NAVBAR MOBILE) ================= --}} {{-- ================= GURU (NAVBAR MOBILE) ================= --}}
@if($role == 'guru') @if(auth()->check() && auth()->user()->role == 'guru')
<nav class="navbar navbar-expand-lg navbar-dark bg-info shadow-sm fixed-top"> <nav class="navbar navbar-expand-lg navbar-dark bg-info shadow-sm fixed-top">
<div class="container-fluid"> <div class="container-fluid">
<!-- Brand --> <!-- Brand -->
<a class="navbar-brand fw-bold" href="#"> <a class="navbar-brand fw-bold" href="{{ route('guru.dashboard') }}">
<i class="fas fa-school"></i> SMAN 1 <i class="fas fa-school"></i> SMAN 1
</a> </a>
@ -104,35 +91,47 @@
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('guru.dashboard') }}"> <a class="nav-link {{ request()->routeIs('guru.dashboard') ? 'active' : '' }}"
href="{{ route('guru.dashboard') }}">
<i class="fas fa-home"></i> Dashboard <i class="fas fa-home"></i> Dashboard
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('guru.jadwal') }}"> <a class="nav-link {{ request()->routeIs('guru.jadwal') ? 'active' : '' }}"
href="{{ route('guru.jadwal') }}">
<i class="fas fa-calendar"></i> Jadwal <i class="fas fa-calendar"></i> Jadwal
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('guru.absensi') }}"> <a class="nav-link {{ request()->routeIs('guru.absensi*') ? 'active' : '' }}"
href="{{ route('guru.absensi') }}">
<i class="fas fa-user-check"></i> Absensi <i class="fas fa-user-check"></i> Absensi
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('guru.nilai.index') }}"> <a class="nav-link {{ request()->routeIs('guru.nilai.*') ? 'active' : '' }}"
href="{{ route('guru.nilai.index') }}">
<i class="fas fa-clipboard-list"></i> Nilai <i class="fas fa-clipboard-list"></i> Nilai
</a> </a>
</li> </li>
<!-- <li class="nav-item"> {{-- 🔥 MENU BARU: TUGAS --}}
<a class="nav-link text-warning" href="#" <li class="nav-item">
onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> <a class="nav-link {{ request()->routeIs('guru.tugas.*') ? 'active' : '' }}"
<i class="fas fa-sign-out-alt"></i> Logout href="{{ route('guru.tugas.index') }}">
<i class="fas fa-tasks"></i> Tugas
</a> </a>
</li> --> </li>
<li class="nav-item">
<a href="{{ route('guru.ujian.index') }}" class="nav-link">
<i class="fas fa-file-alt"></i>
<p>Ujian</p>
</a>
</li>
</ul> </ul>
</div> </div>
@ -170,39 +169,44 @@
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('siswa.dashboard') }}"> <a class="nav-link {{ request()->is('siswa/dashboard') ? 'active' : '' }}"
href="{{ route('siswa.dashboard') }}">
<i class="fas fa-home"></i> Dashboard <i class="fas fa-home"></i> Dashboard
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('siswa.jadwal') }}"> <a class="nav-link {{ request()->is('siswa/jadwal') ? 'active' : '' }}"
href="{{ route('siswa.jadwal') }}">
<i class="fas fa-calendar"></i> Jadwal <i class="fas fa-calendar"></i> Jadwal
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('siswa.nilai') }}"> <a class="nav-link {{ request()->is('siswa/nilai') ? 'active' : '' }}"
href="{{ route('siswa.nilai') }}">
<i class="fas fa-clipboard-list"></i> Nilai <i class="fas fa-clipboard-list"></i> Nilai
</a> </a>
</li> </li>
<!-- TUGAS (SUDAH BENAR) -->
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('siswa.absensi') }}"> <a class="nav-link {{ request()->is('siswa/tugas*') ? 'active' : '' }}"
<i class="fas fa-user-check"></i> Absensi href="{{ route('siswa.tugas.index') }}">
<i class="fas fa-tasks"></i> Tugas
</a>
</li>
<li class="nav-item">
<a href="{{ route('siswa.ujian.index') }}" class="nav-link">
<i class="fas fa-edit"></i> Ujian
</a> </a>
</li> </li>
<!-- <li class="nav-item"> <!-- <li class="nav-item">
<a class="nav-link" href="{{ route('siswa.profile') }}"> <a class="nav-link {{ request()->routeIs('siswa.absensi*') ? 'active' : '' }}"
<i class="fas fa-user"></i> Profile href="{{ route('siswa.absensi') }}">
</a> <i class="fas fa-user-check"></i> Absensi
</li> -->
<!-- <li class="nav-item">
<a class="nav-link text-warning" href="#"
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
<i class="fas fa-sign-out-alt"></i> Logout
</a> </a>
</li> --> </li> -->

View File

@ -1,9 +1,11 @@
<h2>Login Berhasil</h2> <h3>🔐 Login Pertama Akun</h3>
<p>Halo {{ $user->name }}</p> <p>Halo {{ $user->name }},</p>
<p>Akun kamu baru saja login.</p> <p>Akun Anda baru saja login menggunakan password default.</p>
<p><b>Waktu:</b> {{ now() }}</p> <p><b>Email:</b> {{ $user->email }}</p>
<p>Jika bukan kamu, segera ubah password!</p> <p>⚠️ Segera ubah password Anda demi keamanan.</p>
<p>Waktu: {{ now() }}</p>

View File

@ -1,100 +0,0 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid px-4">
{{-- HEADER --}}
<div class="mb-4">
<h4 class="fw-bold mb-0">Absensi Guru</h4>
<small class="text-muted">
Lakukan absensi harian dengan kamera
</small>
</div>
<div class="row">
{{-- KAMERA --}}
<div class="col-md-8">
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<div id="camera-area" class="mb-3">
<video id="video" width="100%" height="350" autoplay class="rounded border"></video>
</div>
<button id="start-camera" class="btn btn-primary px-4">
Aktifkan Kamera
</button>
<button id="capture" class="btn btn-success px-4 d-none">
Ambil Foto
</button>
</div>
</div>
</div>
{{-- INFO ABSENSI --}}
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="fw-bold mb-3">Informasi</h6>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between">
<span>Nama</span>
<strong>{{ auth()->user()->name }}</strong>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Tanggal</span>
<strong>{{ date('d M Y') }}</strong>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Jam</span>
<strong id="jam"></strong>
</li>
<li class="list-group-item d-flex justify-content-between">
<span>Status</span>
<span class="badge bg-secondary">Belum Absen</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{{-- SCRIPT --}}
<script>
const video = document.getElementById('video');
const startBtn = document.getElementById('start-camera');
const captureBtn = document.getElementById('capture');
startBtn.addEventListener('click', async () => {
let stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
startBtn.classList.add('d-none');
captureBtn.classList.remove('d-none');
});
// JAM REALTIME
setInterval(() => {
let now = new Date();
document.getElementById('jam').innerText =
now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0') + ':' +
now.getSeconds().toString().padStart(2, '0');
}, 1000);
</script>
@endsection

View File

@ -0,0 +1,163 @@
@extends('layouts.app')
@section('content')
<div class="container mt-4">
<h4 class="fw-bold mb-4">📊 Manajemen Absensi Guru</h4>
<div class="card shadow p-4 mb-4">
<form method="GET" action="{{ route('guru.absensi') }}">
<div class="row">
<div class="col-md-6 mb-3">
<label class="fw-bold">Kelas</label>
<select name="kelas_id" class="form-control" required>
<option value="">-- Pilih Kelas --</option>
@foreach($kelas as $k)
<option value="{{ $k->id }}"
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
{{ $k->nama_kelas }}
</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="fw-bold">Mapel</label>
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
<input type="text" class="form-control"
value="{{ $mapel->nama_mapel }}" readonly>
</div>
</div>
<button class="btn btn-primary w-100">
Mulai Absensi
</button>
</form>
</div>
{{-- 🔥 TAMPIL FORM ABSENSI --}}
@if(isset($siswa))
<div class="card shadow p-4 mb-4">
<h5 class="fw-bold mb-3">
📋 Absensi Kelas {{ $kelasAktif->nama_kelas }}
</h5>
<form method="POST" action="{{ route('guru.absensi.simpan') }}">
@csrf
<input type="hidden" name="kelas_id" value="{{ $kelasAktif->id }}">
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
<div class="mb-3">
<label class="fw-bold">Mapel</label>
<input type="text" class="form-control"
value="{{ $mapel->nama_mapel }}" readonly>
</div>
<div class="mb-3">
<button type="button" class="btn btn-success btn-sm" onclick="setAll('hadir')">
Semua Hadir
</button>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>Nama</th>
<th>Status</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
@foreach($siswa as $s)
<tr>
<td>{{ $s->nama }}</td>
<td>
<select name="absensi[{{ $s->id }}][status]" class="form-control status">
<option value="hadir">Hadir</option>
<option value="izin">Izin</option>
<option value="sakit">Sakit</option>
<option value="alpha">Alpha</option>
</select>
</td>
<td>
<input type="text"
name="absensi[{{ $s->id }}][keterangan]"
class="form-control"
placeholder="Opsional...">
</td>
</tr>
@endforeach
</tbody>
</table>
<button class="btn btn-primary w-100 mt-3">
💾 Simpan Absensi
</button>
</form>
</div>
@endif
{{-- 🔥 DATA ABSENSI --}}
@if(isset($riwayat))
<div class="card shadow p-4">
<h5 class="fw-bold mb-3">📊 Riwayat Absensi</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Nama</th>
<th>Status</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
@forelse($riwayat as $r)
<tr>
<td>{{ $r->siswa->nama }}</td>
<td>{{ ucfirst($r->status) }}</td>
<td>{{ $r->keterangan }}</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-muted">Belum ada data</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endif
</div>
<script>
function setAll(status) {
document.querySelectorAll('.status').forEach(el => {
el.value = status;
});
}
</script>
@endsection

View File

@ -0,0 +1,83 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<div class="mb-4">
<h4 class="fw-bold mb-1">Tambah Tugas</h4>
<small class="text-muted">Isi data tugas yang akan diberikan kepada siswa</small>
</div>
<div class="card shadow-sm border-0">
<div class="card-body">
<form action="{{ route('guru.tugas.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Judul Tugas</label>
<input type="text" name="judul" class="form-control" required>
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Kelas</label>
<select name="kelas_id" class="form-select" required>
<option value="">-- Pilih Kelas --</option>
@foreach($kelas as $k)
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
@endforeach
</select>
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Mata Pelajaran</label>
@if($mapel)
<input type="text" class="form-control"
value="{{ $mapel->nama_mapel }}"
readonly>
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
@else
<input type="text" class="form-control"
value="Mapel belum diset di akun guru" readonly>
@endif
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Deskripsi</label>
<textarea name="deskripsi" rows="4" class="form-control"></textarea>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Upload File Tugas</label>
<input type="file" name="file" class="form-control"
accept=".pdf,.doc,.docx,.ppt,.pptx">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Deadline</label>
<input type="datetime-local" name="deadline" class="form-control" required>
</div>
</div>
<div class="mt-3 d-flex justify-content-end">
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary me-2">
Kembali
</a>
<button type="submit" class="btn btn-primary">
Simpan Tugas
</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,80 @@
@extends('layouts.app')
@section('content')
<div class="container">
<h4>Edit Tugas</h4>
<form action="{{ route('guru.tugas.update', $tugas->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
{{-- ERROR --}}
@if ($errors->any())
<div class="alert alert-danger">
@foreach ($errors->all() as $error)
<div>{{ $error }}</div>
@endforeach
</div>
@endif
{{-- JUDUL --}}
<div class="mb-3">
<label>Judul</label>
<input type="text" name="judul" class="form-control" value="{{ $tugas->judul }}" required>
</div>
{{-- KELAS --}}
<div class="mb-3">
<label>Kelas</label>
<select name="kelas_id" class="form-control" required>
@foreach($kelas as $k)
<option value="{{ $k->id }}"
{{ $tugas->kelas_id == $k->id ? 'selected' : '' }}>
{{ $k->nama_kelas }}
</option>
@endforeach
</select>
</div>
{{-- DESKRIPSI --}}
<div class="mb-3">
<label>Deskripsi</label>
<textarea name="deskripsi" class="form-control">{{ $tugas->deskripsi }}</textarea>
</div>
{{-- FILE LAMA --}}
<div class="mb-3">
<label>File Lama</label><br>
@if($tugas->file)
<a href="{{ asset('storage/' . $tugas->file) }}" target="_blank">
Lihat / Download File
</a>
@else
<span class="text-muted">Tidak ada file</span>
@endif
</div>
{{-- GANTI FILE --}}
<div class="mb-3">
<label>Ganti File (opsional)</label>
<input type="file" name="file" class="form-control"
accept=".pdf,.doc,.docx,.ppt,.pptx">
</div>
{{-- DEADLINE (DATETIME) --}}
<div class="mb-3">
<label>Deadline</label>
<input type="datetime-local" name="deadline"
value="{{ \Carbon\Carbon::parse($tugas->deadline)->format('Y-m-d\TH:i') }}">
</div>
{{-- BUTTON --}}
<button type="submit" class="btn btn-primary">Update</button>
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary">Kembali</a>
</form>
</div>
@endsection

View File

@ -0,0 +1,117 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<!-- HEADER -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1 fw-bold">Manajemen Tugas</h4>
<small class="text-muted">Kelola dan pantau tugas yang diberikan kepada siswa</small>
</div>
<a href="{{ route('guru.tugas.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Tugas
</a>
</div>
<!-- CARD -->
<div class="card shadow-sm border-0">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Judul</th>
<th>Kelas</th>
<th>Mata Pelajaran</th>
<th>Pengumpulan</th> {{-- 🔥 baru --}}
<th>Status</th> {{-- 🔥 baru --}}
<th>File</th>
<th>Deadline</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($tugas as $t)
<tr>
<td class="fw-semibold">{{ $t->judul }}</td>
<td>{{ $t->kelas->nama_kelas ?? '-' }}</td>
<td>{{ $t->mapel->nama_mapel ?? '-' }}</td>
{{-- 🔥 JUMLAH PENGUMPULAN --}}
<td>
<span class="badge bg-primary">
{{ $t->pengumpulan_count }} / {{ $t->total_siswa }}
</span>
</td>
{{-- 🔥 STATUS DEADLINE --}}
<td>
@if(now()->gt($t->deadline))
<span class="badge bg-danger">Lewat</span>
@else
<span class="badge bg-success">Aktif</span>
@endif
</td>
{{-- FILE --}}
<td>
@if($t->file)
<a href="{{ route('guru.tugas.download', $t->id) }}" class="btn btn-sm btn-success">
Download
</a>
@else
-
@endif
</td>
{{-- DEADLINE --}}
<td>
<span class="badge bg-light text-dark">
{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}
</span>
</td>
{{-- AKSI --}}
<td class="text-center">
<a href="{{ route('guru.tugas.pengumpulan', $t->id) }}"
class="btn btn-sm btn-primary mb-1">
Pengumpulan
</a>
<a href="{{ route('guru.tugas.show', $t->id) }}"
class="btn btn-sm btn-info mb-1">
Detail
</a>
<a href="{{ route('guru.tugas.edit', $t->id) }}"
class="btn btn-sm btn-warning">
Edit
</a>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="text-center text-muted">
Belum ada tugas
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,78 @@
@extends('layouts.app')
@section('content')
<div class="container">
<h4>Pengumpulan Tugas: {{ $tugas->judul }}</h4>
<table class="table table-bordered">
<thead>
<tr>
<th>Nama Siswa</th>
<th>File</th>
<th>Waktu Kumpul</th>
<th>Status</th>
<th>Nilai</th>
<th>Komentar</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach($tugas->pengumpulan as $p)
<tr>
<td>{{ $p->siswa->nama ?? '-' }}</td>
<td>
<a href="{{ asset('storage/' . $p->file) }}" target="_blank">
Download
</a>
</td>
<td>
{{ \Carbon\Carbon::parse($p->created_at)->format('d M Y H:i') }}
</td>
<td>
@if($p->status == 'telat')
<span class="badge bg-warning text-dark">Telat</span>
@else
<span class="badge bg-success">Tepat Waktu</span>
@endif
</td>
<!-- 🔥 FORM NILAI LANGSUNG -->
<form method="POST" action="{{ route('guru.tugas.nilai', $p->id) }}">
@csrf
<td>
<input type="number" name="nilai"
value="{{ $p->nilai }}"
class="form-control"
style="width:80px;">
</td>
<td>
<input type="text" name="komentar"
value="{{ $p->komentar }}"
class="form-control">
</td>
<td>
<button class="btn btn-sm btn-success">
Simpan
</button>
</td>
</form>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection

View File

@ -0,0 +1,23 @@
@extends('layouts.app')
@section('content')
<div class="container">
<h4>Detail Tugas</h4>
<div class="card">
<div class="card-body">
<p><b>Judul:</b> {{ $tugas->judul }}</p>
<p><b>Kelas:</b> {{ $tugas->kelas->nama_kelas }}</p>
<p><b>Mapel:</b> {{ $tugas->mapel->nama_mapel }}</p>
<p><b>Deskripsi:</b> {{ $tugas->deskripsi }}</p>
<p><b>Deadline:</b> {{ $tugas->deadline }}</p>
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary">Kembali</a>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,135 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<h2 class="mb-4 text-primary">📝 Buat Ujian + Soal</h2>
<div class="card shadow-sm border-0">
<div class="card-body">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@if($errors->any())
<div class="alert alert-danger">{{ $errors->first() }}</div>
@endif
<form method="POST" action="{{ route('guru.ujian.store') }}">
@csrf
{{-- ================= UJIAN ================= --}}
<h5 class="text-primary mb-3">📘 Data Ujian</h5>
<div class="row">
<div class="col-md-6 mb-3">
<label>Judul</label>
<input type="text" name="judul" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label>Durasi (menit)</label>
<input type="number" name="durasi" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label>Mulai</label>
<input type="datetime-local" name="mulai" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label>Selesai</label>
<input type="datetime-local" name="selesai" class="form-control">
</div>
</div>
<hr>
{{-- ================= SOAL ================= --}}
<h5 class="text-success mb-3">🧠 Soal Ujian</h5>
<div id="soal-container">
{{-- SOAL PERTAMA --}}
<div class="soal-item border p-3 mb-3 rounded">
<label>Pertanyaan</label>
<textarea name="soal[0][pertanyaan]" class="form-control" required></textarea>
<div class="row mt-2">
<div class="col-md-6">
<input name="soal[0][a]" class="form-control" placeholder="Pilihan A">
</div>
<div class="col-md-6">
<input name="soal[0][b]" class="form-control" placeholder="Pilihan B">
</div>
<div class="col-md-6 mt-2">
<input name="soal[0][c]" class="form-control" placeholder="Pilihan C">
</div>
<div class="col-md-6 mt-2">
<input name="soal[0][d]" class="form-control" placeholder="Pilihan D">
</div>
</div>
<select name="soal[0][jawaban]" class="form-control mt-2">
<option value="a">Jawaban A</option>
<option value="b">Jawaban B</option>
<option value="c">Jawaban C</option>
<option value="d">Jawaban D</option>
</select>
</div>
</div>
<button type="button" class="btn btn-info mb-3" onclick="tambahSoal()">+ Tambah Soal</button>
<hr>
<div class="d-flex justify-content-between">
<a href="{{ route('guru.ujian.index') }}" class="btn btn-secondary">
Kembali
</a>
<button class="btn btn-success">
💾 Simpan Ujian + Soal
</button>
</div>
</form>
</div>
</div>
</div>
<script>
let index = 1;
function tambahSoal() {
let html = `
<div class="soal-item border p-3 mb-3 rounded">
<label>Pertanyaan</label>
<textarea name="soal[${index}][pertanyaan]" class="form-control"></textarea>
<input name="soal[${index}][a]" class="form-control mt-2" placeholder="A">
<input name="soal[${index}][b]" class="form-control mt-2" placeholder="B">
<input name="soal[${index}][c]" class="form-control mt-2" placeholder="C">
<input name="soal[${index}][d]" class="form-control mt-2" placeholder="D">
<select name="soal[${index}][jawaban]" class="form-control mt-2">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
<option value="d">D</option>
</select>
</div>
`;
document.getElementById('soal-container').insertAdjacentHTML('beforeend', html);
index++;
}
</script>
@endsection

View File

@ -0,0 +1,12 @@
@extends('layouts.app')
@section('content')
<a href="{{ route('guru.ujian.create') }}">+ Buat Ujian</a>
<ul>
@foreach($data as $u)
<li>
{{ $u->judul }}
<a href="{{ route('guru.ujian.soal', $u->id) }}">Input Soal</a>
</li>
@endforeach
</ul>
@endsection

View File

@ -0,0 +1,34 @@
@extends('layouts.app')
@section('content')
<h3>Input Soal: {{ $ujian->judul }}</h3>
@if(session('success')) <div>{{ session('success') }}</div> @endif
@if($errors->any()) <div>{{ $errors->first() }}</div> @endif
<form method="POST" action="{{ route('guru.ujian.storeSoal', $ujian->id) }}">
@csrf
@for($i=0; $i<5; $i++)
<p>Soal {{ $i+1 }}</p>
<input type="text" name="soal[{{ $i }}][pertanyaan]" placeholder="Pertanyaan" required><br>
<input type="text" name="soal[{{ $i }}][a]" placeholder="A" required><br>
<input type="text" name="soal[{{ $i }}][b]" placeholder="B" required><br>
<input type="text" name="soal[{{ $i }}][c]" placeholder="C" required><br>
<input type="text" name="soal[{{ $i }}][d]" placeholder="D" required><br>
<select name="soal[{{ $i }}][jawaban]" required>
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
<option value="d">D</option>
</select>
<hr>
@endfor
<button>Simpan Soal</button>
</form>
@endsection

View File

@ -1,24 +1,104 @@
@extends('layouts.app') @extends('layouts.app')
@section('content') @section('content')
<div class="container"> <div class="container-fluid mt-4">
<h3>Absensi</h3> <div class="row">
<table class="table table-bordered"> {{-- LEFT PANEL --}}
<tr> <div class="col-md-3">
<th>Tanggal</th>
<th>Status</th>
</tr>
@foreach($absensi as $a) <div class="card shadow-sm border-0 mb-3">
<tr> <div class="card-body">
<td>{{ $a->tanggal }}</td> <h6 class="text-muted mb-2">Informasi Siswa</h6>
<td>{{ $a->status }}</td> <hr>
</tr> <p class="mb-1"><b>Nama:</b> {{ auth()->user()->name }}</p>
@endforeach <p class="mb-1"><b>Kelas:</b> {{ auth()->user()->siswa->kelas->nama_kelas ?? '-' }}</p>
</div>
</div>
</table> <div class="card shadow-sm border-0 text-center">
<div class="card-body">
<h6 class="text-muted mb-3">Status Hari Ini</h6>
@if($sudahAbsen ?? false)
<span class="badge bg-success px-3 py-2">Sudah Hadir</span>
@else
<span class="badge bg-warning text-dark px-3 py-2">Belum Absen</span>
@endif
</div>
</div>
</div>
{{-- MAIN --}}
<div class="col-md-9">
{{-- LIST ABSENSI PER MAPEL --}}
@forelse($groupedAbsensi as $namaMapel => $items)
<div class="mb-4">
{{-- HEADER MAPEL --}}
<h5 class="fw-bold text-primary">
📚 {{ $namaMapel }}
</h5>
<hr>
<div class="row">
@foreach($items as $absensi)
<div class="col-md-4 mb-3">
<div class="card shadow-sm border-0 h-100">
<div class="card-body text-center">
<i class="fas fa-user-check text-success mb-2"></i>
<h6 class="fw-bold">
{{ optional($absensi->guru)->nama ?? '-' }}
</h6>
<small class="text-muted d-block mb-2">
{{ \Carbon\Carbon::now()->translatedFormat('H:i') }}
</small>
<form method="POST" action="{{ route('siswa.absensi.hadir') }}">
@csrf
<input type="hidden" name="absensi_id" value="{{ $absensi->id }}">
@php
$sudah = \App\Models\AbsensiDetail::where('absensi_id', $absensi->id)
->where('siswa_id', auth()->user()->siswa->id)
->exists();
@endphp
<button class="btn btn-success btn-sm rounded-pill"
@if($sudah) disabled @endif>
{{ $sudah ? 'Sudah Absen' : 'Hadir' }}
</button>
</form>
</div>
</div>
</div>
@endforeach
</div>
</div>
@empty
<div class="text-center text-muted">
Tidak ada absensi aktif
</div>
@endforelse
</div>
</div>
</div> </div>
@endsection @endsection

View File

@ -0,0 +1,92 @@
@extends('layouts.app')
@section('content')
<div class="container">
<h4>Daftar Tugas</h4>
<table class="table table-bordered">
<thead>
<tr>
<th>Judul</th>
<th>Mapel</th>
<th>Deadline</th>
<th>Waktu Kumpul</th>
<th>Nilai</th>
<th>Komentar</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach($tugas as $t)
<tr>
<td>{{ $t->judul }}</td>
<td>{{ $t->mapel->nama_mapel ?? '-' }}</td>
<td>
{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}
</td>
<td>
@if($t->waktu_kumpul)
{{ \Carbon\Carbon::parse($t->waktu_kumpul)->format('d M Y H:i') }}
@else
-
@endif
</td>
{{-- NILAI --}}
<td>
{{ $t->nilai ?? '-' }}
</td>
{{-- KOMENTAR --}}
<td>
{{ $t->komentar ?? '-' }}
</td>
{{-- STATUS --}}
<td>
@if($t->status == 'tepat')
<span class="badge bg-success">Tepat</span>
@elseif($t->status == 'telat')
<span class="badge bg-warning text-dark">Telat</span>
@elseif($t->status == 'lewat')
<span class="badge bg-danger">Belum Kumpul</span>
@else
<span class="badge bg-secondary">Belum</span>
@endif
</td>
{{-- AKSI --}}
<td>
@if($t->file)
<a href="{{ route('guru.tugas.download', $t->id) }}"
class="btn btn-sm btn-success">
Download
</a>
@endif
@if(!$t->waktu_kumpul && now()->lt($t->deadline))
<a href="{{ route('siswa.tugas.kumpul', $t->id) }}"
class="btn btn-sm btn-primary">
Kumpulkan
</a>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection

View File

@ -0,0 +1,21 @@
@extends('layouts.app')
@section('content')
<div class="container">
<h4>Kumpulkan Tugas</h4>
<form action="{{ route('siswa.tugas.store', $tugas->id) }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label>Upload Jawaban</label>
<input type="file" name="file" class="form-control" required>
</div>
<button class="btn btn-success">Upload</button>
</form>
</div>
@endsection

View File

@ -0,0 +1,16 @@
@extends('layouts.app')
@section('content')
<h2>Hasil Ujian</h2>
<div class="card p-4">
<h3>Nilai: {{ $nilai }}</h3>
<p>Benar: {{ $benar }} / {{ $total }}</p>
<a href="{{ route('siswa.dashboard') }}" class="btn btn-primary">
Kembali ke Dashboard
</a>
</div>
@endsection

View File

@ -0,0 +1,18 @@
@extends('layouts.app')
@section('content')
<h2>Ujian Tersedia</h2>
@foreach($ujians as $u)
<div class="card mb-2 p-3">
<h5>{{ $u->judul }}</h5>
<p>Durasi: {{ $u->durasi }} menit</p>
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="btn btn-primary">
Kerjakan
</a>
</div>
@endforeach
@endsection

View File

@ -0,0 +1,115 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<h3 class="mb-3">{{ $ujian->judul }}</h3>
{{-- TIMER --}}
<div class="alert alert-info">
Sisa waktu: <b id="timer"></b>
</div>
<form method="POST" action="{{ route('siswa.ujian.submit', $ujian->id) }}">
@csrf
<div class="row">
{{-- ================= KIRI: SOAL ================= --}}
<div class="col-md-9">
@foreach($soals as $i => $s)
<div class="card mb-3 p-3" id="soal{{ $s->id }}">
<b>Soal {{ $loop->iteration }}</b><br>
{{ $s->pertanyaan }}
<hr>
<label>
<input type="radio" name="jawaban[{{ $s->id }}]" value="a">
{{ $s->a }}
</label><br>
<label>
<input type="radio" name="jawaban[{{ $s->id }}]" value="b">
{{ $s->b }}
</label><br>
<label>
<input type="radio" name="jawaban[{{ $s->id }}]" value="c">
{{ $s->c }}
</label><br>
<label>
<input type="radio" name="jawaban[{{ $s->id }}]" value="d">
{{ $s->d }}
</label><br>
</div>
@endforeach
{{-- BUTTON FINISH --}}
<div class="text-end mb-5">
<button class="btn btn-success btn-lg">
Finish Ujian
</button>
</div>
</div>
{{-- ================= KANAN: DAFTAR SOAL ================= --}}
<div class="col-md-3">
<div class="card p-3">
<b>Daftar Soal</b>
<div class="d-flex flex-wrap mt-2">
@foreach($soals as $i => $s)
<a href="#soal{{ $s->id }}"
class="btn btn-sm btn-outline-primary m-1">
{{ $loop->iteration }}
</a>
@endforeach
</div>
</div>
</div>
</div>
</form>
</div>
{{-- ================= TIMER SCRIPT ================= --}}
<script>
let waktu = {{ $ujian->durasi * 60 }};
let timer = setInterval(function() {
let menit = Math.floor(waktu / 60);
let detik = waktu % 60;
document.getElementById('timer').innerHTML =
menit + " : " + (detik < 10 ? "0" + detik : detik);
waktu--;
if (waktu < 0) {
clearInterval(timer);
alert("Waktu habis!");
document.querySelector('form').submit();
}
}, 1000);
</script>
@endsection

View File

@ -1,67 +1,87 @@
<?php <?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\AuthController; use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Mail; // ================= ADMIN =================
use App\Http\Controllers\Admin\DashboardController as AdminDashboard;
use App\Http\Controllers\Admin\GuruController;
use App\Http\Controllers\Admin\SiswaController;
use App\Http\Controllers\Admin\MataPelajaranController;
use App\Http\Controllers\Admin\KelasController;
use App\Http\Controllers\Admin\JadwalController as AdminJadwal;
use App\Http\Controllers\Admin\ProfileController;
Route::get('/login', [AuthController::class, 'showLogin'])->name('login'); // ================= GURU =================
Route::post('/login', [AuthController::class, 'login']); use App\Http\Controllers\Guru\DashboardController as GuruDashboard;
Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); use App\Http\Controllers\Guru\JadwalController as GuruJadwal;
// ================= FORGOT PASSWORD ================= use App\Http\Controllers\Guru\AbsensiController;
Route::prefix('auth')->middleware('guest')->group(function () { use App\Http\Controllers\Guru\NilaiController as GuruNilai;
use App\Http\Controllers\Guru\TugasController;
use App\Models\Jadwal;
use App\Http\Controllers\Guru\MapelController;
// ================= FORGOT PASSWORD =================
Route::get('/forgot-password', [AuthController::class, 'showForgot']) // ================= SISWA =================
->name('forgot.password'); use App\Http\Controllers\Siswa\DashboardController as SiswaDashboard;
use App\Http\Controllers\Siswa\JadwalController as SiswaJadwal;
use App\Http\Controllers\Siswa\AbsensiController as SiswaAbsensi;
use App\Http\Controllers\Siswa\NilaiController as SiswaNilai;
use App\Http\Controllers\Siswa\ProfileController as SiswaProfile;
use App\Http\Controllers\Siswa\AbsensiController as SiswaAbsensiController;
Route::post('/send-otp', [AuthController::class, 'sendOtp']) //=================otp=================
->name('send.otp');
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp']) use App\Http\Controllers\Auth\OtpController;
->name('verify.otp.form');
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']) use Illuminate\Support\Facades\Password;
->name('verify.otp'); use App\Http\Controllers\Auth\ForgotPasswordController;
use App\Http\Controllers\Auth\ResetPasswordController;
Route::get('/reset-password', [AuthController::class, 'showResetPassword']) use App\Http\Controllers\Siswa\UjianController;
->name('reset.password.form');
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
->name('reset.password');
Route::post('/send-otp', [AuthController::class, 'sendOtp'])->name('send.otp'); use App\Http\Controllers\Guru\UjianController as GuruUjian;
}); use App\Http\Controllers\Siswa\UjianController as SiswaUjian;
Route::get('/test-email', function () {
Mail::raw('Tes email berhasil', function ($message) {
$message->to('EMAIL_KAMU@gmail.com')
->subject('Test Email Laravel');
});
return 'Email terkirim!';
});
// ADMIN
Route::get('/admin', function () {
return view('admin.dashboard');
});
// GURU
Route::get('/guru', function () {
return view('guru.dashboard');
});
// SISWA
Route::get('/siswa', function () {
return view('siswa.dashboard');
});
/* /*
|---------------------------------------- |----------------------------------------
| FORCE PASSWORD (LOGIN SAJA) | ROOT
|----------------------------------------
*/
Route::get('/', function () {
return redirect()->route('login');
});
/*
|----------------------------------------
| LOGIN
|----------------------------------------
*/
Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
Route::post('/login', [AuthController::class, 'login'])->name('login.process');
/*
|----------------------------------------
| LOGOUT
|----------------------------------------
*/
Route::post('/logout', function () {
Auth::logout();
request()->session()->invalidate();
request()->session()->regenerateToken();
return redirect()->route('login');
})->name('logout');
/*
|----------------------------------------
| FORCE PASSWORD
|---------------------------------------- |----------------------------------------
*/ */
Route::middleware('auth')->group(function () { Route::middleware('auth')->group(function () {
Route::get('/force-password', function () { Route::get('/force-password', function () {
return view('auth.force-password'); return view('auth.force-password');
})->name('force.password'); })->name('force.password');
@ -72,176 +92,241 @@
/* /*
|---------------------------------------- |----------------------------------------
| SEMUA HALAMAN (WAJIB FORCE PASSWORD)
|----------------------------------------
*/
Route::middleware(['auth', 'force.password'])->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', [AdminDashboard::class, 'index'])->name('dashboard');
Route::resource('guru', GuruController::class);
Route::resource('siswa', SiswaController::class);
Route::resource('mapel', MataPelajaranController::class);
Route::resource('kelas', KelasController::class);
Route::resource('jadwal', AdminJadwal::class);
Route::get('jadwal-grid', [AdminJadwal::class, 'grid'])->name('jadwal.grid');
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
Route::post('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::get('/guru/{id}/nonaktif', [GuruController::class, 'nonaktif'])->name('guru.nonaktif');
Route::get('/guru/{id}/aktifkan', [GuruController::class, 'aktifkan'])->name('guru.aktifkan');
Route::get('/siswa/{id}/nonaktif', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
Route::get('/siswa/{id}/aktifkan', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif'])
->name('siswa.bulkNonaktif');
});
// ================= FORGOT PASSWORD =================
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
});
Route::post('/forgot-password', [AuthController::class, 'sendOtp']);
Route::get('/verify-otp', function () {
return view('auth.verify-otp');
});
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
Route::get('/reset-password', function () {
return view('auth.reset-password');
});
Route::post('/reset-password', [AuthController::class, 'resetPassword']);
/*
|----------------------------------------
| ROOT
|----------------------------------------
*/
Route::get('/', function () {
return redirect()->route('login');
});
/*
|--------------------------------------------------------------------------
| ADMIN | ADMIN
|-------------------------------------------------------------------------- |----------------------------------------
*/ */
use App\Http\Controllers\Admin\DashboardController as AdminDashboard; Route::middleware(['auth', 'force.password'])
use App\Http\Controllers\Admin\GuruController; ->prefix('admin')
use App\Http\Controllers\Admin\SiswaController; ->name('admin.')
use App\Http\Controllers\Admin\MataPelajaranController; ->group(function () {
use App\Http\Controllers\Admin\KelasController;
use App\Http\Controllers\Admin\JadwalController as AdminJadwal;
use App\Http\Controllers\Admin\ProfileController;
Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () { // 🔥 /admin langsung ke dashboard
Route::get('/', [AdminDashboard::class, 'index']);
Route::get('/dashboard', [AdminDashboard::class, 'index'])->name('dashboard'); Route::get('/dashboard', [AdminDashboard::class, 'index'])->name('dashboard');
Route::resource('guru', GuruController::class); Route::resource('guru', GuruController::class);
Route::resource('siswa', SiswaController::class); Route::resource('siswa', SiswaController::class);
Route::resource('mapel', MataPelajaranController::class); Route::resource('mapel', MataPelajaranController::class);
Route::resource('kelas', KelasController::class); Route::resource('kelas', KelasController::class);
Route::resource('jadwal', AdminJadwal::class); Route::resource('jadwal', AdminJadwal::class);
Route::get('jadwal-grid', [AdminJadwal::class, 'grid'])->name('jadwal.grid'); // 🔥 route tambahan
Route::get('jadwal/grid', [AdminJadwal::class, 'grid'])->name('jadwal.grid');
// PROFILE // PROFILE
Route::get('/profile', [ProfileController::class, 'index'])->name('profile'); Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
Route::post('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::post('/profile', [ProfileController::class, 'update'])->name('profile.update');
// GURU STATUS // STATUS
Route::get('/guru/{id}/nonaktif', [GuruController::class, 'nonaktif'])->name('guru.nonaktif'); Route::get('/guru/{id}/nonaktif', [GuruController::class, 'nonaktif'])->name('guru.nonaktif');
Route::get('/guru/{id}/aktifkan', [GuruController::class, 'aktifkan'])->name('guru.aktifkan'); Route::get('/guru/{id}/aktifkan', [GuruController::class, 'aktifkan'])->name('guru.aktifkan');
// SISWA STATUS // Route::get('/siswa/{id}/nonaktif', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
Route::get('/siswa/{id}/nonaktif', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif'); // Route::post('/nonaktif/{id}', [SiswaController::class, 'nonaktif'])
Route::get('/siswa/{id}/aktifkan', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan'); // ->name('admin.siswa.nonaktif');
// Route::get('/siswa/{id}/aktifkan', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
// 🔥 BULK (INI YANG KAMU BUTUHKAN) Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif'])->name('siswa.bulkNonaktif');
Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif']) });
->name('siswa.bulkNonaktif');
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->name('forgot.password');
Route::post('/forgot-password', [AuthController::class, 'sendOtp']); Route::prefix('admin')->name('admin.')->group(function () {
Route::get('/verify-otp', function () { Route::post('/siswa/nonaktif/{id}', [SiswaController::class, 'nonaktif'])
return view('auth.verify-otp'); ->name('siswa.nonaktif');
})->name('verify.otp');
Route::get('/siswa/aktifkan/{id}', [SiswaController::class, 'aktifkan'])
->name('siswa.aktifkan');
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
Route::get('/reset-password', function () {
return view('auth.reset-password');
})->name('reset.password.form');
Route::post('/reset-password', [AuthController::class, 'resetPasswordOtp']);
}); });
/* /*
|-------------------------------------------------------------------------- |----------------------------------------
| GURU | GURU
|-------------------------------------------------------------------------- |----------------------------------------
*/ */
use App\Http\Controllers\Guru\DashboardController as GuruDashboard; Route::middleware(['auth', 'force.password'])
use App\Http\Controllers\Guru\JadwalController as GuruJadwal; ->prefix('guru')
use App\Http\Controllers\Guru\AbsensiController as GuruAbsensi; ->name('guru.')
use App\Http\Controllers\Guru\NilaiController as GuruNilai; ->group(function () {
Route::middleware(['auth', 'force.password'])->prefix('guru')->name('guru.')->group(function () { Route::get('/', [GuruDashboard::class, 'index']);
Route::get('/dashboard', [GuruDashboard::class, 'index'])->name('dashboard');
Route::get('/dashboard', [GuruDashboard::class, 'index'])->name('dashboard'); Route::get('/jadwal', [GuruJadwal::class, 'index'])->name('jadwal');
Route::get('/jadwal', [GuruJadwal::class, 'index'])->name('jadwal'); // =========================
// ✅ ABSENSI (FINAL)
// =========================
Route::get('/absensi', [AbsensiController::class, 'index'])
->name('absensi');
Route::get('/absensi', [GuruAbsensi::class, 'index'])->name('absensi'); Route::post('/absensi/simpan', [AbsensiController::class, 'simpan'])
->name('absensi.simpan');
// ================= NILAI =================
Route::get('/nilai', [GuruNilai::class, 'index'])->name('nilai.index'); // =========================
// NILAI
// =========================
Route::get('/nilai', [GuruNilai::class, 'index'])->name('nilai.index');
Route::get('/nilai/input', [GuruNilai::class, 'inputNilai'])->name('nilai.input');
Route::get('/get-siswa/{id}', [GuruNilai::class, 'getSiswaByKelas'])->name('get.siswa');
Route::post('/nilai/store-batch', [GuruNilai::class, 'storeBatch'])->name('nilai.storeBatch');
Route::get('/nilai/{id}/edit', [GuruNilai::class, 'edit'])->name('nilai.edit');
Route::put('/nilai/{id}', [GuruNilai::class, 'update'])->name('nilai.update');
Route::get('nilai/input', [GuruNilai::class, 'inputNilai'])->name('nilai.input'); // =========================
// TUGAS
// =========================
Route::resource('tugas', TugasController::class);
// 🔥 PERBAIKAN: kasih name route Route::get('/tugas/{id}/download', [TugasController::class, 'download'])
Route::get('/get-siswa/{id}', [GuruNilai::class, 'getSiswaByKelas'])->name('get.siswa'); ->name('tugas.download');
Route::post('/nilai/store-batch', [GuruNilai::class, 'storeBatch'])->name('nilai.storeBatch'); Route::get('/tugas/{id}/pengumpulan', [TugasController::class, 'pengumpulan'])
Route::get('/nilai/{id}/edit', [GuruNilai::class, 'edit'])->name('nilai.edit'); ->name('tugas.pengumpulan');
Route::put('/nilai/{id}', [GuruNilai::class, 'update'])->name('nilai.update');
});
Route::post('/pengumpulan/{id}/nilai', [TugasController::class, 'nilai'])
->name('tugas.nilai');
// =========================
// MAPEL
// =========================
Route::get('/get-mapel-by-kelas/{id}', [MapelController::class, 'getByKelas'])
->name('get.mapel');
});
//==================otp============
// Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
// Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
// Route::post('/send-otp-login', [OtpController::class, 'sendOtp'])
// ->name('send.otp.login');
Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
// form email
Route::get('/forgot-password', [ForgotPasswordController::class, 'showLinkRequestForm'])
->name('password.request');
// kirim email
Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLinkEmail'])
->name('password.email');
// form reset
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])
->name('password.reset');
// simpan password baru
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])
->name('password.update');
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->name('password.request');
Route::post('/forgot-password', [AuthController::class, 'sendOtp'])
->name('password.email');
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp'])
->name('verify.otp.form');
Route::post('/verify-otp', [AuthController::class, 'verifyOtp'])
->name('verify.otp');
// FORM RESET PASSWORD (GET)
Route::get('/reset-password', [AuthController::class, 'showResetPassword'])
->name('reset.password');
// PROSES RESET PASSWORD (POST)
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
->name('password.update');
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
->name('password.update');
/* /*
|-------------------------------------------------------------------------- |----------------------------------------
| SISWA | SISWA
|-------------------------------------------------------------------------- |----------------------------------------
*/ */
use App\Http\Controllers\Siswa\DashboardController as SiswaDashboard; use App\Http\Controllers\Siswa\TugasController as SiswaTugasController;
use App\Http\Controllers\Siswa\JadwalController as SiswaJadwal;
use App\Http\Controllers\Siswa\AbsensiController as SiswaAbsensi;
use App\Http\Controllers\Siswa\NilaiController as SiswaNilai;
use App\Http\Controllers\Siswa\ProfileController as SiswaProfile;
Route::middleware(['auth', 'force.password'])->prefix('siswa')->name('siswa.')->group(function () { Route::middleware(['auth', 'force.password'])
->prefix('siswa')
->name('siswa.')
->group(function () {
Route::get('/dashboard', [SiswaDashboard::class, 'index'])->name('dashboard'); Route::get('/', [SiswaDashboard::class, 'index']);
Route::get('/jadwal', [SiswaJadwal::class, 'index'])->name('jadwal'); Route::get('/dashboard', [SiswaDashboard::class, 'index'])->name('dashboard');
Route::get('/jadwal', [SiswaJadwal::class, 'index'])->name('jadwal');
Route::get('/absensi', [SiswaAbsensi::class, 'index'])->name('absensi');
Route::get('/nilai', [SiswaNilai::class, 'index'])->name('nilai');
Route::get('/absensi', [SiswaAbsensi::class, 'index'])->name('absensi'); Route::get('/profile', [SiswaProfile::class, 'index'])->name('profile');
Route::post('/profile', [SiswaProfile::class, 'update'])->name('profile.update');
Route::get('/nilai', [SiswaNilai::class, 'index'])->name('nilai'); // ✅ SCAN QR
Route::get('/absen/{token}', [SiswaAbsensi::class, 'scan'])
->name('absensi.scan');
// ✅ SUBMIT HADIR
Route::post('/absen/hadir', [SiswaAbsensi::class, 'hadir'])
->name('absensi.hadir');
// ================= TUGAS =================
Route::get('/tugas', [SiswaTugasController::class, 'index'])->name('tugas.index');
Route::get('/tugas/{id}/kumpul', [SiswaTugasController::class, 'kumpul'])->name('tugas.kumpul');
Route::post('/tugas/{id}/kumpul', [SiswaTugasController::class, 'store'])->name('tugas.store');
});
Route::middleware(['auth'])->group(function () {
Route::prefix('guru')->name('guru.')->group(function () {
Route::get('/ujian', [\App\Http\Controllers\Guru\UjianController::class, 'index'])->name('ujian.index');
Route::get('/ujian/create', [\App\Http\Controllers\Guru\UjianController::class, 'create'])->name('ujian.create');
Route::post('/ujian/store', [\App\Http\Controllers\Guru\UjianController::class, 'store'])->name('ujian.store');
Route::get('/ujian/{id}/soal', [\App\Http\Controllers\Guru\UjianController::class, 'soal'])->name('ujian.soal');
Route::post('/ujian/{id}/soal', [\App\Http\Controllers\Guru\UjianController::class, 'storeSoal'])->name('ujian.storeSoal');
// Route::get('/guru/ujian/{id}/soal', [UjianController::class, 'soal']);
// Route::post('/guru/ujian/{id}/soal', [UjianController::class, 'storeSoal']);
});
// PROFILE });
Route::get('/profile', [SiswaProfile::class, 'index'])->name('profile');
Route::post('/profile', [SiswaProfile::class, 'update'])->name('profile.update'); Route::middleware(['auth'])->prefix('siswa')->group(function () {
Route::get('/ujian', [UjianController::class, 'index'])
->name('siswa.ujian.index');
Route::get('/ujian/{id}/kerjakan', [UjianController::class, 'kerjakan'])
->name('siswa.ujian.kerjakan');
Route::post('/ujian/{id}/submit', [UjianController::class, 'submit'])
->name('siswa.ujian.submit');
});
/*
|----------------------------------------
| TEST EMAIL
|----------------------------------------
*/
Route::get('/test-email', function () {
Mail::raw('Tes email berhasil', function ($message) {
$message->to('EMAIL_KAMU@gmail.com')
->subject('Test Email Laravel');
});
return 'Email terkirim!';
}); });

974
smasa.sql Normal file
View File

@ -0,0 +1,974 @@
-- phpMyAdmin SQL Dump
-- version 5.2.0
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: May 03, 2026 at 02:21 AM
-- Server version: 8.0.30
-- PHP Version: 8.3.21
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `smasa`
--
-- --------------------------------------------------------
--
-- Table structure for table `absensi`
--
CREATE TABLE `absensi` (
`id` bigint UNSIGNED NOT NULL,
`guru_id` bigint UNSIGNED DEFAULT NULL,
`kelas_id` bigint UNSIGNED DEFAULT NULL,
`mapel_id` bigint UNSIGNED DEFAULT NULL,
`tanggal` date DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `absensi`
--
INSERT INTO `absensi` (`id`, `guru_id`, `kelas_id`, `mapel_id`, `tanggal`, `created_at`, `updated_at`) VALUES
(1, 39, 6, 7, '2026-04-24', '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(2, 39, 8, 7, '2026-04-24', '2026-04-23 19:31:54', '2026-04-23 19:31:54');
-- --------------------------------------------------------
--
-- Table structure for table `absensi_detail`
--
CREATE TABLE `absensi_detail` (
`id` bigint UNSIGNED NOT NULL,
`absensi_id` bigint UNSIGNED DEFAULT NULL,
`siswa_id` bigint UNSIGNED DEFAULT NULL,
`status` enum('hadir','izin','sakit','alpha') DEFAULT 'hadir',
`keterangan` text,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `absensi_detail`
--
INSERT INTO `absensi_detail` (`id`, `absensi_id`, `siswa_id`, `status`, `keterangan`, `created_at`, `updated_at`) VALUES
(1, 1, 15, 'hadir', NULL, '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(2, 1, 16, 'hadir', NULL, '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(3, 1, 19, 'hadir', NULL, '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(4, 1, 22, 'hadir', NULL, '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(5, 1, 23, 'hadir', NULL, '2026-04-23 19:31:25', '2026-04-23 19:31:25'),
(6, 2, 17, 'izin', NULL, '2026-04-23 19:31:54', '2026-04-23 19:31:54'),
(7, 2, 20, 'hadir', NULL, '2026-04-23 19:31:54', '2026-04-23 19:31:54'),
(8, 2, 21, 'hadir', NULL, '2026-04-23 19:31:54', '2026-04-23 19:31:54');
-- --------------------------------------------------------
--
-- Table structure for table `cache`
--
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `cache_locks`
--
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `failed_jobs`
--
CREATE TABLE `failed_jobs` (
`id` bigint UNSIGNED NOT NULL,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `gurus`
--
CREATE TABLE `gurus` (
`id` bigint UNSIGNED NOT NULL,
`nama` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`nip` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`alamat` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`telepon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`status` enum('aktif','nonaktif') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'aktif',
`mapel_id` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `gurus`
--
INSERT INTO `gurus` (`id`, `nama`, `nip`, `alamat`, `telepon`, `created_at`, `updated_at`, `status`, `mapel_id`) VALUES
(36, 'zakaria', '242424213', 'jember', '089785463773', '2026-04-19 21:18:08', '2026-04-19 21:18:08', 'aktif', 2),
(37, 'Umam', '213213123', 'kembang', '0879635231', '2026-04-21 11:52:56', '2026-04-21 11:52:56', 'aktif', 3),
(39, 'Arengga', '2423123', 'Bondowoso', '6289504732922', '2026-04-22 02:17:14', '2026-04-22 02:47:48', 'aktif', 7);
-- --------------------------------------------------------
--
-- Table structure for table `hasils`
--
CREATE TABLE `hasils` (
`id` bigint UNSIGNED NOT NULL,
`siswa_id` bigint UNSIGNED DEFAULT NULL,
`ujian_id` bigint UNSIGNED DEFAULT NULL,
`nilai` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `jadwals`
--
CREATE TABLE `jadwals` (
`id` bigint UNSIGNED NOT NULL,
`kelas_id` bigint UNSIGNED NOT NULL,
`mata_pelajaran_id` bigint UNSIGNED NOT NULL,
`hari` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`jam_mulai` time NOT NULL,
`jam_selesai` time NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`guru_id` bigint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `jadwals`
--
INSERT INTO `jadwals` (`id`, `kelas_id`, `mata_pelajaran_id`, `hari`, `jam_mulai`, `jam_selesai`, `created_at`, `updated_at`, `guru_id`) VALUES
(13, 6, 5, 'Senin', '07:00:00', '08:00:00', '2026-03-31 21:24:11', '2026-03-31 21:24:11', 14),
(14, 6, 2, 'Senin', '12:00:00', '13:00:00', '2026-03-31 21:26:45', '2026-03-31 21:26:45', 15),
(15, 6, 5, 'Selasa', '07:00:00', '08:00:00', '2026-03-31 21:27:12', '2026-03-31 21:27:12', 14),
(16, 6, 4, 'Selasa', '09:00:00', '10:00:00', '2026-03-31 21:27:30', '2026-03-31 21:27:30', 15),
(19, 6, 6, 'Rabu', '20:00:00', '22:00:00', '2026-04-17 02:26:01', '2026-04-17 02:26:01', 25),
(20, 6, 2, 'Rabu', '09:00:00', '10:00:00', '2026-04-17 02:28:19', '2026-04-17 02:28:19', 25);
-- --------------------------------------------------------
--
-- Table structure for table `jawabans`
--
CREATE TABLE `jawabans` (
`id` bigint UNSIGNED NOT NULL,
`siswa_id` bigint UNSIGNED NOT NULL,
`soal_id` bigint UNSIGNED NOT NULL,
`jawaban` char(1) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `jawaban_siswas`
--
CREATE TABLE `jawaban_siswas` (
`id` bigint UNSIGNED NOT NULL,
`siswa_id` bigint UNSIGNED DEFAULT NULL,
`soal_id` bigint UNSIGNED DEFAULT NULL,
`jawaban` enum('a','b','c','d') DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- --------------------------------------------------------
--
-- Table structure for table `jobs`
--
CREATE TABLE `jobs` (
`id` bigint UNSIGNED NOT NULL,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint UNSIGNED NOT NULL,
`reserved_at` int UNSIGNED DEFAULT NULL,
`available_at` int UNSIGNED NOT NULL,
`created_at` int UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `job_batches`
--
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `kelas`
--
CREATE TABLE `kelas` (
`id` bigint UNSIGNED NOT NULL,
`nama_kelas` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`jurusan` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `kelas`
--
INSERT INTO `kelas` (`id`, `nama_kelas`, `jurusan`, `created_at`, `updated_at`) VALUES
(6, 'X IPA 1', 'IPA', '2026-03-30 23:19:53', '2026-03-30 23:19:53'),
(7, 'X IPA 2', 'IPA', '2026-03-30 23:20:08', '2026-03-30 23:20:08'),
(8, 'X IPA 3', 'IPA', '2026-03-30 23:20:16', '2026-03-30 23:20:16'),
(9, 'X IPS 1', 'IPS', '2026-03-30 23:20:33', '2026-03-30 23:20:33'),
(10, 'X IPS 2', 'IPS', '2026-03-30 23:20:44', '2026-03-30 23:20:44'),
(11, 'X IPS 3', 'IPS', '2026-03-30 23:20:57', '2026-03-30 23:20:57');
-- --------------------------------------------------------
--
-- Table structure for table `mata_pelajarans`
--
CREATE TABLE `mata_pelajarans` (
`id` bigint UNSIGNED NOT NULL,
`nama_mapel` varchar(255) NOT NULL,
`kode_mapel` varchar(100) DEFAULT NULL,
`jam_pelajaran` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `mata_pelajarans`
--
INSERT INTO `mata_pelajarans` (`id`, `nama_mapel`, `kode_mapel`, `jam_pelajaran`, `created_at`, `updated_at`) VALUES
(1, 'Matematika', 'MTK', 2, '2026-04-30 10:25:15', '2026-04-30 10:25:15'),
(2, 'Bahasa Indonesia', 'IND', 2, '2026-04-30 10:25:15', '2026-04-30 10:25:15'),
(3, 'Bahasa Inggris', 'ENG', 2, '2026-04-30 10:25:15', '2026-04-30 10:25:15'),
(4, 'Desain Grafis', 'DG', 3, '2026-04-30 10:25:15', '2026-04-30 10:25:15');
-- --------------------------------------------------------
--
-- Table structure for table `migrations`
--
CREATE TABLE `migrations` (
`id` int UNSIGNED NOT NULL,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `migrations`
--
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES
(1, '0001_01_01_000000_create_users_table', 1),
(2, '0001_01_01_000001_create_cache_table', 1),
(3, '0001_01_01_000002_create_jobs_table', 1),
(4, '2025_08_19_084855_create_gurus_table', 1),
(5, '2025_08_20_012827_create_siswa_table', 1),
(6, '2026_03_29_121631_create_mata_pelajarans_table', 1),
(7, '2026_03_29_131627_create_kelas_table', 1),
(8, '2026_03_29_132044_create_jadwals_table', 1),
(9, '2026_03_30_010154_add_role_to_users_table', 2),
(10, '2026_03_31_022301_create_nilai_table', 3),
(11, '2026_04_01_022443_add_photo_to_users_table', 4),
(12, '2026_04_13_142509_add_status_to_gurus_table', 5),
(13, '2026_04_13_145014_add_status_to_siswas_table', 6),
(14, '2026_04_13_154302_add_field_to_siswa_table', 7),
(15, '2026_04_13_155232_add_jenis_kelamin_to_siswa_table', 8),
(16, '2026_04_14_130307_add_is_default_password_to_users', 9),
(17, '2026_04_14_153904_add_otp_to_users_table', 10),
(19, '2026_04_17_084158_create_tugas_table', 1),
(21, '2026_04_16_041005_add_telepon_to_users_table', 10),
(22, '2026_04_17_084159_create_pengumpulan_tugas_table', 10),
(23, '2026_04_17_105854_add_mapel_to_gurus_table', 11),
(24, '2026_04_20_024223_add_file_to_tugas_table', 11),
(25, '2026_04_20_044829_update_deadline_to_datetime_in_tugas', 12),
(26, '2026_04_20_115943_add_komentar_to_pengumpulan_tugas', 13),
(27, '2026_04_20_123724_create_absensis_table', 14),
(28, '2026_04_20_123747_create_absensi_details_table', 14);
-- --------------------------------------------------------
--
-- Table structure for table `nilai`
--
CREATE TABLE `nilai` (
`id` bigint UNSIGNED NOT NULL,
`siswa_id` bigint UNSIGNED NOT NULL,
`mapel_id` bigint UNSIGNED NOT NULL,
`nilai` int NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `nilai`
--
INSERT INTO `nilai` (`id`, `siswa_id`, `mapel_id`, `nilai`, `created_at`, `updated_at`) VALUES
(14, 15, 4, 80, '2026-03-31 23:45:22', '2026-03-31 23:45:22'),
(15, 15, 5, 40, '2026-03-31 23:46:27', '2026-04-21 11:49:48'),
(16, 15, 2, 90, '2026-04-15 21:02:32', '2026-04-15 21:02:32'),
(17, 16, 2, 80, '2026-04-15 21:02:32', '2026-04-15 21:02:32'),
(18, 19, 2, 70, '2026-04-15 21:02:32', '2026-04-15 21:02:32'),
(19, 22, 2, 50, '2026-04-15 21:02:32', '2026-04-15 21:02:32'),
(20, 16, 5, 70, '2026-04-21 11:49:48', '2026-04-21 11:49:48'),
(21, 19, 5, 90, '2026-04-21 11:49:48', '2026-04-21 11:49:48'),
(22, 22, 5, 80, '2026-04-21 11:49:48', '2026-04-21 11:49:48'),
(23, 23, 5, 30, '2026-04-21 11:49:48', '2026-04-21 11:49:48');
-- --------------------------------------------------------
--
-- Table structure for table `password_reset_tokens`
--
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `pengumpulan_tugas`
--
CREATE TABLE `pengumpulan_tugas` (
`id` bigint UNSIGNED NOT NULL,
`tugas_id` bigint UNSIGNED NOT NULL,
`siswa_id` bigint UNSIGNED NOT NULL,
`file` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`nilai` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`komentar` text COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `sessions`
--
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint UNSIGNED DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `sessions`
--
INSERT INTO `sessions` (`id`, `user_id`, `ip_address`, `user_agent`, `payload`, `last_activity`) VALUES
('8UFetMqwkKV2CYFII4GrKKnlo9ZrMrg01oUx17Zd', 44, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36', 'YTo0OntzOjY6Il90b2tlbiI7czo0MDoiajdTc1BwbkdvRkxEQ3VWRG1UaW82eERMaXN4dFVYN2d2cU51YkRzaiI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319czo5OiJfcHJldmlvdXMiO2E6MTp7czozOiJ1cmwiO3M6Mzk6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9ndXJ1L3VqaWFuL2NyZWF0ZSI7fXM6NTA6ImxvZ2luX3dlYl81OWJhMzZhZGRjMmIyZjk0MDE1ODBmMDE0YzdmNThlYTRlMzA5ODlkIjtpOjQ0O30=', 1777544964),
('qR6okGJIUFM6wIISXVaUCFxImbjsZ1LYQixVgnCO', 40, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0', 'YTo0OntzOjY6Il90b2tlbiI7czo0MDoicmFKclU0TVo4d1dLY3BrOFA4bXNQQlRSN1hGRG16QWlSRXJFWnpGZCI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319czo5OiJfcHJldmlvdXMiO2E6MTp7czozOiJ1cmwiO3M6NDQ6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9zaXN3YS91amlhbi8xL2tlcmpha2FuIjt9czo1MDoibG9naW5fd2ViXzU5YmEzNmFkZGMyYjJmOTQwMTU4MGYwMTRjN2Y1OGVhNGUzMDk4OWQiO2k6NDA7fQ==', 1777545038);
-- --------------------------------------------------------
--
-- Table structure for table `siswa`
--
CREATE TABLE `siswa` (
`id` bigint UNSIGNED NOT NULL,
`nama` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`jenis_kelamin` enum('L','P') COLLATE utf8mb4_unicode_ci NOT NULL,
`nama_ortu` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`nis` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`alamat` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`telepon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` enum('aktif','nonaktif') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'aktif',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`kelas_id` bigint UNSIGNED DEFAULT NULL,
`alasan_nonaktif` text COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `siswa`
--
INSERT INTO `siswa` (`id`, `nama`, `jenis_kelamin`, `nama_ortu`, `nis`, `alamat`, `telepon`, `status`, `created_at`, `updated_at`, `kelas_id`, `alasan_nonaktif`) VALUES
(14, 'sipol', 'L', 'sipoladon', '212321321', 'Jember barat timur', '09897865464', 'nonaktif', '2026-03-31 21:13:11', '2026-04-15 21:00:43', 9, NULL),
(15, 'Fuad', 'L', '', '1213213', 'Jember barat timur', '09897865464', 'nonaktif', '2026-03-31 21:23:26', '2026-04-15 21:00:43', 6, NULL),
(16, 'ilham', 'L', '', '3243234', 'dsfdfs32', '234234124124124', 'nonaktif', '2026-04-13 07:20:20', '2026-04-15 21:00:43', 6, NULL),
(17, 'bayu akbar', 'L', '', '35423412', 'Bondowoso', '08976544534', 'nonaktif', '2026-04-13 08:32:46', '2026-04-15 21:00:43', 8, NULL),
(18, 'Gavin', 'L', 'Gavinaldo', '41412312', 'Bondowoso', '08931231252', 'nonaktif', '2026-04-13 08:53:17', '2026-04-15 21:00:43', 9, NULL),
(19, 'Irul', 'L', 'khoirul', '412421', 'Bondowoso', '08978675453', 'nonaktif', '2026-04-14 06:31:15', '2026-04-15 21:00:43', 6, NULL),
(20, 'Fauzi', 'L', 'Sukri', '758567', 'Bondowoso', '08975643221', 'nonaktif', '2026-04-14 06:51:57', '2026-04-15 21:00:43', 8, NULL),
(21, 'hardi', 'P', 'memo', '121312', 'Bondowoso', '0893723132', 'nonaktif', '2026-04-14 06:57:42', '2026-04-15 21:00:43', 8, NULL),
(22, 'Maulana', 'L', 'Gavinaldo', '21313', 'Surabaya', '08954224122', 'aktif', '2026-04-14 07:14:06', '2026-04-28 08:01:10', 6, NULL),
(23, 'Siswa Rengga', 'L', 'Andik', '345333242', 'Bondowoso', '08977654321233', 'aktif', '2026-04-19 21:55:36', '2026-04-19 21:55:36', 6, NULL),
(24, 'Bayu anggara', 'P', 'sipoladon', '3242341', 'Bondowoso', '08978546377332', 'aktif', '2026-04-20 04:33:22', '2026-04-20 04:33:22', 7, NULL),
(35, 'rayenn', 'L', 'Sudik', '35465686', 'surabaya', '6281252519535', 'nonaktif', '2026-04-23 22:22:03', '2026-04-28 08:30:41', 6, 'karena sudah di do');
-- --------------------------------------------------------
--
-- Table structure for table `soals`
--
CREATE TABLE `soals` (
`id` bigint UNSIGNED NOT NULL,
`ujian_id` bigint UNSIGNED DEFAULT NULL,
`pertanyaan` text,
`a` text,
`b` text,
`c` text,
`d` text,
`jawaban_benar` enum('a','b','c','d') DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `soals`
--
INSERT INTO `soals` (`id`, `ujian_id`, `pertanyaan`, `a`, `b`, `c`, `d`, `jawaban_benar`, `created_at`, `updated_at`) VALUES
(1, 1, 'rumah rengga dimana', 'kembang', 'sukowiryo', 'duko', 'nangkaan', 'a', '2026-04-30 09:06:48', '2026-04-30 09:06:48');
-- --------------------------------------------------------
--
-- Table structure for table `tugas`
--
CREATE TABLE `tugas` (
`id` bigint UNSIGNED NOT NULL,
`guru_id` bigint UNSIGNED NOT NULL,
`kelas_id` bigint UNSIGNED NOT NULL,
`mapel_id` bigint UNSIGNED NOT NULL,
`judul` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`deskripsi` text COLLATE utf8mb4_unicode_ci NOT NULL,
`deadline` datetime NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`file` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `tugas`
--
INSERT INTO `tugas` (`id`, `guru_id`, `kelas_id`, `mapel_id`, `judul`, `deskripsi`, `deadline`, `created_at`, `updated_at`, `file`) VALUES
(18, 39, 6, 7, 'minggu 10', 'adasdasdas', '2026-04-22 10:05:00', '2026-04-22 03:05:05', '2026-04-22 03:05:05', 'tugas_file/SKQvPt7XGSUxDILwzB1uPfhMYmHlTWFiVkC2tKpR.pdf'),
(19, 39, 7, 7, 'minggu 9', 'dwqdasd', '2026-04-22 10:06:00', '2026-04-22 03:06:13', '2026-04-22 03:06:13', 'tugas_file/KpYeWe5f60Awf51go9lw8DvFh3mC45Dlx8v0CkMm.pdf');
-- --------------------------------------------------------
--
-- Table structure for table `ujians`
--
CREATE TABLE `ujians` (
`id` bigint UNSIGNED NOT NULL,
`judul` varchar(255) DEFAULT NULL,
`durasi` int DEFAULT NULL,
`mulai` datetime DEFAULT NULL,
`selesai` datetime DEFAULT NULL,
`guru_id` bigint UNSIGNED DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `ujians`
--
INSERT INTO `ujians` (`id`, `judul`, `durasi`, `mulai`, `selesai`, `guru_id`, `created_at`, `updated_at`) VALUES
(1, 'UTS Desain Grafis', 45, '2026-04-30 16:00:00', '2026-04-30 16:45:00', 39, '2026-04-30 09:06:48', '2026-04-30 09:06:48');
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` bigint UNSIGNED NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`role` enum('admin','guru','siswa') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'siswa',
`guru_id` bigint UNSIGNED DEFAULT NULL,
`siswa_id` bigint UNSIGNED DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`mapel_id` bigint DEFAULT NULL,
`photo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`is_default_password` tinyint(1) NOT NULL DEFAULT '1',
`otp` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`otp_expired_at` timestamp NULL DEFAULT NULL,
`telepon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `name`, `email`, `password`, `role`, `guru_id`, `siswa_id`, `remember_token`, `created_at`, `updated_at`, `mapel_id`, `photo`, `is_default_password`, `otp`, `otp_expired_at`, `telepon`) VALUES
(1, 'Admin', 'admin@gmail.com', '$2y$12$nYOJKfRSziDIvXdFLMSej.RrniIDukDG6Z6tNt6zmZ3pSGOmFPsn.', 'admin', 2, NULL, NULL, '2026-03-29 20:52:52', '2026-04-15 20:03:42', NULL, '1775011418.jpg', 0, NULL, NULL, NULL),
(4, 'Siswa 1', 'siswa@gmail.com', '$2y$12$sDRQgwkz34hk3ru6AitfBegN9mS30Z9BHZMw85f8QRxrQZcMgGeQ.', 'siswa', NULL, NULL, NULL, '2026-03-29 20:53:52', '2026-03-29 20:53:52', NULL, NULL, 1, NULL, NULL, NULL),
(5, 'Ryan MAulana', 'guru1@gmail.com', '$2y$12$mLnhDhHvTnVCEMhFtPxCkeBFEfggfkCifZ6yrRt19QV06uF.nH2P6', 'guru', NULL, NULL, NULL, '2026-03-30 20:55:29', '2026-03-30 20:55:29', NULL, NULL, 1, NULL, NULL, NULL),
(6, 'zakaria', 'zaka@gmail.com', '$2y$12$GCbsvTxo5x1s3J8Xm4Lnxe0AiLQCPpYgcgR5ZP4/xsjdROf/Z1pFe', 'guru', 3, NULL, NULL, '2026-03-30 21:17:29', '2026-03-30 21:17:29', NULL, NULL, 1, NULL, NULL, NULL),
(7, 'adis bauk', 'adis12@gmail.com', '$2y$12$f2ezxB.v.z25y/Cz0JyfEun7Mh0JU9z/LXwF5pYy9ry.q.jfqviY.', 'guru', NULL, NULL, NULL, '2026-03-30 21:30:30', '2026-03-30 21:30:30', NULL, NULL, 1, NULL, NULL, NULL),
(8, 'asca2421', 'erinefarah@gmail.com', '$2y$12$3kgiNhZ9cEOsomVLjo95guzCzo78aXWRoB.TYdwIzzNXQOZH39kCC', 'guru', NULL, NULL, NULL, '2026-03-30 21:33:42', '2026-03-30 21:33:42', NULL, NULL, 1, NULL, NULL, NULL),
(9, 'Reyfandi', 'kelompok1@gmail.com', '$2y$12$4WkNPHZ7wThq9KVdY7FZLuCvDR.p1cqUsZdIyzxPQ2GlTxg/u5OFS', 'guru', 10, NULL, NULL, '2026-03-30 21:40:12', '2026-03-30 21:40:12', 2, NULL, 1, NULL, NULL, NULL),
(16, 'sipol', 'sipol@gmail.com', '$2y$12$P19iPvgcI3nmTQjUItQOvu7krp9tt.8PIsbPzrv6A8u9Z9w0todQC', 'siswa', NULL, 14, NULL, '2026-03-31 21:13:11', '2026-03-31 21:13:11', NULL, NULL, 1, NULL, NULL, NULL),
(17, 'Fuad', 'fuad@gmail.com', '$2y$12$vaMwtmvqbSK/rZ5XdA6K7.VW5WiCudpHfro0.OjZYFuBiMJ2DgDJi', 'siswa', NULL, 15, NULL, '2026-03-31 21:23:26', '2026-03-31 21:23:26', NULL, NULL, 1, NULL, NULL, NULL),
(18, 'ilham', 'ilham@gmail.com', '$2y$12$MUBrQ7UGm1pP5Rb8mUVtMOjHaqKeeEh3etLcmwD.Krf4WPJQwwNz2', 'siswa', NULL, 16, NULL, '2026-04-13 07:20:21', '2026-04-13 07:20:21', NULL, NULL, 1, NULL, NULL, NULL),
(19, 'bayu akbar', 'bayuakbar@gmail.com', '$2y$12$gq9mN9dlZdDLKwq1CgrpUO03hQYcV.UrwttALNo0sDb7nrvPP7P1K', 'siswa', NULL, 17, NULL, '2026-04-13 08:32:47', '2026-04-13 08:32:47', NULL, NULL, 1, NULL, NULL, NULL),
(20, 'Gavin', 'gavin@gmail.com', '$2y$12$sNUy3C7bXC121Z8b4CP3HO91LiCNmQydgA.Bm4rt7ChUNw86UHdgG', 'siswa', NULL, 18, NULL, '2026-04-13 08:53:17', '2026-04-13 08:53:17', NULL, NULL, 1, NULL, NULL, NULL),
(21, 'Irul', 'irul@gmail.com', '$2y$12$tWo5A.QaV4ROnck1WJ0qSucrAEIxKc1JYuxyGlYf9XZ7VjNvPh8fa', 'siswa', NULL, 19, NULL, '2026-04-14 06:31:15', '2026-04-14 06:41:38', NULL, NULL, 1, NULL, NULL, NULL),
(22, 'Fauzi', 'fauzi@gmail.com', '$2y$12$LEnvbaAeEoMaK19M7GZCHeoaxgdLYkay58dtVI3Q8dm8rAHfNvSSK', 'siswa', NULL, 20, NULL, '2026-04-14 06:51:57', '2026-04-14 06:51:57', NULL, NULL, 1, NULL, NULL, NULL),
(23, 'hardi', 'hardi@gmail.com', '$2y$12$RVSNOsL7WFRojnhfU6MMFOMD4tkAiv4YWG1pzZS0gFu5PFaHzC7v.', 'siswa', NULL, 21, NULL, '2026-04-14 06:57:42', '2026-04-14 07:03:41', NULL, NULL, 1, NULL, NULL, NULL),
(24, 'Maulana', 'maulana@gmail.com', '$2y$12$GsuWYF.uc0vdCITV8YuLDegOI58BEvJZ8XbIHN0ELi7GBQMjAqr5a', 'siswa', NULL, 22, NULL, '2026-04-14 07:14:06', '2026-04-14 07:14:37', NULL, NULL, 0, NULL, NULL, NULL),
(39, 'zakaria', 'mlnryan05@gmail.com', '$2y$12$yZ5p1XhIGhjdUxXNvyML5uVHNDfzMd4d95oI0T3iEvXZZ2f7RYaWq', 'guru', 36, NULL, NULL, '2026-04-19 21:18:08', '2026-04-19 21:18:08', 2, NULL, 1, NULL, NULL, '089785463773'),
(40, 'Siswa Rengga', 'renggalaviosa@gmail.com', '$2y$12$XI.nsw906.wBE8hvepLLr.oVzgpH/teA5Qlqp7eDQ1YiKNyQRKbLG', 'siswa', NULL, 23, NULL, '2026-04-19 21:55:37', '2026-04-30 09:15:59', NULL, NULL, 0, NULL, NULL, NULL),
(41, 'Bayu anggara', 'bayu@gmail.com', '$2y$12$TuNb6v/RBJF6TaIpFm0dFu9PTkrQrMvMcWq2QFoIck..uwvDsnLZC', 'siswa', NULL, 24, NULL, '2026-04-20 04:33:22', '2026-04-20 04:33:51', NULL, NULL, 0, NULL, NULL, NULL),
(42, 'Umam', 'umam@gmail.com', '$2y$12$psH1qXXs6kgk1jNJJ7Wpt.w7fPxm9wuSFUxWAT5m5GlO8UeIRHOta', 'guru', 37, NULL, NULL, '2026-04-21 11:52:56', '2026-04-21 11:53:39', 3, NULL, 0, NULL, NULL, '0879635231'),
(44, 'Arengga', 'renggalaviosa78@gmail.com', '$2y$12$0XiSSR2G/pmqIxMY7vNJPei0.w.3n/JgD0hnJ45Rikwl.0vwsTfpW', 'guru', 39, NULL, NULL, '2026-04-22 02:17:15', '2026-04-30 10:29:18', 7, NULL, 0, NULL, NULL, '6289504732922'),
(55, 'rayenn', 'nunieiffel@gmail.com', '$2y$12$gDoPf94nRLUr9gXKefPW4ereEctEZwJb3JzhUx6yJB5nEmnzGwh4O', 'siswa', NULL, 35, NULL, '2026-04-23 22:22:03', '2026-04-23 22:22:03', NULL, NULL, 1, NULL, NULL, NULL);
--
-- Indexes for dumped tables
--
--
-- Indexes for table `absensi`
--
ALTER TABLE `absensi`
ADD PRIMARY KEY (`id`),
ADD KEY `guru_id` (`guru_id`),
ADD KEY `kelas_id` (`kelas_id`),
ADD KEY `mapel_id` (`mapel_id`);
--
-- Indexes for table `absensi_detail`
--
ALTER TABLE `absensi_detail`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `unique_absensi` (`absensi_id`,`siswa_id`),
ADD KEY `siswa_id` (`siswa_id`);
--
-- Indexes for table `cache`
--
ALTER TABLE `cache`
ADD PRIMARY KEY (`key`);
--
-- Indexes for table `cache_locks`
--
ALTER TABLE `cache_locks`
ADD PRIMARY KEY (`key`);
--
-- Indexes for table `failed_jobs`
--
ALTER TABLE `failed_jobs`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`);
--
-- Indexes for table `gurus`
--
ALTER TABLE `gurus`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `gurus_nip_unique` (`nip`);
--
-- Indexes for table `hasils`
--
ALTER TABLE `hasils`
ADD PRIMARY KEY (`id`),
ADD KEY `ujian_id` (`ujian_id`);
--
-- Indexes for table `jadwals`
--
ALTER TABLE `jadwals`
ADD PRIMARY KEY (`id`),
ADD KEY `jadwals_kelas_id_foreign` (`kelas_id`),
ADD KEY `jadwals_mata_pelajaran_id_foreign` (`mata_pelajaran_id`);
--
-- Indexes for table `jawabans`
--
ALTER TABLE `jawabans`
ADD PRIMARY KEY (`id`),
ADD KEY `siswa_id` (`siswa_id`),
ADD KEY `soal_id` (`soal_id`);
--
-- Indexes for table `jawaban_siswas`
--
ALTER TABLE `jawaban_siswas`
ADD PRIMARY KEY (`id`),
ADD KEY `soal_id` (`soal_id`);
--
-- Indexes for table `jobs`
--
ALTER TABLE `jobs`
ADD PRIMARY KEY (`id`),
ADD KEY `jobs_queue_index` (`queue`);
--
-- Indexes for table `job_batches`
--
ALTER TABLE `job_batches`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `kelas`
--
ALTER TABLE `kelas`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `mata_pelajarans`
--
ALTER TABLE `mata_pelajarans`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `kode_mapel` (`kode_mapel`);
--
-- Indexes for table `migrations`
--
ALTER TABLE `migrations`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `nilai`
--
ALTER TABLE `nilai`
ADD PRIMARY KEY (`id`),
ADD KEY `nilai_siswa_id_foreign` (`siswa_id`),
ADD KEY `nilai_mapel_id_foreign` (`mapel_id`);
--
-- Indexes for table `password_reset_tokens`
--
ALTER TABLE `password_reset_tokens`
ADD PRIMARY KEY (`email`);
--
-- Indexes for table `pengumpulan_tugas`
--
ALTER TABLE `pengumpulan_tugas`
ADD PRIMARY KEY (`id`),
ADD KEY `pengumpulan_tugas_tugas_id_foreign` (`tugas_id`);
--
-- Indexes for table `sessions`
--
ALTER TABLE `sessions`
ADD PRIMARY KEY (`id`),
ADD KEY `sessions_user_id_index` (`user_id`),
ADD KEY `sessions_last_activity_index` (`last_activity`);
--
-- Indexes for table `siswa`
--
ALTER TABLE `siswa`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `siswa_nis_unique` (`nis`),
ADD KEY `fk_kelas` (`kelas_id`);
--
-- Indexes for table `soals`
--
ALTER TABLE `soals`
ADD PRIMARY KEY (`id`),
ADD KEY `ujian_id` (`ujian_id`);
--
-- Indexes for table `tugas`
--
ALTER TABLE `tugas`
ADD PRIMARY KEY (`id`),
ADD KEY `tugas_guru_id_foreign` (`guru_id`),
ADD KEY `tugas_kelas_id_foreign` (`kelas_id`);
--
-- Indexes for table `ujians`
--
ALTER TABLE `ujians`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `users_email_unique` (`email`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `absensi`
--
ALTER TABLE `absensi`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT for table `absensi_detail`
--
ALTER TABLE `absensi_detail`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
--
-- AUTO_INCREMENT for table `failed_jobs`
--
ALTER TABLE `failed_jobs`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `gurus`
--
ALTER TABLE `gurus`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=40;
--
-- AUTO_INCREMENT for table `hasils`
--
ALTER TABLE `hasils`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `jadwals`
--
ALTER TABLE `jadwals`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;
--
-- AUTO_INCREMENT for table `jawabans`
--
ALTER TABLE `jawabans`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `jawaban_siswas`
--
ALTER TABLE `jawaban_siswas`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `jobs`
--
ALTER TABLE `jobs`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `kelas`
--
ALTER TABLE `kelas`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12;
--
-- AUTO_INCREMENT for table `mata_pelajarans`
--
ALTER TABLE `mata_pelajarans`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT for table `migrations`
--
ALTER TABLE `migrations`
MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=29;
--
-- AUTO_INCREMENT for table `nilai`
--
ALTER TABLE `nilai`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=24;
--
-- AUTO_INCREMENT for table `pengumpulan_tugas`
--
ALTER TABLE `pengumpulan_tugas`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
--
-- AUTO_INCREMENT for table `siswa`
--
ALTER TABLE `siswa`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=36;
--
-- AUTO_INCREMENT for table `soals`
--
ALTER TABLE `soals`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `tugas`
--
ALTER TABLE `tugas`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=20;
--
-- AUTO_INCREMENT for table `ujians`
--
ALTER TABLE `ujians`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=56;
--
-- Constraints for dumped tables
--
--
-- Constraints for table `absensi`
--
ALTER TABLE `absensi`
ADD CONSTRAINT `absensi_ibfk_1` FOREIGN KEY (`guru_id`) REFERENCES `gurus` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `absensi_ibfk_2` FOREIGN KEY (`kelas_id`) REFERENCES `kelas` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `absensi_ibfk_3` FOREIGN KEY (`mapel_id`) REFERENCES `mata_pelajarans` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `absensi_detail`
--
ALTER TABLE `absensi_detail`
ADD CONSTRAINT `absensi_detail_ibfk_1` FOREIGN KEY (`absensi_id`) REFERENCES `absensi` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `absensi_detail_ibfk_2` FOREIGN KEY (`siswa_id`) REFERENCES `siswa` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `hasils`
--
ALTER TABLE `hasils`
ADD CONSTRAINT `hasils_ibfk_1` FOREIGN KEY (`ujian_id`) REFERENCES `ujians` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `jadwals`
--
ALTER TABLE `jadwals`
ADD CONSTRAINT `jadwals_kelas_id_foreign` FOREIGN KEY (`kelas_id`) REFERENCES `kelas` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `jadwals_mata_pelajaran_id_foreign` FOREIGN KEY (`mata_pelajaran_id`) REFERENCES `mata_pelajarans` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `jawabans`
--
ALTER TABLE `jawabans`
ADD CONSTRAINT `jawabans_ibfk_1` FOREIGN KEY (`siswa_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `jawabans_ibfk_2` FOREIGN KEY (`soal_id`) REFERENCES `soals` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `jawaban_siswas`
--
ALTER TABLE `jawaban_siswas`
ADD CONSTRAINT `jawaban_siswas_ibfk_1` FOREIGN KEY (`soal_id`) REFERENCES `soals` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `nilai`
--
ALTER TABLE `nilai`
ADD CONSTRAINT `nilai_mapel_id_foreign` FOREIGN KEY (`mapel_id`) REFERENCES `mata_pelajarans` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `nilai_siswa_id_foreign` FOREIGN KEY (`siswa_id`) REFERENCES `siswa` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `pengumpulan_tugas`
--
ALTER TABLE `pengumpulan_tugas`
ADD CONSTRAINT `pengumpulan_tugas_tugas_id_foreign` FOREIGN KEY (`tugas_id`) REFERENCES `tugas` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `siswa`
--
ALTER TABLE `siswa`
ADD CONSTRAINT `fk_kelas` FOREIGN KEY (`kelas_id`) REFERENCES `kelas` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `soals`
--
ALTER TABLE `soals`
ADD CONSTRAINT `soals_ibfk_1` FOREIGN KEY (`ujian_id`) REFERENCES `ujians` (`id`) ON DELETE CASCADE;
--
-- Constraints for table `tugas`
--
ALTER TABLE `tugas`
ADD CONSTRAINT `tugas_guru_id_foreign` FOREIGN KEY (`guru_id`) REFERENCES `gurus` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `tugas_kelas_id_foreign` FOREIGN KEY (`kelas_id`) REFERENCES `kelas` (`id`) ON DELETE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;