get($this->apiUrl('/penugasan'), ['is_archived' => 'false']); $dashResponse = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/dashboard')); $items = collect([]); if ($response->successful()) { $items = collect($response->json()['data'])->map(function($item) { return (object) [ 'id' => $item['id'], 'nama_kegiatan' => $item['kegiatan'], 'tanggal' => $item['tanggal_mulai'], 'tanggal_selesai' => $item['tanggal_selesai'] ?? null, 'created_at' => \Carbon\Carbon::parse($item['created_at']), 'status' => $item['status'] == 'proses' ? 'on_progres' : $item['status'], 'user' => (object) ['name' => $item['pembuat']['name'] ?? 'System'], 'petugas' => $item['petugas'] ?? [], 'pembuat' => $item['pembuat'] ?? [] ]; }); } $allItems = clone $items; $items = $items->filter(function($item) { return $item->status !== 'dibatalkan'; })->values(); // Manual Pagination $perPage = 10; $currentPage = \Illuminate\Pagination\Paginator::resolveCurrentPage(); $currentItems = $items->slice(($currentPage - 1) * $perPage, $perPage)->all(); $activities = new \Illuminate\Pagination\LengthAwarePaginator($currentItems, count($items), $perPage, $currentPage, [ 'path' => \Illuminate\Pagination\Paginator::resolveCurrentPath() ]); $stats = [ 'total' => $items->count(), 'users' => 0, // diisi nanti 'proses' => $items->where('status', 'on_progres')->count(), 'deleted' => $allItems->where('status', 'dibatalkan')->count(), ]; // Ambil User $usersResponse = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/users')); $users = collect([]); if ($usersResponse->successful()) { $users = collect($usersResponse->json()['data']); $stats['users'] = $users->count(); } // ── Top 5 Petugas (Dihitung dari penugasan) $topUsers = $users->map(function($u) use ($items) { $count = $items->filter(function($item) use ($u) { if (isset($item->petugas) && is_array($item->petugas)) { foreach ($item->petugas as $p) { if (isset($p['id']) && $p['id'] == $u['id']) return true; } } return isset($item->pembuat['id']) && $item->pembuat['id'] == $u['id']; })->count(); return (object) [ 'name' => $u['name'] ?? 'System', 'activities_count' => $count ]; })->sortByDesc('activities_count')->take(5)->values(); // ── Data Grafik $chartData = $this->buildChartData($items); $system = [ 'server' => rand(25, 55), 'database' => rand(45, 75), 'storage' => rand(60, 85), ]; return view('superadmin.dashboard', compact( 'activities', 'stats', 'topUsers', 'chartData', 'system' )); } private function buildChartData($items) { $now = Carbon::now(); // ── MINGGU INI ───────────────────────────────── $startOfWeek = $now->copy()->startOfWeek(Carbon::MONDAY); $endOfWeek = $now->copy()->endOfWeek(Carbon::SUNDAY); $weekLabels = ['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min']; $weekValues = array_fill(0, 7, 0); foreach ($items as $item) { $date = $item->created_at; if ($date->between($startOfWeek, $endOfWeek)) { $idx = $date->dayOfWeekIso - 1; $weekValues[$idx]++; } } // ── BULAN INI ────────────────────────────── $startOfMonth = $now->copy()->startOfMonth(); $endOfMonth = $now->copy()->endOfMonth(); $daysInMonth = $now->daysInMonth; $monthLabels = []; $monthValues = array_fill(0, $daysInMonth, 0); for ($i = 1; $i <= $daysInMonth; $i++) { $monthLabels[] = ($i % 5 === 1 || $i === 1) ? (string) $i : ''; } foreach ($items as $item) { $date = $item->created_at; if ($date->between($startOfMonth, $endOfMonth)) { $monthValues[$date->day - 1]++; } } // ── TAHUN INI ──────────────────────────────── $monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ags', 'Sep', 'Okt', 'Nov', 'Des']; $yearValues = array_fill(0, 12, 0); foreach ($items as $item) { $date = $item->created_at; if ($date->year === $now->year) { $yearValues[$date->month - 1]++; } } return [ 'week' => [ 'labels' => $weekLabels, 'values' => $weekValues, 'todayIndex' => $now->dayOfWeekIso - 1, ], 'month' => [ 'labels' => $monthLabels, 'values' => $monthValues, 'todayIndex' => $now->day - 1, ], 'year' => [ 'labels' => $monthNames, 'values' => $yearValues, 'todayIndex' => $now->month - 1, ], ]; } public function show($id) { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/penugasan/' . $id)); if (!$response->successful()) { return redirect()->back()->with('error', 'Kegiatan tidak ditemukan.'); } $data = $response->json()['data']; $activity = (object) [ 'id' => $data['id'], 'nama_kegiatan' => $data['kegiatan'], 'tanggal' => $data['tanggal_mulai'], 'tanggal_selesai' => $data['tanggal_selesai'] ?? null, 'status' => $data['status'] == 'proses' ? 'on_progres' : $data['status'], 'deskripsi' => $data['keterangan'] ?? '-', 'lokasi' => $data['lokasi'] ?? '-', 'status_note' => $data['status_note'] ?? null, 'updated_at' => \Carbon\Carbon::parse($data['updated_at'] ?? now()), 'user' => (object) ['name' => $data['pembuat']['name'] ?? 'System'], 'petugas' => $data['petugas'] ?? [], 'nomor_surat' => $data['nomor_surat'] ?? '-', 'progress' => $data['progress'] ?? [] ]; return view('superadmin.kegiatan.show', compact('activity')); } public function approve(Request $request, $id) { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->post($this->apiUrl("/penugasan/{$id}/update-status"), [ 'status' => 'telah_ditinjau' ]); if ($response->successful()) { return redirect()->back()->with('success', 'Kegiatan berhasil disetujui (Telah Ditinjau).'); } return redirect()->back()->with('error', 'Terjadi kesalahan saat menyetujui kegiatan.'); } public function revision(Request $request, $id) { $request->validate([ 'status_note' => 'required|string|min:5|max:255' ]); $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->post($this->apiUrl("/penugasan/{$id}/update-status"), [ 'status' => 'proses', 'status_note' => 'REVISI dari ' . session('user_name') . ': ' . $request->status_note ]); if ($response->successful()) { return redirect()->back()->with('success', 'Kegiatan dikembalikan ke pegawai untuk direvisi.'); } return redirect()->back()->with('error', 'Terjadi kesalahan saat meminta revisi.'); } public function destroy(Request $request, $id) { $request->validate([ 'alasan_hapus' => 'required|string|min:10|max:500', ]); $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->post($this->apiUrl("/penugasan/{$id}/delete"), [ 'alasan' => $request->alasan_hapus ]); if ($response->successful()) { return redirect()->back()->with('success', 'Kegiatan berhasil dibatalkan dan dicatat dalam log.'); } return redirect()->back()->with('error', 'Gagal membatalkan kegiatan.'); } public function arsip() { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/penugasan'), ['is_archived' => 'true']); $items = collect([]); if ($response->successful()) { $items = collect($response->json()['data'])->map(function($item) { return (object) [ 'id' => $item['id'], 'nama_kegiatan' => $item['kegiatan'], 'tanggal' => $item['tanggal_mulai'], 'tanggal_selesai' => $item['tanggal_selesai'] ?? null, 'created_at' => \Carbon\Carbon::parse($item['created_at']), 'status' => $item['status'] == 'proses' ? 'on_progres' : $item['status'], 'user' => (object) ['name' => $item['pembuat']['name'] ?? 'System'], 'petugas' => $item['petugas'] ?? [], 'pembuat' => $item['pembuat'] ?? [] ]; }); } // Manual Pagination $perPage = 10; $currentPage = \Illuminate\Pagination\Paginator::resolveCurrentPage(); $currentItems = $items->slice(($currentPage - 1) * $perPage, $perPage)->all(); $activities = new \Illuminate\Pagination\LengthAwarePaginator($currentItems, count($items), $perPage, $currentPage, [ 'path' => \Illuminate\Pagination\Paginator::resolveCurrentPath() ]); return view('superadmin.arsip.index', compact('activities')); } public function archive($id) { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->post($this->apiUrl("/penugasan/{$id}/archive")); if ($response->successful()) { return redirect()->back()->with('success', 'Status arsip laporan berhasil diperbarui.'); } return redirect()->back()->with('error', 'Gagal memperbarui status arsip laporan.'); } public function userIndex() { // Panggil API Penugasan untuk menghitung activities dinamis $penugasanResponse = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/penugasan')); $items = collect([]); if ($penugasanResponse->successful()) { $items = collect($penugasanResponse->json()['data']); } // Panggil API Users $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/users')); $users = collect([]); if ($response->successful()) { $users = collect($response->json()['data'])->map(function($u) use ($items) { $activitiesCount = $items->filter(function($item) use ($u) { // Cek apakah user ada di array petugas if (isset($item['petugas']) && is_array($item['petugas'])) { foreach ($item['petugas'] as $p) { if ($p['id'] == $u['id']) return true; } } // Fallback cek pembuat return ($item['pembuat']['id'] ?? null) == $u['id']; })->count(); return (object) [ 'id' => $u['id'], 'name' => $u['name'], 'username' => $u['username'], 'role' => $u['role'], 'created_at' => isset($u['created_at']) ? \Carbon\Carbon::parse($u['created_at']) : now(), 'activities_count' => $activitiesCount ]; }); } // Manual Pagination $perPage = 15; $currentPage = \Illuminate\Pagination\Paginator::resolveCurrentPage(); $currentItems = $users->slice(($currentPage - 1) * $perPage, $perPage)->all(); $paginatedUsers = new \Illuminate\Pagination\LengthAwarePaginator($currentItems, $users->count(), $perPage, $currentPage, [ 'path' => \Illuminate\Pagination\Paginator::resolveCurrentPath() ]); $stats = [ 'total' => $users->count(), 'active' => $users->where('activities_count', '>', 0)->count(), 'activities' => $users->sum('activities_count'), 'max_activities' => max($users->max('activities_count') ?? 1, 1), ]; return view('superadmin.user.index', ['users' => $paginatedUsers, 'stats' => $stats]); } public function createUser() { return view('superadmin.user.create-user'); } public function storeUser(Request $request) { $request->validate([ 'name' => 'required|string', 'username' => 'required|string', 'password' => 'required|string|min:6', 'role' => 'required|in:user,admin,superadmin', ]); $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->acceptJson() ->post($this->apiUrl('/register'), [ 'name' => $request->name, 'username' => $request->username, 'password' => $request->password, 'role' => $request->role, ]); if ($response->successful()) { return redirect()->route('admin.superadmin.user.index')->with('success', 'Pengguna berhasil ditambahkan'); } if ($response->status() === 422) { return back()->withErrors($response->json('errors'))->withInput(); } return back()->with('error', $response->json('message') ?? 'Gagal menambah pengguna')->withInput(); } public function editUser($id) { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl("/users/{$id}")); if ($response->successful()) { $userData = $response->json()['data']; $user = (object) [ 'id' => $userData['id'], 'name' => $userData['name'], 'username' => $userData['username'], 'role' => $userData['role'], 'email' => $userData['email'] ?? '', 'created_at' => isset($userData['created_at']) ? \Carbon\Carbon::parse($userData['created_at']) : now(), ]; return view('superadmin.user.edit-user', compact('user')); } return redirect()->back()->with('error', 'User tidak ditemukan atau gagal mengambil data.'); } public function updateUser(Request $request, $id) { $request->validate([ 'name' => 'required', 'username' => 'required', 'role' => 'required', 'password' => 'nullable|min:6|confirmed', ]); $payload = $request->only('name', 'username', 'role'); if ($request->filled('password')) { $payload['password'] = $request->password; } $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->acceptJson() ->put($this->apiUrl("/users/{$id}"), $payload); if ($response->successful()) { return redirect()->route('admin.superadmin.user.index')->with('success', 'User berhasil diupdate'); } if ($response->status() === 422) { return back()->withErrors($response->json('errors'))->withInput(); } return redirect()->back()->with('error', 'Gagal update user.')->withInput(); } // 🔥 DELETE USER public function destroyUser($id) { $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->delete($this->apiUrl('/users/' . $id)); if ($response->successful()) { return redirect()->route('admin.superadmin.user.index')->with('success', 'Pengguna berhasil dihapus'); } return back()->with('error', 'Gagal menghapus pengguna'); } // 🔥 CREATE KEGIATAN public function createKegiatan() { // Ambil data users untuk dropdown pilihan pegawai (opsional) $usersResponse = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/users')); $users = []; if ($usersResponse->successful()) { $allUsers = $usersResponse->json()['data'] ?? []; // Hanya tampilkan user dengan role "user" (Pegawai/Petugas) $users = array_filter($allUsers, function($u) { return isset($u['role']) && strtolower($u['role']) === 'user'; }); } return view('superadmin.kegiatan.create', compact('users')); } // 🔥 STORE KEGIATAN public function storeKegiatan(Request $request) { $request->validate([ 'nomor_surat' => 'required|string', 'kegiatan' => 'required|string', 'tanggal_mulai' => 'required|date', 'tanggal_selesai' => 'nullable|date', 'pegawai_ids' => 'required|array', ]); $payload = [ 'nomor_surat' => $request->nomor_surat, 'kegiatan' => $request->kegiatan, 'tanggal_mulai' => $request->tanggal_mulai, 'tanggal_selesai' => $request->tanggal_selesai, 'pegawai_ids' => $request->pegawai_ids, ]; $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->acceptJson() ->post($this->apiUrl('/penugasan'), $payload); if ($response->successful()) { return redirect()->route('admin.superadmin.dashboard')->with('success', 'Tugas baru berhasil dibuat'); } if ($response->status() === 422) { return back()->withErrors($response->json('errors'))->withInput(); } return back()->with('error', 'Gagal membuat tugas baru: ' . $response->json('message', 'Error API'))->withInput(); } public function export(Request $request) { $columns = $request->input('columns', []); $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/export-penugasan'), [ 'columns' => $columns ]); if ($response->successful()) { $fileName = 'Laporan_Penugasan.xlsx'; $contentDisposition = $response->header('Content-Disposition'); if ($contentDisposition && preg_match('/filename="?([^"]+)"?/', $contentDisposition, $matches)) { $fileName = $matches[1]; } return response($response->body()) ->header('Content-Type', $response->header('Content-Type') ?? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') ->header('Content-Disposition', 'attachment; filename="' . $fileName . '"'); } return back()->with('error', 'Gagal mengunduh laporan: Endpoint backend mungkin bermasalah atau tidak tersedia.'); } public function chartData(Request $request) { $mode = $request->get('mode', 'week'); $response = \Illuminate\Support\Facades\Http::withToken(session('api_token')) ->get($this->apiUrl('/penugasan')); $items = collect([]); if ($response->successful()) { $items = collect($response->json()['data'])->filter(function($item) { return $item['status'] !== 'dibatalkan'; })->map(function($item) { return (object) ['created_at' => \Carbon\Carbon::parse($item['created_at'])]; }); } $data = $this->buildChartData($items); return response()->json([ 'success' => true, 'data' => $data[$mode] ?? $data['week'], ]); } }