session()->get('legacy_auth'); abort_unless(($auth['role'] ?? null) === 'dosen', 403); $user = $auth['user']; $cachePrefix = 'rebuild:dosen:dashboard:'.$user['prodi'].':'.$user['id']; $today = now(); $startOfMonth = $today->copy()->startOfMonth(); $endOfMonth = $today->copy()->endOfMonth(); $unreadAnnouncements = Cache::remember($cachePrefix.':unread-announcements', 60, function () use ($user) { return DB::table('tbpengumuman') ->where('idProdi', $user['prodi']) ->whereIn('tujuan', ['A', 'D']) ->whereNotIn('id', function ($query) use ($user) { $query->select('idkonten') ->from('tmp_notif') ->where('iduser', $user['id']) ->where('typeuser', 'D') ->where('jenis', 'P'); }) ->count(); }); $unreadProposals = Cache::remember($cachePrefix.':unread-proposals', 60, function () use ($user) { return DB::table('tbpraoutline') ->where('status_usulan', '0') ->whereNotIn('id', function ($query) use ($user) { $query->select('idkonten') ->from('tmp_notif') ->where('iduser', $user['id']) ->where('typeuser', 'D') ->where('jenis', 'J'); }) ->count(); }); $scheduleRows = Cache::remember($cachePrefix.':calendar:'.$startOfMonth->format('Y-m'), 60, function () use ($user, $startOfMonth, $endOfMonth) { return DB::table('tbjadwal as tj') ->leftJoin('tbmhs as tm', 'tm.idmhs', '=', 'tj.idmhs') ->select([ 'tj.id', 'tj.jenis', 'tj.start', 'tm.nmLengkap', ]) ->where('tj.publish', 'Y') ->where('tj.idProdi', $user['prodi']) ->whereBetween('tj.start', [$startOfMonth->toDateTimeString(), $endOfMonth->copy()->endOfDay()->toDateTimeString()]) ->orderBy('tj.start') ->get(); }); $upcomingSchedules = Cache::remember($cachePrefix.':upcoming', 60, function () use ($user, $today) { return DB::table('tbjadwal as tj') ->leftJoin('tbmhs as tm', 'tm.idmhs', '=', 'tj.idmhs') ->select([ 'tj.id', 'tj.jenis', 'tj.start', 'tj.ruangan', 'tj.judul', 'tm.nim', 'tm.nmLengkap', ]) ->where('tj.publish', 'Y') ->where('tj.idProdi', $user['prodi']) ->whereNotNull('tj.start') ->where('tj.start', '>=', $today->copy()->startOfDay()->toDateTimeString()) ->orderBy('tj.start') ->limit(5) ->get() ->map(fn ($row) => [ 'id' => $row->id, 'jenis' => $this->scheduleLabel($row->jenis), 'jenisClass' => $this->scheduleBadgeClass($row->jenis), 'nama' => $row->nmLengkap ?: 'Mahasiswa tidak ditemukan', 'nim' => $row->nim ?: '-', 'judul' => $row->judul ?: 'Judul belum tersedia', 'ruangan' => $row->ruangan ?: '-', 'tanggal' => $this->formatIndonesianDateTime($row->start), ]) ->all(); }); $calendar = $this->buildCalendar($scheduleRows); $dashboard = [ 'dateLabel' => $this->formatIndonesianDateTime(now()->toDateTimeString()), 'pageTitle' => 'Dashboard', 'sidebar' => DosenNavigation::build('dashboard.dosen'), 'menus' => [ ['title' => 'Dashboard', 'href' => route('dashboard.dosen'), 'icon' => 'home', 'active' => true], ], 'legacyMenus' => [ 'Utama' => [ ['title' => 'Penawaran Judul', 'href' => $this->legacyUrl('dosen/dashboard.php?page=penawaran&menu=list-judul-saya'), 'icon' => 'briefcase'], ], 'Tugas Akhir 1' => [ ['title' => 'Daftar Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=new'), 'icon' => 'folder'], ['title' => 'Review Saya', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=myreview'), 'icon' => 'chat'], ['title' => 'Pencarian Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=cari'), 'icon' => 'search'], ['title' => 'Daftar Bimbingan Saya', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=keputusan'), 'icon' => 'users'], ['title' => 'Statistik Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=statistik'), 'icon' => 'chart'], ['title' => 'Pemberitahuan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=pemberitahuan'), 'icon' => 'bell'], ], 'Lainnya' => [ ['title' => 'Pengumuman', 'href' => $this->legacyUrl('dosen/dashboard.php?page=pengumuman'), 'icon' => 'megaphone'], ['title' => 'Akun Pengguna', 'href' => $this->legacyUrl('dosen/dashboard.php?page=user&menu=my-profile'), 'icon' => 'user'], ['title' => 'Dokumen Sidang', 'href' => 'https://edoxid.untan.ac.id/', 'icon' => 'document'], ['title' => 'Early Warning', 'href' => $this->legacyUrl('dosen/dashboard.php?page=early-warning'), 'icon' => 'warning'], ['title' => 'Konsultasi Skripsi', 'href' => 'https://spota.untan.ac.id/konsultasi/', 'icon' => 'chat'], ['title' => 'Statistik Seminar', 'href' => 'https://spota.untan.ac.id/cek_banyak_sidang.php', 'icon' => 'chart'], ['title' => 'Konsultasi KP', 'href' => 'https://informatika.untan.ac.id/konsultasi/', 'icon' => 'chat'], ['title' => 'Pra LIRS (Dosen PA)', 'href' => $this->legacyUrl('dosen/dashboard.php?page=pra-lirs'), 'icon' => 'clipboard'], ['title' => 'Evaluasi Mahasiswa', 'href' => 'https://spota.untan.ac.id/steven/rekapMahasiswaEvaluasi.php?angkatan='.(date('Y') - 5).'&show=belumlulus', 'icon' => 'chart'], ], ], 'welcomeTitle' => 'Yth. Bapak/Ibu '.$user['nama_lengkap'], 'welcomeText' => 'Selamat datang di Sistem Pendukung Outline Tugas Akhir (SPOTA) Universitas Tanjungpura', 'androidLink' => $user['prodi'] === '2' ? $this->legacyUrl('spotaif.apk') : null, 'announcementNotice' => [ 'count' => $unreadAnnouncements, 'title' => 'Pengumuman Terbaru', 'message' => $unreadAnnouncements > 0 ? 'Terdapat '.$unreadAnnouncements.' Pengumuman Terbaru Yang Belum Dibaca' : 'Tidak Ada Pengumuman Terbaru', 'primaryLabel' => 'Lihat Semua Pengumuman', 'primaryHref' => $this->legacyUrl('dosen/dashboard.php?page=pengumuman'), ], 'proposalNotice' => [ 'count' => $unreadProposals, 'title' => 'Usulan Terbaru', 'message' => $unreadProposals > 0 ? 'Terdapat '.$unreadProposals.' Usulan Terbaru.' : 'Tidak terdapat Usulan terbaru.', 'primaryLabel' => $unreadProposals > 0 ? 'Lihat Usulan Terbaru' : 'Lihat Semua Usulan', 'primaryHref' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=new'), 'secondaryLabel' => $unreadProposals > 0 ? null : 'Cari Usulan', 'secondaryHref' => $unreadProposals > 0 ? null : $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=cari'), ], 'calendar' => $calendar, 'upcomingSchedules' => $upcomingSchedules, ]; return view('dashboard.dosen', [ 'title' => 'Dashboard Dosen | SPOTA Rebuild', 'dashboard' => $dashboard, 'user' => $user, ]); } private function buildCalendar($scheduleRows): array { $today = now(); $startOfMonth = $today->copy()->startOfMonth(); $endOfMonth = $today->copy()->endOfMonth(); $startOfGrid = $startOfMonth->copy()->startOfWeek(Carbon::MONDAY); $endOfGrid = $endOfMonth->copy()->endOfWeek(Carbon::SUNDAY); $eventsByDate = []; foreach ($scheduleRows as $row) { if (! $row->start || strtotime($row->start) === false) { continue; } $dateKey = Carbon::parse($row->start)->toDateString(); $eventsByDate[$dateKey][] = [ 'id' => $row->id, 'title' => ucwords(strtolower((string) ($row->nmLengkap ?: 'Tanpa Nama'))), 'jenis' => $this->scheduleLabel($row->jenis), 'className' => $this->scheduleEventClass($row->jenis), ]; } $weeks = []; $cursor = $startOfGrid->copy(); while ($cursor->lessThanOrEqualTo($endOfGrid)) { $week = []; for ($day = 0; $day < 7; $day++) { $dateKey = $cursor->toDateString(); $week[] = [ 'date' => $dateKey, 'day' => $cursor->day, 'isCurrentMonth' => $cursor->month === $today->month, 'isToday' => $cursor->isToday(), 'events' => $eventsByDate[$dateKey] ?? [], ]; $cursor->addDay(); } $weeks[] = $week; } return [ 'monthLabel' => $this->formatMonthYear($today), 'weekdays' => ['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min'], 'weeks' => $weeks, ]; } private function scheduleBadgeClass(?string $jenis): string { return match ($jenis) { 'Sidang' => 'bg-orange-100 text-orange-700', 'Outline' => 'bg-emerald-100 text-emerald-700', 'SidHas' => 'bg-amber-100 text-amber-700', default => 'bg-slate-200 text-slate-700', }; } private function scheduleLabel(?string $jenis): string { return match ($jenis) { 'Sidang' => 'Sidang', 'Outline' => 'Outline', 'SidHas' => 'Seminar Hasil', default => $jenis ?? 'Jadwal', }; } private function scheduleEventClass(?string $jenis): string { return match ($jenis) { 'Sidang' => 'bg-orange-100 text-orange-700 border-orange-200', 'Outline' => 'bg-emerald-100 text-emerald-700 border-emerald-200', 'SidHas' => 'bg-amber-100 text-amber-700 border-amber-200', default => 'bg-slate-100 text-slate-700 border-slate-200', }; } private function formatIndonesianDateTime(?string $value): string { if (! $value) { return '-'; } return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); } private function formatMonthYear(Carbon $date): string { return $date->copy()->locale('id')->translatedFormat('F Y'); } private function legacyUrl(string $path): string { $baseUrl = rtrim((string) env('LEGACY_BASE_URL', 'http://127.0.0.1:8080'), '/'); return $baseUrl.'/'.ltrim($path, '/'); } }