where('id_petugas', $id_petugas) ->first(); // ========================= // PERBAIKAN FASYANKES // ========================= $fasyankes = null; if ( $petugas && !empty($petugas->id_fasyankes) ) { $dataFasyankes = DB::table('fasyankes') ->where('id_fasyankes', $petugas->id_fasyankes) ->first(); $fasyankes = $dataFasyankes ? $dataFasyankes->nama_fasyankes : null; } $role = session('role'); // 1. Statistik Utama (Cards) $totalPasienQuery = DB::table('users') ->join('pasien_medis', 'users.id_user', '=', 'pasien_medis.id_user') ->where('pasien_medis.status_kesehatan', 'Masih Proses'); if ($role != 'super_admin') { $totalPasienQuery->where('users.nama_fasyankes', $fasyankes); } $totalPasien = $totalPasienQuery->count(); $sudahMinumQuery = DB::table('monitoring_obat') ->join('pasien_medis', 'monitoring_obat.id_medis', '=', 'pasien_medis.id_medis') ->join('users', 'pasien_medis.id_user', '=', 'users.id_user') ->whereDate('monitoring_obat.created_at', Carbon::today()); if ($role != 'super_admin') { $sudahMinumQuery->where('users.nama_fasyankes', $fasyankes); } $sudahMinum = $sudahMinumQuery ->pluck('users.id_user') ->toArray(); $belumMinumObat = $totalPasien - count($sudahMinum); $skriningResikoQuery = DB::table('skrining') ->join('users', 'skrining.id_user', '=', 'users.id_user') ->where('hasil_skrining', 'Terduga TBC') ->whereDate('skrining.tgl_skrining', Carbon::today()); if ($role != 'super_admin') { $skriningResikoQuery->where('users.nama_fasyankes', $fasyankes); } $skriningResiko = $skriningResikoQuery->count(); // 2. Data Grafik Bulanan $tahunDipilih = request('tahun', date('Y')); $tahunList = DB::table('pasien_medis') ->selectRaw('YEAR(tgl_mulai_pengobatan) as tahun') ->whereNotNull('tgl_mulai_pengobatan') ->distinct() ->orderBy('tahun', 'desc') ->pluck('tahun') ->toArray(); $tahunSkriningList = DB::table('skrining') ->selectRaw('YEAR(tgl_skrining) as tahun') ->whereNotNull('tgl_skrining') ->distinct() ->pluck('tahun') ->toArray(); $tahunList = array_unique( array_merge($tahunList, $tahunSkriningList) ); rsort($tahunList); if (empty($tahunList)) { $tahunList[] = date('Y'); } if (!in_array($tahunDipilih, $tahunList)) { $tahunDipilih = $tahunList[0]; } /* |-------------------------------------------------------------------------- | BATASI BULAN |-------------------------------------------------------------------------- | Jika tahun sekarang -> hanya sampai bulan sekarang | Jika tahun lama -> tampil full 12 bulan |-------------------------------------------------------------------------- */ if ($tahunDipilih == date('Y')) { $maxBulan = date('n'); } else { $maxBulan = 12; } $bulanLabels = []; $bulanValues = []; for ($bulan = 1; $bulan <= $maxBulan; $bulan++) { $countQuery = DB::table('pasien_medis') ->join('users', 'pasien_medis.id_user', '=', 'users.id_user') ->whereMonth( 'pasien_medis.tgl_mulai_pengobatan', $bulan ) ->whereYear( 'pasien_medis.tgl_mulai_pengobatan', $tahunDipilih ); if ($role != 'super_admin') { $countQuery->where( 'users.nama_fasyankes', $fasyankes ); } $count = $countQuery->count(); $bulanLabels[] = Carbon::create() ->month($bulan) ->translatedFormat('M'); $bulanValues[] = $count; } $dataBulanan = [ 'labels' => $bulanLabels, 'values' => $bulanValues ]; // 3. Data Grafik Status $tahunStatusDipilih = request('tahun_status', $tahunList[0]); if (!in_array($tahunStatusDipilih, $tahunList)) { $tahunStatusDipilih = $tahunList[0]; } $statusList = ['Masih Proses', 'Sembuh', 'Gagal', 'Meninggal']; $statusValues = []; foreach ($statusList as $st) { /* ===================================================== AMBIL HANYA RIWAYAT TERAKHIR TIAP PASIEN ===================================================== */ $subQuery = DB::table('pasien_medis') ->select( 'id_user', DB::raw('MAX(id_medis) as last_id_medis') ) ->groupBy('id_user'); $query = DB::table('pasien_medis') ->joinSub($subQuery, 'latest', function ($join) { $join->on( 'pasien_medis.id_medis', '=', 'latest.last_id_medis' ); }) ->join('users', 'pasien_medis.id_user', '=', 'users.id_user') ->where('pasien_medis.status_kesehatan', $st); /* ===================================================== FILTER FASYANKES ===================================================== */ if ($role != 'super_admin') { $query->where( 'users.nama_fasyankes', $fasyankes ); } /* ===================================================== FILTER TAHUN ===================================================== */ $query->whereYear( 'pasien_medis.tgl_mulai_pengobatan', $tahunStatusDipilih ); $statusValues[] = $query->count(); } $dataStatus = [ 'labels' => $statusList, 'values' => $statusValues ]; // 4. Data Grafik Skrining Bulanan $bulanSkriningDipilih = request('bulan_skrining', date('m')); $tahunSkrining = request('tahun_skrining', $tahunList[0]); if (!in_array($tahunSkrining, $tahunList)) { $tahunSkrining = $tahunList[0]; } if ( $bulanSkriningDipilih == date('m') && $tahunSkrining == date('Y') ) { $jumlahHari = date('d'); } else { $jumlahHari = Carbon::create( $tahunSkrining, $bulanSkriningDipilih )->daysInMonth; } $skriningRawQuery = DB::table('skrining') ->join('users', 'skrining.id_user', '=', 'users.id_user') ->whereMonth('skrining.tgl_skrining', $bulanSkriningDipilih) ->whereYear('skrining.tgl_skrining', $tahunSkrining); if ($role != 'super_admin') { $skriningRawQuery->where('users.nama_fasyankes', $fasyankes); } $skriningRaw = $skriningRawQuery ->select( DB::raw('DAY(skrining.tgl_skrining) as hari'), DB::raw('COUNT(*) as total'), DB::raw(" SUM( CASE WHEN skrining.hasil_skrining = 'Terduga TBC' THEN 1 ELSE 0 END ) as terduga "), DB::raw(" SUM( CASE WHEN skrining.hasil_skrining != 'Terduga TBC' THEN 1 ELSE 0 END ) as tidak_terduga ") ) ->groupBy('hari') ->get() ->keyBy('hari'); $labelsSkrining = []; $valuesSkrining = []; $terdugaValues = []; $tidakTerdugaValues = []; for ($i = 1; $i <= $jumlahHari; $i++) { $labelsSkrining[] = $i; $valuesSkrining[] = $skriningRaw[$i]->total ?? 0; $terdugaValues[] = $skriningRaw[$i]->terduga ?? 0; $tidakTerdugaValues[] = $skriningRaw[$i]->tidak_terduga ?? 0; } $dataSkrining = [ 'labels' => $labelsSkrining, 'values' => $valuesSkrining, 'terduga' => $terdugaValues, 'tidak_terduga' => $tidakTerdugaValues ]; return view('dashboard', compact( 'totalPasien', 'belumMinumObat', 'skriningResiko', 'dataSkrining', 'dataStatus', 'dataBulanan', 'tahunList', 'tahunDipilih', 'bulanSkriningDipilih', 'tahunSkrining', 'tahunStatusDipilih' )); } public function getAdvancedData(Request $request) { $id_petugas = session('petugas_id'); $petugas = DB::table('petugas') ->where('id_petugas', $id_petugas) ->first(); // ========================= // PERBAIKAN FASYANKES // ========================= $fasyankes = null; if ( $petugas && !empty($petugas->id_fasyankes) ) { $dataFasyankes = DB::table('fasyankes') ->where('id_fasyankes', $petugas->id_fasyankes) ->first(); $fasyankes = $dataFasyankes ? $dataFasyankes->nama_fasyankes : null; } $role = session('role'); $tgl = $request->tanggal ?? Carbon::today()->toDateString(); $belumMinumQuery = DB::table('users') ->join('pasien_medis', 'users.id_user', '=', 'pasien_medis.id_user') ->where('pasien_medis.status_kesehatan', 'Masih Proses') ->whereNotExists(function ($query) use ($tgl) { $query->select(DB::raw(1)) ->from('monitoring_obat') ->whereRaw('monitoring_obat.id_medis = pasien_medis.id_medis') ->whereDate('monitoring_obat.created_at', $tgl); }); if ($role != 'super_admin') { $belumMinumQuery->where('users.nama_fasyankes', $fasyankes); } $belumMinum = $belumMinumQuery ->select('users.nama_lengkap') ->get(); return response()->json([ 'top_pasien' => $belumMinum ]); } // public function generateNotifikasiHarian() // { // try { // $tanggalHariIni = now()->toDateString(); // $users = DB::table('users') // ->join('pasien_medis', 'users.id_user', '=', 'pasien_medis.id_user') // /* // ========================================================== // HANYA PASIEN YANG MASIH PROSES // ========================================================== // */ // ->where('pasien_medis.status_kesehatan', 'Masih Proses') // /* // ========================================================== // PASTIKAN ADA JAM MINUM // ========================================================== // */ // ->whereNotNull('pasien_medis.jam_minum') // ->select( // 'users.id_user', // 'users.nama_lengkap', // 'pasien_medis.jam_minum', // 'pasien_medis.id_medis', // 'pasien_medis.status_kesehatan' // ) // ->get(); // foreach ($users as $u) { // $namaUser = $u->nama_lengkap ?? 'Pengguna'; // $prefixPesan = "Halo, {$namaUser}! "; // $jamMinum = Carbon::createFromFormat('H:i:s', $u->jam_minum); // $baseToday = Carbon::parse( // $tanggalHariIni . ' ' . $jamMinum->format('H:i:s') // ); // $now = now(); // /* // ========================================================== // CEK KONFIRMASI MINUM OBAT HARI INI // ========================================================== // */ // $sudahKonfirmasi = DB::table('monitoring_obat') // ->where('id_medis', $u->id_medis) // ->where('tanggal', $tanggalHariIni) // ->where('status', 1) // ->exists(); // // >>> TAMBAHAN BARU DI SINI <<< // if ($sudahKonfirmasi) { // continue; // Skip user ini, tidak perlu generate notif apapun hari ini // } // /* // ========================================================== // JADWAL NOTIFIKASI HARIAN // ========================================================== // */ // $jadwals = [ // [ // 'jenis' => 'pengingat_awal', // 'jam' => $baseToday->copy()->subHours(2), // 'judul' => 'Pengingat Minum Obat TBC', // 'pesan' => $prefixPesan . // '2 jam lagi waktunya minum obat TBC. Segera makan untuk persiapan minum obat, lalu kosongkan perut di 2 jam kedepan.' // ], // [ // 'jenis' => 'waktu_minum', // 'jam' => $baseToday->copy(), // 'judul' => 'Waktunya Minum Obat TBC', // 'pesan' => $prefixPesan . // 'Saatnya minum obat TBC sekarang dan segera lakukan konfirmasi setelah minum obat.' // ], // [ // 'jenis' => 'telat_1_jam', // 'jam' => $baseToday->copy()->addHour(), // 'judul' => 'Belum Ada Konfirmasi Minum Obat', // 'pesan' => $prefixPesan . // 'Anda belum melakukan konfirmasi minum obat TBC. Jika sudah minum obat, segera lakukan konfirmasi.' // ], // [ // 'jenis' => 'telat_2_jam', // 'jam' => $baseToday->copy()->addHours(2), // 'judul' => 'Peringatan Minum Obat TBC', // 'pesan' => $prefixPesan . // 'Anda belum konfirmasi minum obat TBC hari ini. Segera minum obat dan lakukan konfirmasi sekarang.' // ] // ]; // foreach ($jadwals as $j) { // // LOGIKA LAMA YANG MENGHAPUS NOTIF TELAT DI SINI SUDAH DIHAPUS TOTAL // /* // ========================================================== // CEK APAKAH NOTIFIKASI SUDAH ADA // ========================================================== // */ // $notif = DB::table('notifikasi') // ->where('id_user', $u->id_user) // ->where('jenis_notif', $j['jenis']) // ->where('tanggal_notif', $tanggalHariIni) // ->first(); // /* // ========================================================== // JIKA BELUM ADA -> INSERT // ========================================================== // */ // if (!$notif) { // DB::table('notifikasi')->insert([ // 'id_user' => $u->id_user, // 'judul' => $j['judul'], // 'pesan' => $j['pesan'], // 'jenis_notif' => $j['jenis'], // 'waktu_kirim' => $j['jam'], // 'tanggal_notif' => $tanggalHariIni, // 'is_sent' => 0, // 'created_at' => now(), // 'updated_at' => now() // ]); // continue; // } // /* // ========================================================== // JIKA JAM MINUM BERUBAH -> RESET NOTIFIKASI // ========================================================== // */ // $waktuDb = Carbon::parse($notif->waktu_kirim); // $jamDb = $waktuDb->format('H:i:s'); // $jamBaru = $j['jam']->format('H:i:s'); // if ($jamDb != $jamBaru) { // DB::table('notifikasi') // ->where('id', $notif->id) // ->update([ // 'judul' => $j['judul'], // 'pesan' => $j['pesan'], // 'waktu_kirim' => $j['jam'], // 'is_sent' => 0, // 'updated_at' => now(), // ]); // Log::info( // "NOTIF DIRESET KARENA JAM BERUBAH ID: {$notif->id}" // ); // continue; // } // } // } // } catch (\Exception $e) { // Log::error( // "ERROR generateNotifikasiHarian: " . $e->getMessage() // ); // } // } public function generateNotifikasiHarian() { try { $tanggalHariIni = now()->toDateString(); $users = DB::table('users') ->join('pasien_medis', 'users.id_user', '=', 'pasien_medis.id_user') ->where('pasien_medis.status_kesehatan', 'Masih Proses') ->whereNotNull('pasien_medis.jam_minum') ->select('users.id_user', 'users.nama_lengkap', 'pasien_medis.jam_minum', 'pasien_medis.id_medis') ->get(); $sudahKonfirmasiSet = DB::table('monitoring_obat') ->whereIn('id_medis', $users->pluck('id_medis')) ->where('tanggal', $tanggalHariIni) ->where('status', 1) ->pluck('id_medis') ->flip(); $notifExisting = DB::table('notifikasi') ->whereIn('id_user', $users->pluck('id_user')) ->where('tanggal_notif', $tanggalHariIni) ->get() ->groupBy(fn($n) => $n->id_user . '_' . $n->jenis_notif); $inserts = []; $now = now(); foreach ($users as $u) { if (isset($sudahKonfirmasiSet[$u->id_medis])) continue; $namaUser = $u->nama_lengkap ?? 'Pengguna'; $prefixPesan = "Halo, {$namaUser}! "; $jamMinum = Carbon::createFromFormat('H:i:s', $u->jam_minum); $baseToday = Carbon::parse($tanggalHariIni . ' ' . $jamMinum->format('H:i:s')); $jadwals = [ [ 'jenis' => 'pengingat_awal', 'jam' => $baseToday->copy()->subHours(2), 'judul' => 'Pengingat Minum Obat TBC', 'pesan' => $prefixPesan . '2 jam lagi waktunya minum obat TBC. Segera makan untuk persiapan minum obat, lalu kosongkan perut di 2 jam kedepan.' ], [ 'jenis' => 'waktu_minum', 'jam' => $baseToday->copy(), 'judul' => 'Waktunya Minum Obat TBC', 'pesan' => $prefixPesan . 'Saatnya minum obat TBC sekarang dan segera lakukan konfirmasi setelah minum obat.' ], [ 'jenis' => 'ulang_5_menit', 'jam' => $baseToday->copy()->addMinutes(5), 'judul' => 'Pengingat Minum Obat TBC', 'pesan' => $prefixPesan . 'Saatnya minum obat TBC sekarang dan segera lakukan konfirmasi setelah minum obat.' ], [ 'jenis' => 'ulang_10_menit', 'jam' => $baseToday->copy()->addMinutes(10), 'judul' => 'Pengingat Minum Obat TBC', 'pesan' => $prefixPesan . 'Saatnya minum obat TBC sekarang dan segera lakukan konfirmasi setelah minum obat.' ], [ 'jenis' => 'telat_1_jam', 'jam' => $baseToday->copy()->addHour(), 'judul' => 'Belum Ada Konfirmasi Minum Obat', 'pesan' => $prefixPesan . 'Anda belum melakukan konfirmasi minum obat TBC. Jika sudah minum obat, segera lakukan konfirmasi.' ], [ 'jenis' => 'telat_2_jam', 'jam' => $baseToday->copy()->addHours(2), 'judul' => 'Peringatan Minum Obat TBC', 'pesan' => $prefixPesan . 'Anda belum konfirmasi minum obat TBC hari ini. Segera minum obat dan lakukan konfirmasi sekarang.' ] ]; foreach ($jadwals as $j) { $key = $u->id_user . '_' . $j['jenis']; $notif = $notifExisting->get($key)?->first(); if (!$notif) { $inserts[] = [ 'id_user' => $u->id_user, 'judul' => $j['judul'], 'pesan' => $j['pesan'], 'jenis_notif' => $j['jenis'], 'waktu_kirim' => $j['jam'], 'tanggal_notif' => $tanggalHariIni, 'is_sent' => 0, 'created_at' => $now, 'updated_at' => $now, ]; continue; } $jamDb = Carbon::parse($notif->waktu_kirim)->format('H:i:s'); $jamBaru = $j['jam']->format('H:i:s'); if ($jamDb != $jamBaru) { DB::table('notifikasi')->where('id', $notif->id)->update([ 'judul' => $j['judul'], 'pesan' => $j['pesan'], 'waktu_kirim' => $j['jam'], 'is_sent' => 0, 'updated_at' => $now, ]); Log::info("NOTIF DIRESET KARENA JAM BERUBAH ID: {$notif->id}"); } } } if (!empty($inserts)) { foreach (array_chunk($inserts, 500) as $chunk) { DB::table('notifikasi')->insert($chunk); } Log::info("NOTIFIKASI DIGENERATE: " . count($inserts) . " record"); } } catch (\Exception $e) { Log::error("ERROR generateNotifikasiHarian: " . $e->getMessage()); } } public static function kirimFCM($tokens, $notif) { try { $client = new Client(); $client->setAuthConfig( storage_path('app/firebase/service-account.json') ); $client->addScope( 'https://www.googleapis.com/auth/firebase.messaging' ); $tokenData = $client->fetchAccessTokenWithAssertion(); if (!isset($tokenData['access_token'])) { Log::error('ACCESS TOKEN FIREBASE GAGAL'); return; } $accessToken = $tokenData['access_token']; $projectId = 'sistemtbc'; foreach ($tokens as $token) { $payload = [ 'message' => [ 'token' => $token, 'notification' => [ 'title' => $notif->judul, 'body' => $notif->pesan, ], 'android' => [ 'priority' => 'high', 'ttl' => '300s', 'notification' => [ 'channel_id' => 'obat_alarm_v2', 'sound' => 'alarm', 'visibility' => 'PUBLIC', ], ], 'data' => [ 'notif_id' => (string) $notif->id, 'id_user' => (string) $notif->id_user, 'title' => $notif->judul, 'body' => $notif->pesan, 'jenis_notif' => $notif->jenis_notif, 'is_fullscreen' => in_array( $notif->jenis_notif, [ 'waktu_minum', 'ulang_5_menit', 'ulang_10_menit', 'telat_1_jam', 'telat_2_jam' ] ) ? '1' : '0', ], ], ]; Log::info('TOKEN FCM: ' . $token); Log::info('PAYLOAD FCM:', $payload); $response = Http::timeout(30) ->withToken($accessToken) ->acceptJson() ->post( "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", $payload ); Log::info("FCM STATUS: " . $response->status()); Log::info("FCM BODY: " . $response->body()); if ( $response->status() == 404 || str_contains($response->body(), 'UNREGISTERED') ) { DB::table('fcm_tokens') ->where('token', $token) ->delete(); } } } catch (\Exception $e) { Log::error( 'FCM ERROR: ' . $e->getMessage() ); } } public static function getAccessToken() { $keyFile = storage_path('app/firebase/service-account.json'); $client = new \Google_Client(); $client->setAuthConfig($keyFile); $client->addScope('https://www.googleapis.com/auth/firebase.messaging'); $token = $client->fetchAccessTokenWithAssertion(); return $token['access_token']; } public static function updateNotifikasiHariIni($idUser) { try { $tanggalHariIni = now()->toDateString(); $user = DB::table('users') ->join( 'pasien_medis', 'users.id_user', '=', 'pasien_medis.id_user' ) ->where('users.id_user', $idUser) ->select( 'users.id_user', 'users.nama_lengkap', 'pasien_medis.id_medis', 'pasien_medis.jam_minum' ) ->first(); if (!$user || !$user->jam_minum) { return; } $sudahKonfirmasi = DB::table('monitoring_obat') ->where('id_medis', $user->id_medis) ->where('tanggal', $tanggalHariIni) ->where('status', 1) ->exists(); if ($sudahKonfirmasi) { return; } $namaUser = $user->nama_lengkap ?? 'Pengguna'; $prefixPesan = "Halo, {$namaUser}! "; $jamMinum = Carbon::createFromFormat( 'H:i:s', $user->jam_minum ); $baseToday = Carbon::parse( $tanggalHariIni . ' ' . $jamMinum->format('H:i:s') ); $jadwals = [ [ 'jenis' => 'pengingat_awal', 'jam' => $baseToday->copy()->subHours(2), 'judul' => 'Pengingat Minum Obat TBC', 'pesan' => $prefixPesan . '2 jam lagi waktunya minum obat TBC. Segera makan untuk persiapan minum obat, lalu kosongkan perut di 2 jam kedepan.' ], [ 'jenis' => 'waktu_minum', 'jam' => $baseToday->copy(), 'judul' => 'Waktunya Minum Obat TBC', 'pesan' => $prefixPesan . 'Saatnya minum obat TBC sekarang dan segera lakukan konfirmasi setelah minum obat.' ], [ 'jenis' => 'telat_1_jam', 'jam' => $baseToday->copy()->addHour(), 'judul' => 'Belum Ada Konfirmasi Minum Obat', 'pesan' => $prefixPesan . 'Anda belum melakukan konfirmasi minum obat TBC. Jika sudah minum obat, segera lakukan konfirmasi.' ], [ 'jenis' => 'telat_2_jam', 'jam' => $baseToday->copy()->addHours(2), 'judul' => 'Peringatan Minum Obat TBC', 'pesan' => $prefixPesan . 'Anda belum konfirmasi minum obat TBC hari ini. Segera minum obat dan lakukan konfirmasi sekarang.' ] ]; foreach ($jadwals as $j) { $isSent = $j['jam']->lte(now()) ? 1 : 0; DB::table('notifikasi') ->where('id_user', $idUser) ->where('jenis_notif', $j['jenis']) ->where('tanggal_notif', $tanggalHariIni) ->update([ 'judul' => $j['judul'], 'pesan' => $j['pesan'], 'waktu_kirim' => $j['jam'], 'is_sent' => $isSent, 'updated_at' => now() ]); } Log::info( "NOTIFIKASI DIPERBARUI USER {$idUser}" ); } catch (\Exception $e) { Log::error( 'UPDATE NOTIF ERROR: ' . $e->getMessage() ); } } }