qrLinkService = $qrLinkService; } public function index() { $kelas = Classes::all(); $mapel = Subject::all(); // Join teachers dengan subjects berdasarkan kolom subject (code) $guru = Teacher::all()->map(function($teacher) { // Cari subject berdasarkan code yang ada di kolom subject teacher // Gunakan array_map('trim', ...) untuk menghilangkan spasi sebelum/sesudah koma $subjectCodes = array_map('trim', explode(',', $teacher->subject)); $subjects = Subject::whereIn('code', $subjectCodes)->pluck('id')->toArray(); $teacher->subject_ids = $subjects; return $teacher; }); $periodes = AcademicPeriod::orderBy('start_date', 'desc')->get(); $periode_aktif = AcademicPeriod::where('is_active', true)->first(); $jadwal = Schedule::with(['class', 'subject', 'teacher'])->get(); return view('pages.jadwal.jadwal', compact('kelas', 'mapel', 'guru', 'periodes', 'periode_aktif', 'jadwal')); } public function store(Request $request) { $validated = $request->validate([ 'day_of_week' => 'required|string', 'period_start' => 'required|integer', 'period_end' => 'required|integer', 'start_time' => 'required|string', 'end_time' => 'required|string', 'code' => 'nullable|string', 'id_class' => 'required|exists:clases,id', 'id_subject' => 'required|exists:subjects,id', 'id_teacher' => 'required|exists:teachers,id', 'id_academic_periods' => 'nullable|exists:academic_periods,id', ]); // Hitung jam mulai dan jam selesai $startHour = 7 + ($validated['period_start'] - 1); $endHour = 7 + ($validated['period_end'] - 1); $start_time = sprintf('%02d:00', $startHour); $end_time = sprintf('%02d:00', $endHour); // Ambil data untuk kode otomatis $class = Classes::find($validated['id_class']); $subject = Subject::find($validated['id_subject']); $period = $validated['id_academic_periods'] ?? ''; $token = (string) random_int(1000, 9999); $day = strtoupper(substr($validated['day_of_week'], 0, 3)); $className = $class ? $class->name : ''; $subjectCode = $subject ? $subject->code : ''; $periodCode = $period ?: '0'; $autoCode = "{$className}-{$day}-{$validated['period_start']}-{$validated['period_end']}-{$subjectCode}-{$periodCode}-{$token}"; $code = $request->input('code') ?: $autoCode; // Hash code sebelum simpan ke database $hashedCode = Crypt::encryptString($code); Schedule::create([ // make day of week to lowercase 'day_of_week' => strtolower($validated['day_of_week']), 'period_start' => $validated['period_start'], 'period_end' => $validated['period_end'], 'start_time' => $validated['start_time'], 'end_time' => $validated['end_time'], 'code' => $hashedCode, 'id_class' => $validated['id_class'], 'id_subject' => $validated['id_subject'], 'id_teacher' => $validated['id_teacher'], 'id_academic_periods' => $validated['id_academic_periods'] ?? null, ]); return redirect()->route('jadwal.index')->with('success', 'Jadwal berhasil ditambahkan!'); } public function update(Request $request, $id) { $validated = $request->validate([ 'day_of_week' => 'required|string', 'period_start' => 'required|integer', 'period_end' => 'required|integer', 'start_time' => 'required|string', 'end_time' => 'required|string', 'code' => 'required|string', 'id_class' => 'required|exists:clases,id', 'id_subject' => 'required|exists:subjects,id', 'id_teacher' => 'required|exists:teachers,id', 'id_academic_periods' => 'nullable|exists:academic_periods,id', ]); // Hitung jam mulai dan jam selesai $startHour = 7 + ($validated['period_start'] - 1); $endHour = 7 + ($validated['period_end'] - 1); $start_time = sprintf('%02d:00', $startHour); $end_time = sprintf('%02d:00', $endHour); // Ambil schedule lama untuk cek perubahan dan avoid double encryption $jadwalLama = Schedule::findOrFail($id); // Cek apakah ada perubahan di field-field yang mempengaruhi code $codeChanged = false; if ($jadwalLama->day_of_week != strtolower($validated['day_of_week']) || $jadwalLama->period_start != $validated['period_start'] || $jadwalLama->period_end != $validated['period_end'] || $jadwalLama->id_subject != $validated['id_subject'] || $jadwalLama->id_class != $validated['id_class'] || $jadwalLama->id_academic_periods != $validated['id_academic_periods']) { $codeChanged = true; } // Generate code baru jika ada perubahan, atau gunakan code lama if ($codeChanged) { // Generate code baru $class = Classes::find($validated['id_class']); $subject = Subject::find($validated['id_subject']); $period = $validated['id_academic_periods'] ?? ''; $token = (string) random_int(1000, 9999); $day = strtoupper(substr($validated['day_of_week'], 0, 3)); $className = $class ? $class->name : ''; $subjectCode = $subject ? $subject->code : ''; $periodCode = $period ?: '0'; $autoCode = "{$className}-{$day}-{$validated['period_start']}-{$validated['period_end']}-{$subjectCode}-{$periodCode}-{$token}"; $hashedCode = Crypt::encryptString($autoCode); } else { // Gunakan code lama tanpa encrypt ulang (avoid double encryption) $hashedCode = $jadwalLama->code; } $jadwalLama->update([ 'day_of_week' => strtolower($validated['day_of_week']), 'period_start' => $validated['period_start'], 'period_end' => $validated['period_end'], 'start_time' => $validated['start_time'], 'end_time' => $validated['end_time'], 'code' => $hashedCode, 'id_class' => $validated['id_class'], 'id_subject' => $validated['id_subject'], 'id_teacher' => $validated['id_teacher'], 'id_academic_periods' => $validated['id_academic_periods'] ?? null, ]); return redirect()->route('jadwal.index')->with('success', 'Jadwal berhasil diupdate!'); } public function destroy(Request $request) { $ids = $request->input('ids'); if (!$ids) { return redirect()->route('jadwal.index')->with('error', 'Tidak ada jadwal yang dipilih untuk dihapus.'); } // Ubah string menjadi array jika perlu if (is_string($ids)) { $ids = explode(',', $ids); } Schedule::destroy($ids); return redirect()->route('jadwal.index')->with('success', 'Jadwal berhasil dihapus!'); } public function showQr($id) { $jadwal = Schedule::findOrFail($id); return view('pages.jadwal.qr', compact('jadwal')); } public function showPublicQr($token) { $tokenData = Cache::get("public_qr_link:$token"); if (!$tokenData) { return view('public.attendance.qr-error', [ 'status' => 'expired', 'message' => 'Link QR sudah expired atau tidak valid. Silakan minta link baru dari guru.' ]); } $idSchedule = $tokenData['schedule_id']; $expiresAt = $tokenData['expires_at']; $jadwal = Schedule::with(['class', 'subject', 'teacher'])->find($idSchedule); if (!$jadwal) { return view('public.attendance.qr-error', [ 'status' => 'notfound', 'message' => 'Data jadwal tidak ditemukan.' ]); } $publicUrl = route('jadwal.qr.public', ['token' => $token]); return view('public.attendance.qr', compact('jadwal', 'publicUrl', 'expiresAt')); } public function generateQrLink($id) { $result = $this->qrLinkService->generateLink($id); return response()->json($result); } }