LAPORAN PROYEK UJIAN TENGAH SEMESTER
ANALISIS EPIDEMIOLOGI DEMAM
BERDARAH DENGUE (DBD) JAWA BARAT
EPIDEMIOLOGI 2025

Disusun Oleh:
Farah Nadira
NPM: 140610230015

Dosen Mata Kuliah:
Dr. I Gede Nyoman Mindra Jaya, M.Si.
NIP: 19800603 200312 1 002

ABSTRAK

Demam Berdarah Dengue (DBD) merupakan salah satu penyakit menular di Provinsi Jawa Barat dengan jumlah kasus yang meningkat drastis dari tahun 2023. Penelitian ini bertujuan untuk mendeskripsikan pola spasial kasus DBD tahun 2024 serta menganalisis hubungan faktor lingkungan terhadap peningkatan kejadian DBD. Data yang digunakan meliputi jumlah kasus dan kematian akibat DBD, jumlah penduduk, kejadian banjir, persentase rumah tangga dengan sanitasi layak, dan persentase rumah layak huni pada 27 kabupaten/kota. Analisis dilakukan menggunakan pendekatan deskriptif epidemiologi melalui ukuran frekuensi berupa prevalensi dan Case Fatality Rate (CFR), serta pemetaan spasial untuk melihat pola distribusi antarwilayah. Hasil menunjukkan bahwa faktor lingkungan seperti curah hujan yang memicu banjir, sanitasi yang kurang memadai, kondisi rumah yang tidak layak huni, serta kepadatan penduduk berkontribusi terhadap peningkatan potensi perkembangbiakan nyamuk Aedes dan risiko penularan DBD. Secara spasial, pola distribusi kasus memperlihatkan bahwa wilayah Jawa Barat bagian tengah dan barat memiliki intensitas kasus yang lebih tinggi dibandingkan wilayah timur. Kota Bandung tercatat memiliki jumlah kasus tertinggi, Kota Sukabumi memiliki prevalensi tertinggi, sedangkan Kota Cimahi memiliki CFR tertinggi meskipun jumlah kasusnya tidak termasuk besar. Perbedaan ini menunjukkan bahwa tingginya prevalensi lebih berkaitan dengan faktor lingkungan dan risiko paparan, sedangkan tingginya CFR menggambarkan kapasitas deteksi dini dan layanan kesehatan. Temuan penelitian ini menekankan pentingnya intervensi berbasis lingkungan, perbaikan sanitasi, pengelolaan risiko banjir, serta peningkatan kualitas layanan kesehatan untuk menurunkan angka kejadian dan kematian akibat DBD di Jawa Barat.

PENDAHULUAN

Latar Belakang

Demam Berdarah Dengue (DBD) merupakan penyakit infeksi virus Dengue yang ditularkan melalui gigitan nyamuk Aedes baik Aedes aegypti maupun Aedes albopictus. Indonesia sebagai negara dengan iklim tropis menjadi daerah yang tepat untuk nyamuk Aedes berkembang biak. Selain itu, Indonesia dengan 285,7 juta jiwa yang menempati posisi keempat sebagai negara terpadat di dunia dengan kepadatan penduduk diperkirakan mencapai 158 jiwa per km^2 menjadi salah satu alasan memperkuat tersebarnya penyakit DBD di Indonesia akibat lingkungan yang padat dan mobilitas penduduk yang tinggi.

Jawa Barat sebagai provinsi terpadat kedua di Indonesia pun diketahui menjadi provinsi dengan jumlah kasus DBD tertinggi di Indonesia, berdasarkan data yang diambil dari Open Data Jabar yakni sebanyak 61.423 kasus di tahun 2024, meningkat 217,8% dari tahun 2023 yang hanya berjumlah 19.328 kasus yang tersebar di 27 kabupaten/kota di Provinsi Jawa Barat. Selain jumlah kasus yang tinggi, Jawa Barat juga merupakan provinsi dengan jumlah kematian akibat DBD tertinggi di Indonesia, yakni sebanyak 339 kasus kematian.

Jumlah kasus meningkat dan kematian yang tinggi tersebut menimbulkan kekhawatiran khususnya bagi pemerintah dan fasilitas kesehatan dan menunjukkan bahwa upaya Pemberantasan Sarang Nyamuk (PSN) di masyarakat masih belum optimal. Selain itu, perubahan iklim dan perilaku masyarakat juga diduga memperparah situasi ini. Hal-hal tersebut kemudian menjadi faktor dari urgensinya penelitian Demam Berdarah Dengue (DBD) di Jawa Barat yang kemudian hasilnya dapat digunakan oleh pihak berwenang terkait untuk mengurangi angka kenaikan kasus DBD dan mengetahui kabupaten/kota apa yang memerlukan perhatian khusus dan harus diaudit penanganannya akibat jumlah kematian yang juga tinggi.

Rumusan Masalah

Berdasarkan uraian latar belakang tersebut, didapat rumusan masalah dalam penelitian ini antara lain:

  1. Bagaimana gambaran kasus DBS di Provinsi Jawa Barat?
  2. Bagaimana proses terjadinya penyakit DBD ditinjau berdasarkan model agent host environment di Provinsi Jawa Barat?
  3. Bagaimana pola persebaran kasus DBD antar kabupaten/kota di Provinsi Jawa Barat?
  4. Wilayah mana yang perlu mendapatkan perhatian khusus dan menjadi prioritas utama dalam upaya pencegahan dan pengendalian DBD?

Tujuan Penelitian

Dari rumusan masalah di atas, tujuan penelitian yang akhirnya dijadikan dasar dalam penelitian ini antara lain:

  1. Mendeskripsikan gambaran kasus DBD di Provinsi Jawa Barat.
  2. Menjelaskan proses terjadinya DBD berdasarkan pendekatan agent-host-environment.
  3. Mengetahui persebaran kasus DBD antar wilayah kabupaten/kota di Provinsi Jawa Barat.
  4. Mengetahui wilayah yang perlu mendapatkan perhatian khusus dan menjadi prioritas utama dalam upaya pencegahan dan pengendalian DBD.

TINJAUAN PUSTAKA

Konsep Dasar Epidemiologi

Epidemiologi adalah cabang ilmu kesehatan masyarakat yang mempelajari kejadian penyakit dalam suatu populasi, termasuk seberapa sering penyakit muncul (frekuensi), bagaimana pola penyebarannya berdasarkan waktu, tempat, dan karakteristik orang (distribusi), serta faktor-faktor yang memengaruhinya (determinan). Secara etimologi, istilah ini berasal dari bahasa Yunani epi (pada), demos (penduduk), dan logos (ilmu). Dalam praktiknya, epidemiologi digunakan untuk mengidentifikasi penyebab penyakit, memahami mekanisme penularan, serta merancang upaya pencegahan dan pengendalian agar masalah kesehatan dapat diminimalkan secara efektif di tingkat populasi.

Model Agent-Host-Environment

Model segitiga epidemiologi menjelaskan bahwa munculnya penyakit terjadi karena interaksi antara tiga komponen utama: agent, host, dan environment. Agent adalah penyebab penyakit, bisa berupa virus, bakteri, parasit, atau zat kimia yang menimbulkan gangguan kesehatan. Kemampuan agent untuk menular, bertahan hidup, dan memicu reaksi pada tubuh manusia menentukan seberapa besar potensi penularannya. Host adalah individu yang bisa terinfeksi, dengan tingkat kerentanan yang dipengaruhi oleh faktor seperti usia, status gizi, daya tahan tubuh, genetik, dan perilaku. Sementara itu, environment atau lingkungan mencakup faktor fisik (iklim, suhu, kelembapan), biologis (keberadaan vektor), dan sosial (kepadatan penduduk, kebersihan, perilaku masyarakat) yang dapat mendukung atau menghambat penularan penyakit.

Ketiga komponen ini saling berinteraksi dan menentukan apakah suatu penyakit bisa muncul atau tidak. Penyakit akan terjadi jika agent yang patogen bertemu dengan host yang rentan dalam lingkungan yang kondusif. Sebaliknya, jika salah satu faktor tidak mendukung, misalnya host memiliki kekebalan kuat atau lingkungan tidak sesuai untuk agent penyakit bisa dicegah.

Ukuran Epidemiologi

Di dalam studi epidemiologi terdapat dua jenis ukuran, yakni ukuran frekuensi yang digunakan untuk mengetahui frekuensi atau seberapa sering suatu penyakit terjadi dan ukuran asosiasi yang digunakan untuk mengetahui seberapa kuat hubungan faktor risiko dengan kejadian penyakit tersebut.

Baik ukuran frekuensi maupun ukuran asosiasi sangat penting untuk membantu pihak terkait dalam memahami pola penyakit dan menentukan prioritas dari penyelesaian faktor-faktor yang paling erat hubungannya.

Ukuran Frekuensi

Ukuran frekuensi menggambarkan seberapa sering suatu penyakit atau kejadian kesehatan terjadi pada populasi tertentu dalam periode waktu tertentu.

  1. Prevalensi menunjukkan berapa banyak orang yang sakit (baik kasus baru maupun lama) pada satu waktu tertentu. Biasanya digunakan untuk mengukur beban penyakit di masyarakat.
    …(1)

  2. Cumulative Incidence (Insidensi Kumulatif) menunjukkan proporsi individu yang menjadi kasus baru selama periode waktu tertentu. Ukuran ini digunakan untuk mengetahui risiko seseorang tertular atau terkena penyakit dalam periode tertentu.
    x 100%

  3. Incidence Rate (Angka Insidensi) menunjukkan laju terjadinya kasus baru suatu penyakit dalam periode tertentu di antara populasi yang berisiko. Ukuran ini menggambarkan kecepatan munculnya kasus baru dalam suatu waktu.

  4. Case Fatality Rate (CFR) menunjukkan persentase penderita yang meninggal akibat penyakit tersebut. CFR sering digunakan untuk mengukur tingkat keparahan penyakit, misalnya kalau CFR-nya tinggi berarti penyakit itu cenderung berbahaya
    …(2)

Ukuran Asosiasi

Ukuran asosiasi digunakan untuk menilai hubungan antara paparan (exposure) dan kejadian penyakit (outcome). Tujuannya adalah untuk mengetahui apakah suatu faktor benar-benar berperan sebagai penyebab atau faktor risiko penyakit.

  1. Relative Risk (RR) digunakan pada penelitian kohort untuk membandingkan risiko penyakit antara kelompok terpapar dan tidak terpapar.

  2. Odds Ratio (OR) digunakan pada penelitian kasus-kontrol untuk membandingkan peluang paparan antara kelompok kasus dan kontrol.

  3. Attributable Risk (AR) atau risk difference menunjukkan selisih risiko antara kelompok terpapar dan tidak terpapar, menggambarkan seberapa besar proporsi kejadian penyakit yang bisa dicegah jika paparan dihilangkan.

  4. Population Attributable Risk (PAR) menunjukkan seberapa besar proporsi penyakit di populasi yang disebabkan oleh paparan tertentu. Jadi, ukuran ini bantu nunjukin dampak faktor risiko terhadap keseluruhan populasi.

Desain Studi

Desain studi epidemiologi digunakan untuk meneliti hubungan antara paparan (exposure) dan kejadian penyakit (outcome) dalam suatu populasi. Secara umum, desain ini dibagi menjadi dua kelompok besar, yaitu studi observasional, di mana peneliti tidak melakukan intervensi terhadap subjek, dan studi eksperimental, di mana peneliti secara langsung memberikan perlakuan atau intervensi untuk melihat pengaruhnya terhadap outcome.

  1. Cross-sectional study atau studi potong lintang
    Desain ini mengukur paparan dan penyakit pada waktu yang sama, sehingga memberikan gambaran tentang kondisi populasi pada saat tertentu. Studi ini biasanya digunakan untuk memperkirakan prevalensi penyakit atau faktor risiko, serta untuk mengeksplorasi hubungan awal antara variabel. Kelebihan studi ini adalah prosesnya cepat dan biayanya relatif murah, cocok untuk survei kesehatan masyarakat. Namun, karena tidak dapat menentukan urutan waktu antara paparan dan penyakit, studi ini tidak bisa memastikan hubungan sebab-akibat dan kurang tepat digunakan untuk penyakit langka.

  2. Case-control study atau studi kasus-kontrol
    Desain yang membandingkan individu yang memiliki penyakit (kasus) dengan individu yang tidak memiliki penyakit (kontrol), kemudian menelusuri apakah mereka pernah terpapar faktor risiko di masa lalu. Studi ini bersifat retrospektif karena dimulai dari outcome menuju exposure. Ukuran asosiasi yang digunakan adalah Odds Ratio (OR). Keunggulan desain ini adalah efisien untuk penyakit yang jarang terjadi dan tidak membutuhkan waktu lama, namun memiliki kelemahan berupa potensi bias recall dan bias seleksi karena bergantung pada ingatan atau catatan masa lalu.

  3. Cohort study atau studi kohort
    Desain ini menjelaskan di mana sekelompok individu yang terpapar dan tidak terpapar diikuti dalam jangka waktu tertentu untuk melihat apakah mereka mengalami penyakit atau tidak. Studi ini bisa dilakukan secara prospektif (ke depan) maupun retrospektif (menggunakan data historis). Ukuran asosiasi yang umum digunakan adalah Relative Risk (RR). Keunggulan studi kohort adalah kemampuannya menentukan hubungan sebab-akibat serta mengukur beberapa outcome sekaligus. Namun, kekurangannya adalah membutuhkan waktu lama, biaya besar, dan risiko kehilangan partisipan selama periode pengamatan.

  4. Randomized Controlled Trial (RCT)
    RCT termasuk dalam kategori studi eksperimental. Pada desain ini, peserta dibagi secara acak menjadi dua kelompok, yaitu kelompok intervensi dan kelompok kontrol. Tujuannya adalah menilai efektivitas suatu intervensi atau terapi dengan meminimalkan bias melalui randomisasi dan kontrol. RCT dianggap sebagai desain dengan tingkat bukti paling kuat dalam menentukan hubungan sebab-akibat, meskipun dalam pelaksanaannya membutuhkan biaya besar, waktu lama, serta pengawasan etika yang ketat.

METODOLOGI PENELITIAN

Sumber Data

Data yang digunakan merupakan data sekunder yang berkaitan dengan Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat dengan 27 kabupaten/kota tahun 2024 yang bersumber dari sumber resmi seperti Badan Pusat Statistik (BPS) dan Open Data Provinsi Jawa Barat.

Variabel Penelitian

Penelitian ini menggunakan beberapa variabel yang berkaitan dengan kejadian Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat. Variabel-variabel tersebut dibagi menjadi dua kelompok, yaitu variabel dependen (terikat) dan variabel independen (bebas). Variabel dependen dalam penelitian ini adalah jumlah kasus DBD di setiap kabupaten/kota di Jawa Barat, yang menggambarkan tingkat kejadian penyakit dan menjadi fokus utama analisis. Selain itu, jumlah kasus meninggal akibat DBD juga digunakan sebagai indikator tingkat keparahan.

Sementara itu, variabel independen meliputi beberapa faktor lingkungan dan sosial yang diduga berhubungan dengan penyebaran DBD. Variabel-variabel tersebut antara lain:

  1. Jumlah penduduk di setiap kabupaten/kota, yang digunakan sebagai dasar perhitungan angka prevalensi. Jumlah penduduk sering kali berkaitan dengan peningkatan risiko penularan penyakit menular, termasuk DBD.

  2. Jumlah kejadian bencana banjir, yang mencerminkan kondisi lingkungan yang lembap dan berpotensi menimbulkan genangan air pasca banjir, tempat ideal bagi nyamuk Aedes untuk berkembang biak.

  3. Persentase rumah tangga yang memiliki akses terhadap sanitasi layak, sebagai indikator kebersihan lingkungan dan potensi terbentuknya genangan baru akibat sistem sanitasi yang kurang baik.

  4. Persentase rumah tangga yang menempati rumah layak huni, yang menunjukkan kondisi fisik perumahan masyarakat. Rumah yang tidak layak huni dapat meningkatkan risiko kontak dengan nyamuk pembawa virus DBD.

Hubungan antara variabel-variabel tersebut dianalisis untuk melihat apakah terdapat keterkaitan antara kondisi lingkungan, sosial, dan jumlah kasus DBD di Jawa Barat. Dengan mengetahui variabel yang berpengaruh, diharapkan hasil penelitian ini dapat memberikan gambaran faktor risiko utama yang berkontribusi terhadap tingginya angka DBD di wilayah tersebut.

Kemudian digunakan juga variabel bantu untuk mencari ukuran frekuensi dari penyakit DBD di Provinsi Jawa Barat, adapun variabel tersebut antara lain:

  1. Jumlah penduduk yang digunakan untuk menghitung prevalensi sesuai dengan rumus ..(1).
  2. Jumlah kasus meninggal akibat DBD yang digunakan untuk menghitung Case Fatality Rate (CFR) sesuai dengan rumus ..(2).

Metode Analisis

Penelitian ini menggunakan pendekatan analisis deskriptif epidemiologi untuk memahami pola penyebaran dan faktor yang berhubungan dengan kejadian Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat. Analisis deskriptif dipilih karena sesuai untuk menggambarkan karakteristik penyakit berdasarkan waktu, tempat, dan populasi tanpa memanipulasi variabel. Analisis dilakukan melalui ukuran frekuensi untuk melihat seberapa besar penyebaran DBD pada tiap wilayah dengan menghitung prevalensi dan case fatality rate (CFR), ini membantu menggambarkan tingkat kejadian dan keparahan penyakit di populasi. Selain itu, digunakan statistika deskriptif untuk memperjelas distribusi dan karakteristik data, serta visualisasi peta dan grafik untuk memperlihatkan pola spasial antarwilayah.

Dengan metode ini, hasil analisis diharapkan mampu memberikan gambaran yang lebih jelas tentang hubungan antara kondisi lingkungan, sosial, dan tingkat kejadian DBD di Jawa Barat. Untuk menerapkan metode tersebut secara sistematis, penelitian ini mengikuti beberapa tahapan analisis yang dijabarkan pada bagian alur kerja.

Alur Kerja

Alur kerja penelitian ini dimulai dari tahap pengumpulan data sekunder yang bersumber dari Open Data Jabar dan Badan Pusat Statistik (BPS). Data yang dikumpulkan meliputi jumlah kasus dan kematian akibat DBD, jumlah penduduk, serta beberapa faktor lingkungan seperti kejadian banjir, sanitasi, dan kondisi rumah di tiap kabupaten/kota di Jawa Barat.

Selanjutnya dilakukan tahap pengolahan data, seperti pengecekan kesesuaian format, penyatuan nama wilayah, serta pembuatan variabel turunan agar data siap dianalisis. Setelah data bersih, dilakukan analisis statistika deskriptif untuk memberikan gambaran umum mengenai sebaran data, seperti nilai rata-rata, minimum, maksimum, dan persebaran kasus DBD di tiap kabupaten/kota.

Kemudian dilanjutkan dengan perhitungan ukuran frekuensi untuk melihat pola penyebaran DBD di wilayah Jawa Barat. Ukuran frekuensi yang digunakan meliputi prevalensi, yang menunjukkan proporsi penduduk yang terjangkit DBD; angka insidensi, yang menggambarkan laju munculnya kasus baru; serta case fatality rate (CFR), yang menunjukkan tingkat keparahan penyakit berdasarkan perbandingan jumlah kematian dan total kasus. Tahap berikutnya adalah analisis ukuran asosiasi untuk menilai seberapa besar pengaruh faktor lingkungan dan sosial terhadap peningkatan kasus DBD di tiap wilayah.

Tahap akhir adalah visualisasi dan interpretasi hasil. Data yang telah dianalisis disajikan dalam bentuk grafik dan peta distribusi guna menggambarkan pola spasial antarwilayah. Dari hasil tersebut, dilakukan interpretasi untuk memahami kecenderungan, wilayah dengan kasus tinggi, serta faktor risiko yang mungkin berperan dalam penyebaran DBD di Jawa Barat.

HASIL DAN PEMBAHASAN

Deskripsi Kasus

Jumlah Kasus Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat terhitung sangat tinggi dengan Kota Bandung sebagai Kab/Kota dengan jumlah kasus tertinggi di angka sekitar 7,680 kasus, kemudian di bawahnya kota Depok dan Bekasi dengan angka kasus yang cukup jauh yakni Depok sebanyak 5,040 kasus dan Bekasi sebanyak 4,167 kasus.

Dapat dilihat dari boxplot di atas, mayoritas kabupaten/kota berada di kisaran 900 sampai 3200 kasus jumlah DBD yang berarti utamanya sebaran data jumlah kasus DBD di Provinsi Jawa Barat berada pada rentang tersebut. Namun, dapat dilihat terdapat satu outlier dengan jumlah kasus yang sangat tinggi yakni sekitar 7600 kasus yang menandakan adanya satu wilayah dengan beban penanganan DBD jauh lebih besar dibandingkan wilayah lainnya.

Faktor Agent, Host, Environment

Demam Berdarah Dengue (DBD) merupakan penyakit infeksi yang disebabkan oleh virus dengue dan ditularkan melalui gigitan nyamuk Aedes aegypti. Penularan penyakit ini dapat dijelaskan menggunakan model agent host environment, di mana ketiga faktor tersebut saling berinteraksi dan berperan dalam terjadinya penyakit.

  1. Agent (Agen penyebab) Agen penyebab DBD adalah virus dengue yang terdiri dari empat serotipe (DEN-1 hingga DEN-4) dari famili Flaviviridae. Virus ini ditularkan melalui gigitan nyamuk Aedes yang berkembang biak di air bersih dan genangan yang tidak tertutup.

  2. Host (Pejamu) Host atau pejamu utama penyakit ini adalah manusia. Risiko tertinggi terdapat pada anak-anak dan masyarakat yang tinggal di wilayah padat penduduk. Beberapa faktor yang mempengaruhi kerentanan tubuh host seperti golongan darah, jenis kelamin, status gizi, dan sebagainya juga dapat mempengaruhi terjangkitnya host (manusia) dari penyakit DBD.

  3. Environment (Lingkungan) Faktor lingkungan berperan penting dalam penyebaran DBD. Lingkungan dengan sanitasi buruk, curah hujan tinggi, genangan air, dan kepadatan penduduk yang tinggi menjadi tempat ideal bagi perkembangbiakan nyamuk. Lingkungan dengan suhu yang tinggi juga dapat mempercepat perkembangbiakan nyamuk.

Ketiga faktor tersebut saling berhubungan dalam siklus penularan DBD. Upaya pengendalian penyakit perlu dilakukan secara terpadu melalui perbaikan kondisi lingkungan, pengendalian vektor nyamuk, serta peningkatan kesadaran masyarakat terhadap kebersihan dan pencegahan penyakit.

Diagram Hubungan Faktor Risiko dan Penyakit

Berikut merupakan diagram dari model segitiga epidemiologi (Agent, Host, Environment) yang menjelaskan bagaimana faktor lingkungan, host (manusia), dan agent (virus dengue) saling berinteraksi hingga menyebabkan kasus Demam Berdarah Dengue.

Dari diagram di atas, dapat dilihat bahwa ketiga faktor berperan dan saling berhubungan terhadap terjadinya kasus penyakit DBD. Untuk Environment sendiri terbagi ke dalam beberapa variabel yang memungkinkan, antara lain:

  1. Suhu yang tinggi mempercepat siklus hidup nyamuk, proses perkembang biakan nyamuk Aedes yang semakin cepat akan membuat populasi nyamuk bertambah banyak.
  2. Banjir pada awalnya akan menghilangkan genangan tempat sarang nyamuk berkembang. Namun, setelah banjir surut, akan terbentuk banyak genangan baru; air hujan di wadah, air tergenang di tanah berkontur lubang, sampah menumpuk, dan banyak orang menyimpan air bersih karena supplai air terganggu akibat banjir. Kondisi setelah banjir inilah yang menyebabkan meningkatnya jumlah genangan air, yang selanjutnya juga meningkatkan jumlah nyamuk Aedes.
  3. Salah satu bentuk dari pengelolaan sanitasi yang buruk adalah terbentuknya banyak genangan air yang kemudian akan meningkatkan jumlah nyamuk Aedes.
  4. Pada wilayah dengan penduduk padat, terdapat banyak wadah dan tempat penampungan air rumah tangga seperti bak mandi, ember, pot tanaman, serta saluran air yang berpotensi menjadi lokasi perkembangbiakan larva nyamuk. Ketika curah hujan tinggi, volume genangan air dan tampungan air hujan di lingkungan tersebut semakin bertambah, sehingga menyediakan habitat ideal bagi telur Aedes untuk menetas dan berkembang. Kondisi lembap setelah hujan juga akan memperpanjang umur nyamuk dewasa.

Kelima variabel dari environment tersebut berujung pada peningkatan jumlah nyamuk Aedes. Khusus untuk variabel kepadatan penduduk juga berhubungan langsung ke faktor host, yaitu manusia. Pada kawasan dengan pemukiman yang rapat dan mobilitas masyarakat yang tinggi, nyamuk lebih mudah menemukan inang untuk menggigit dan memperoleh darah, sehingga peluang penularan virus dengue antarindividu meningkat. Peningkatan jumlah nyamuk Aedes juga akan menambah vektor (agent) Dengue virus. Selain itu, tingginya jumlah penduduk juga menyebabkan penyebaran infeksi dapat berlangsung lebih cepat ketika terdapat kasus positif, karena siklus nyamuk-manusia-nyamuk lebih mudah terjadi.

Ukuran Epidemiologi

Ukuran Frekuensi

Akibat keterbatasan data yang ada, ukuran frekuensi yang dapat dihitung pada analisis kali ini hanya prevalensi dan Case Fatality Rate (CFR).

Prevalensi

Hasil perhitungan menunjukkan bahwa Kota Sukabumi memiliki prevalensi DBD tertinggi di Jawa Barat, yaitu sekitar 0,45% atau 446 kasus per 100.000 penduduk. Angka ini menunjukkan bahwa dari setiap 100.000 penduduk, terdapat sekitar 446 orang yang terinfeksi DBD selama tahun 2024. Tingginya prevalensi ini kemungkinan dipengaruhi oleh kepadatan permukiman, frekuensi kejadian banjir yang cukup tinggi, persentase rumah layak huni yang masih rendah, serta akses sanitasi yang belum optimal, yang semuanya mendukung perkembangbiakan nyamuk Aedes aegypti sebagai vektor DBD. Selanjutnya, Kota Bandung (0,30%) dan Kota Bogor (0,29%) juga menunjukkan prevalensi tinggi, sejalan dengan karakteristik wilayah perkotaan padat penduduk. Kota Tasikmalaya dan Kota Depok berada di posisi menengah dengan nilai sekitar 0,23%, sedangkan Pangandaran, Bandung Barat, Sumedang, Kota Banjar, dan Kuningan menempati urutan berikutnya dengan prevalensi di bawah 0,21%. Rendahnya angka ini dapat dikaitkan dengan tingkat akses sanitasi yang lebih baik, persentase rumah layak huni yang tinggi, dan kejadian banjir yang relatif jarang, sehingga risiko penularan lebih kecil.

Secara umum, kota dengan kepadatan penduduk tinggi cenderung memiliki angka prevalensi DBD lebih besar dibandingkan daerah perdesaan. Hal ini menegaskan pentingnya pengendalian berbasis lingkungan dan upaya preventif untuk menekan penularan DBD di wilayah perkotaan.

Case Fatality Rate (CFR)

Berdasarkan grafik Case Fatality Rate (CFR) DBD di Jawa Barat, terlihat bahwa beberapa wilayah memiliki tingkat kematian yang lebih tinggi dibandingkan daerah lainnya. Kota Cimahi menempati posisi tertinggi, diikuti oleh Kabupaten Subang, Purwakarta, dan Kota Banjar. Angka CFR yang lebih tinggi di wilayah-wilayah tersebut menunjukkan bahwa proporsi pasien DBD yang meninggal relatif lebih besar dibandingkan daerah lain di Jawa Barat.

Sementara itu, daerah seperti Pangandaran, Kuningan, Kota Depok, dan Kota Tasikmalaya menunjukkan CFR yang lebih rendah. Hal ini mengindikasikan bahwa meskipun kasus DBD mungkin terjadi, kemampuan dalam penanganan pasien atau kecepatan deteksi dan respon kesehatan di wilayah tersebut relatif lebih baik sehingga kematian dapat ditekan.

Ukuran Asosiasi

Akibat keterbatasan data, tidak ada ukuran asosiasi yang dapat dihitung dari data yang ada.

Visualisasi

Peta Jumlah Kasus DBD (Distribusi Kasus)

Berdasarkan peta sebaran jumlah kasus DBD, terdapat daerah berwarna gelap berwarna ungu tua yang merupakah wilayah Kota Bandung dengan jumlah kasus di atas 6000, ini menunjukkan Kota Bandung sebagai wilayah dengan jumlah kasus DBD tertinggi di Provinsi Jawa Barat. Kemudian daerah seperti Kota Bekasi dan Kota Depok berwarna merah-oranye gelap yang berarti jumlah kasus termasuk tinggi dan berada di wilayah metropolitan bagian barat. Sementara wilayah Selatan Jawa Barat tampak berwarna lebih kuning menunjukkan jumlah kasus relatif rendah yakni di bawah 3000, ini sesuai dengan kepadatan penduduk wilayah Selatan Jawa Barat yang juga lebih rendah serta lingkungan yang lebih rural, sehingga potensi kontak antara vektor atau Dengue virus dengan manusia lebih rendah.

Peta Jumlah Penduduk Provinsi Jawa Barat

Berdasarkan peta sebaran jumlah penduduk, terdapat daerah berwarna hijau sangat gelap yang merupakan wilayah Kota Bekasi dengan jumlah penduduk di atas 5 juta jiwa, ini menunjukkan Kota Bekasi sebagai wilayah dengan jumlah penduduk tertinggi di Provinsi Jawa Barat. Kemudian daerah seperti Kabupaten Bogor dan Kabupaten Bandung tampak berwarna hijau gelap yang berarti jumlah penduduk termasuk tinggi dan berada pada wilayah dengan konsentrasi pemukiman besar. Sementara wilayah Selatan Jawa Barat tampak berwarna lebih terang menunjukkan jumlah penduduk relatif rendah yakni di bawah 2 juta jiwa. Selain itu, terlihat bahwa wilayah Jawa Barat bagian timur juga didominasi warna hijau muda, yang menunjukkan jumlah penduduk relatif rendah, menandakan bahwa kawasan timur Jawa Barat memiliki distribusi penduduk yang lebih sedikit dan tersebar dibandingkan wilayah tengah hingga barat yang merupakan pusat aktivitas dan pemukiman padat.

Peta Prevalensi DBD

Pada peta prevalensi, intensitas warna gelap terlihat pada wilayah-wilayah dengan proporsi kasus terhadap jumlah penduduk lebih tinggi. Kota Sukabumi (445,94 per 100.000 penduduk), Kota Bandung (303,77 per 100.000), dan Kab. Sumedang (193,99 per 100.000) tampak menjadi klaster dengan tingkat risiko tinggi. 10Warna yang lebih gelap pada peta menunjukkan bahwa meskipun beberapa wilayah tidak memiliki jumlah kasus tertinggi secara absolut, proporsi penduduk yang terdampak sangat besar.

Di sisi lain, wilayah seperti Kab. Indramayu (27,58 per 100.000) dan Kab. Sukabumi (29,73 per 100.000) tampak lebih terang, menandakan prevalensi rendah. Kondisi ini menunjukkan bahwa distribusi beban penyakit tidak hanya dipengaruhi jumlah kasus, tetapi juga ukuran populasi. Artinya, wilayah lebih kecil dengan kasus cukup besar dapat menghasilkan prevalensi yang lebih tinggi dibandingkan kota besar.

Peta Kasus Meninggal Akibat DBD

Pada peta kematian DBD, warna lebih gelap menandakan jumlah kematian lebih tinggi. Daerah seperti Kab. Bandung (38 kematian), Kota Bandung (31 kematian), Kab. Bogor (23 kematian), dan Subang (25 kematian) tampak lebih gelap karena mencatat kematian lebih banyak dibanding wilayah lain. Sementara daerah seperti Pangandaran (0 kematian) dan Indramayu (2 kematian) terlihat lebih terang, menunjukkan angka kematian yang sangat rendah.

Beberapa wilayah dengan prevalensi tinggi seperti Kota Sukabumi justru tidak terlalu berwarna gelap (6 kematian) yang mengindikasikan penanganan klinis yang cukup baik. Secara umum, pola ini menunjukkan bahwa angka kasus tinggi tidak selalu berujung pada angka kematian tinggi, sehingga faktor layanan kesehatan dan deteksi dini memegang peran penting dalam menekan fatalitas DBD.

Peta Case Fatality Rate (CFR)

Peta CFR menunjukkan intensitas warna gelap pada wilayah dengan tingkat fatalitas lebih tinggi. Daerah seperti Kota Cimahi (1.41%), Kab. Subang (1.29%), dan Kota Banjar (1.25%) tampak gelap, menandakan proporsi kematian yang lebih tinggi dibandingkan jumlah kasus. Ini dapat mencerminkan keterlambatan deteksi kasus, akses fasilitas kesehatan yang terbatas, atau kurangnya kesadaran masyarakat terkait tanda bahaya DBD.

Sebaliknya, wilayah seperti Pangandaran (0%), Kab. Sumedang (0.30%), dan Kota Tasikmalaya (0.28%) tampak lebih terang, menunjukkan bahwa meskipun ada transmisi aktif, penanganan klinis lebih efektif sehingga angka kematian lebih rendah. Pola ini menegaskan bahwa tingginya kasus tidak selalu berbanding lurus dengan fatalitas, kualitas respon kesehatan sangat berperan.

Desain Studi Epidemiologi

Desain Studi

Dilakukan penelitian dengan menggunakan desain cross-sectional (potong lintang) di mana unit analisis adalah kabupaten/kota di Provinsi Jawa Barat pada tahun 2024. Penelitian ini dilakukan untuk mengetahui hubungan antara variabel akses sanitasi, jumlah banjir, dan akses rumah layak huni terhadap jumlah kasus DBD di Provinsi Jawa Barat.

Variabel Utama

Variabel utama yang digunakan adalah variabel Jumlah Kasus DBD per kabupaten/kota di Provinsi Jawa Barat dengan total 27 kabupaten/kota. Sedangkan variabel lainnya adalah variabel akses sanitasi, jumlah banjir, dan akses rumah layak huni terhadap jumlah kasus DBD di Provinsi Jawa Barat.

Semua data yang digunakan bersumber dari Open Data Jabar, Badan Pusat Statistik (BPS), dan Dinas Kesehatan Provinsi Jawa Barat dalam periode waktu yang sama (tahun 2024), agar hasil analisis tetap konsisten dan dapat dibandingkan antarwilayah.

Populasi dan Sampling

Populasi penelitian mencakup 27 kabupaten/kota di Provinsi Jawa Barat, di mana setiap wilayah menjadi satu unit analisis. Karena semua wilayah yang ada dimasukkan dalam penelitian, metode yang digunakan adalah total sampling, sehingga gambaran yang dihasilkan mewakili keseluruhan provinsi tanpa harus melakukan generalisasi dari sampel kecil. Semua data diperoleh dari sumber resmi pemerintah pada periode yang sama agar hasilnya tetap konsisten.

Potensi Bias

Potensi bias utama dalam penelitian ini adalah bias ekologis, yaitu ketika hubungan yang ditemukan pada level kabupaten/kota tidak selalu berlaku pada tingkat individu. Misalnya, wilayah dengan sanitasi buruk dan angka DBD tinggi belum tentu berarti semua individu di wilayah tersebut memiliki risiko tinggi yang sama.

Selain itu, terdapat kemungkinan bias informasi, seperti perbedaan kualitas dan ketepatan pelaporan kasus antarwilayah atau adanya kasus yang tidak tercatat (underreporting). Beberapa variabel pengganggu (confounding) seperti curah hujan, suhu udara, dan perilaku masyarakat dalam menjaga kebersihan lingkungan juga belum sepenuhnya tercakup dalam analisis ini, sehingga dapat memengaruhi hasil secara tidak langsung.

KESIMPULAN DAN SARAN

Kesimpulan

Faktor lingkungan memiliki peran penting dalam proses terjadinya peningkatan kasus DBD. Suhu yang tinggi mempercepat siklus hidup nyamuk Aedes, sementara curah hujan dan banjir menciptakan banyak genangan air baru yang menjadi tempat ideal bagi nyamuk berkembang biak. Kondisi sanitasi yang buruk turut memperbanyak tempat perindukan nyamuk, terutama di wilayah padat penduduk yang memiliki banyak wadah penampungan air. Ketika curah hujan tinggi, volume genangan dan kelembapan lingkungan meningkat, memperpanjang umur nyamuk dewasa dan jumlah penduduk yang tinggi mempercepat penularan virus.

Untuk pola spasial menunjukkan bahwa wilayah Jawa Barat bagian tengah dan barat memiliki intensitas kasus yang lebih tinggi dibandingkan bagian timur dan selatan. Warna yang lebih muda pada peta distribusi di wilayah timur menandakan rendahnya jumlah kasus, sejalan dengan karakter wilayah yang lebih rural dan kepadatan penduduk yang lebih rendah. Hasil interpretasi spasial juga memperlihatkan bahwa kombinasi antara kepadatan penduduk tinggi dan curah hujan besar berhubungan dengan peningkatan populasi nyamuk Aedes aegypti, sehingga memperbesar potensi penularan DBD. Dengan demikian, wilayah perkotaan dengan kondisi lingkungan padat dan curah hujan tinggi menjadi area dengan risiko paling tinggi terhadap kejadian DBD.

Hasil analisis juga menunjukkan bahwa Kota Bandung memiliki jumlah kasus DBD terbanyak, Kota Sukabumi memiliki prevalensi tertinggi, dan Kota Cimahi mencatat Case Fatality Rate (CFR) tertinggi, meskipun jumlah kasusnya tidak termasuk yang paling tinggi. Hal ini menunjukkan bahwa masih terdapat kekurangan dalam penanganan kasus DBD di Kota Cimahi sehingga perlu difokuskan untuk meningkatkan pengendalian atau penanganan kesehatan atas kasus DBD di Kota Cimahi. Kemudian dibutuhkan proses pencegahan yang baik di Kota Bandung sebagai kabupaten/kota dengan jumlah kasus DBD tertinggi dan Kota Sukabumi sebagai kabupaten/kota dengan angka prevalensi tertinggi.

Saran

Untuk menurunkan risiko penularan DBD, pemerintah daerah perlu memperkuat intervensi lingkungan sebagai langkah pencegahan. Upaya ini mencakup perbaikan sanitasi, pengelolaan sampah dan drainase, pengurangan genangan air, serta peningkatan kegiatan PSN 3M Plus terutama di wilayah dengan kepadatan penduduk tinggi. Untuk daerah seperti Kota Bandung dan Kota Sukabumi yang menunjukkan jumlah kasus dan prevalensi tinggi, program pengendalian vektor harus dilakukan secara lebih intensif dan berkelanjutan, termasuk edukasi masyarakat, pemantauan jentik berkala, serta peningkatan partisipasi warga dalam menjaga kebersihan lingkungan.

Sementara itu, daerah dengan CFR tinggi seperti Kota Cimahi memerlukan penguatan aspek layanan kesehatan. Hal ini dapat dilakukan melalui peningkatan kapasitas tenaga kesehatan dalam deteksi dini dan tata laksana kasus DBD, penyediaan fasilitas rawat inap yang memadai pada periode puncak kasus, serta memastikan sistem rujukan berjalan cepat dan efektif.

REFERENSI

     Liu, Z., Zhang, Q., Li, L., He, J., Guo, J., Wang, Z., Huang, Y., Xi, Z., Yuan, F., Li, Y., & Li, T. (2023). The effect of temperature on dengue virus transmission by Aedes mosquitoes. Frontiers in Cellular and Infection Microbiology, 13. https://doi.org/10.3389/fcimb.2023.1242173.

     Yani, A., Batang, J., No, H., 58, B., Sunggal, K., & , S. (2024). The Influence of Environmental Factors on the Development of Dengue Fever. The International Science of Health Journal. https://doi.org/10.59680/ishel.v2i4.1569.

     Rahmawati, Y., Jamil, I., & Hidayah, I. (2025). A Spatial Analysis on Heterogenous Determinant of Dengue Fever Cases in Indonesia. Journal of Geovisualization and Spatial Analysis. https://doi.org/10.1007/s41651-024-00212-1.

     Sajib, A., Akter, S., Saha, G., & Hossain, Z. (2024). Demographic-environmental effect on dengue outbreaks in 11 countries. PLOS ONE, 19. https://doi.org/10.1371/journal.pone.0305854.

     Sutriyawan, A., Martini, M., Sutiningsih, D., Akbar, H., Agushybana, F., Wahyuningsih, N., Nurlaela, S., & Eneojo, A. (2025). Spatial analysis of dengue incidence and linear effects with climate conditions in Bandung City Indonesia in 2021-2023. Journal of Public Health and Development. https://doi.org/10.55131/jphd/2025/230119.

     Ramadona, A., Tozan, Y., Lazuardi, L., & Rocklöv, J. (2019). A combination of incidence data and mobility proxies from social media predicts the intra-urban spread of dengue in Yogyakarta, Indonesia. PLoS Neglected Tropical Diseases, 13. https://doi.org/10.1371/journal.pntd.0007298.

     Khan, M., Abdalgader, T., Pedersen, M., & Zhang, L. (2025). Interactive effects of climate change and human mobility on dengue transmission. Ecological Modelling. https://doi.org/10.1016/j.ecolmodel.2024.110924.

     Araujo, J., Bomfim, R., Filho, C., Cavalcanti, L., Neto, A., Andrade, J., & Furtado, V. (2024). The impact of COVID-19 mobility restrictions on dengue transmission in urban areas. PLOS Neglected Tropical Diseases, 18. https://doi.org/10.1371/journal.pntd.0012644.

     Fauzi, I., Nuraini, N., Ayu, R., & Lestari, B. (2022). Temporal trend and spatial clustering of the dengue fever prevalence in West Java, Indonesia. Heliyon, 8. https://doi.org/10.1016/j.heliyon.2022.e10350.

     Yanti, Y., Rahardiantoro, S., & Dito, G. (2023). Spatio-temporal Clustering Analysis of Dengue Hemorrhagic Fever Cases in West Java 2016 – 2021. Indonesian Journal of Statistics and Its Applications. https://doi.org/10.29244/ijsa.v7i1p56-63.

     Jaya, I., Ruchjana, B., Abdullah, A., & Sunengsih, N. (2020). Multilevel model of dengue disease transmission in West Java province, Indonesia by means INLA. . https://doi.org/10.28919/cmbn/5134.

     Ridhatusalma, G., Diandra, S., Rahma, H., Pangastuti, S., & Roslan, N. (2024). Multiple Exponential Regression Modeling of Dengue Haemorrhagic Fever Factors in West Java Province, Indonesia. Indonesian Journal of Applied Mathematics and Statistics. https://doi.org/10.71385/idjams.v1i1.12.

LAMPIRAN

Link Dashboard https://farahnadira.shinyapps.io/DBDJawaBarat/

Syntax Dashboard

# app.R - Dashboard DBD
suppressPackageStartupMessages({
  library(shiny)
  library(shinydashboard)
  library(DT)
  library(dplyr)
  library(ggplot2)
  library(readr)
  library(readxl)
  library(stringr)
  library(scales)
  library(bslib)
  library(tidyr)
  library(leaflet)
  library(sf)
  library(geodata)
  library(plotly)
})

# Setting file
DATA_FILE <- readxl::read_excel("Data DBD Epidem Kosongan.xlsx")
DATA_TREN_FILE <- readxl::read_excel("Jumlah Kasus DBD 2016-2024.xlsx")

# Helper functions
safe_num <- function(x) {
  if (is.numeric(x)) return(as.numeric(x))
  x2 <- as.character(x)
  x2 <- str_replace_all(x2, "\\s+", "")
  x2 <- str_replace_all(x2, "\\.", "")
  x2 <- str_replace_all(x2, ",", ".")
  x2 <- str_replace_all(x2, "[^0-9eE+\\-\\.]", "")
  suppressWarnings(as.numeric(x2))
}

find_column <- function(cols, keywords) {
  cols_l <- tolower(cols)
  for (kw in keywords) {
    hits <- which(str_detect(cols_l, fixed(tolower(kw))))
    if (length(hits) > 0) return(cols[hits[1]])
  }
  for (kw in keywords) {
    hits <- which(str_detect(cols_l, kw))
    if (length(hits) > 0) return(cols[hits[1]])
  }
  NA_character_
}

keywords_map <- list(
  wilayah = c("wilayah","kabupaten","kota","region","nama","area","district"),
  cases   = c("jumlah_dbd","jumlah dbd","dbd","kasus","cases","jumlah_kasus","jumlah kasus"),
  pop     = c("jumlah_penduduk","penduduk","populasi","population","jumlahpenduduk","jumlah penduduk"),
  death   = c("kasus_meninggal","meninggal","death","mati","kasus meninggal"),
  banjir  = c("banjir","kejadian_banjir","jumlah_banjir","kejadian banjir","jumlah banjir"),
  sanit   = c("sanit","sanitasi","akses_sanitasi","persen_sanitasi","akses sanitasi","persen sanitasi"),
  rumah   = c("rumah","layak","rumah_layak","persen_rumah","rumah_layak_huni","persen_rumah_layak_huni","rumah layak","persen rumah"),
  tahun   = c("tahun","year","periode")
)

load_and_process_data <- function(filepath) {
  ext <- tolower(tools::file_ext(filepath))
  df <- tryCatch({
    if (ext %in% c("xls","xlsx")) {
      read_excel(filepath)
    } else {
      read_csv(filepath, show_col_types = FALSE)
    }
  }, error = function(e) {
    stop(paste("Gagal membaca file:", e$message))
  })
  
  names(df) <- str_trim(names(df))
  names(df) <- str_replace_all(names(df), "\\s+", " ")
  
  cols <- names(df)
  
  col_wil <- find_column(cols, keywords_map$wilayah)
  col_cases <- find_column(cols, keywords_map$cases)
  col_pop <- find_column(cols, keywords_map$pop)
  col_death <- find_column(cols, keywords_map$death)
  col_banjir <- find_column(cols, keywords_map$banjir)
  col_sanit <- find_column(cols, keywords_map$sanit)
  col_rumah <- find_column(cols, keywords_map$rumah)
  col_tahun <- find_column(cols, keywords_map$tahun)
  
  df2 <- df %>% mutate(across(everything(), ~ if(is.character(.)) str_trim(.) else .))
  
  df2$Wilayah <- if (!is.na(col_wil)) as.character(df2[[col_wil]]) else paste0("R", seq_len(nrow(df2)))
  df2$Jumlah_DBD <- if (!is.na(col_cases)) safe_num(df2[[col_cases]]) else NA_real_
  df2$Jumlah_Penduduk <- if (!is.na(col_pop)) safe_num(df2[[col_pop]]) else NA_real_
  df2$Kasus_Meninggal <- if (!is.na(col_death)) safe_num(df2[[col_death]]) else NA_real_
  df2$Jumlah_Kejadian_Banjir <- if (!is.na(col_banjir)) safe_num(df2[[col_banjir]]) else NA_real_
  df2$Persen_Akses_Sanitasi <- if (!is.na(col_sanit)) safe_num(df2[[col_sanit]]) else NA_real_
  df2$Persen_Rumah_Layak_Huni <- if (!is.na(col_rumah)) safe_num(df2[[col_rumah]]) else NA_real_
  df2$Tahun <- if (!is.na(col_tahun)) as.character(df2[[col_tahun]]) else "2024"
  
  std_cols <- c("Wilayah", "Tahun", "Jumlah_DBD", "Jumlah_Penduduk", "Kasus_Meninggal",
                "Jumlah_Kejadian_Banjir", "Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni")
  df2 <- df2[, std_cols]
  
  df2 <- df2 %>%
    mutate(
      Prevalensi_pct = ifelse(!is.na(Jumlah_DBD) & !is.na(Jumlah_Penduduk) & Jumlah_Penduduk>0,
                              round((Jumlah_DBD / Jumlah_Penduduk) * 100, 4), NA_real_),
      CaseFatality_pct = ifelse(!is.na(Kasus_Meninggal) & !is.na(Jumlah_DBD) & Jumlah_DBD>0,
                                round((Kasus_Meninggal / Jumlah_DBD) * 100, 4), NA_real_)
    )
  
  return(df2)
}

# AUTO-LOAD data
data_tren <- tryCatch({
  if(file.exists(DATA_TREN_FILE)) {
    ext <- tolower(tools::file_ext(DATA_TREN_FILE))
    df <- if (ext %in% c("xls", "xlsx")) {
      read_excel(DATA_TREN_FILE)
    } else {
      read_csv(DATA_TREN_FILE, show_col_types = FALSE)
    }
    names(df)[1] <- "Wilayah"
    df$Wilayah <- str_trim(df$Wilayah)
    year_cols <- names(df)[names(df) != "Wilayah"]
    for(col in year_cols) {
      df[[col]] <- safe_num(df[[col]])
    }
    df
  } else {
    NULL
  }
}, error = function(e) {
  NULL
})

initial_data <- tryCatch({
  if(file.exists(DATA_FILE)) {
    load_and_process_data(DATA_FILE)
  } else {
    NULL
  }
}, error = function(e) {
  NULL
})

# ----------------------------
# UI
# ----------------------------
theme_bs <- bs_theme(
  bootswatch = "minty",
  bg = "#FAFAFA",
  fg = "#0f3d3e",
  primary = "#2C7873"
)

ui <- dashboardPage(
  skin = "black",
  dashboardHeader(title = span("Dashboard Analisis DBD Jawa Barat", style = "font-weight:700;")),
  
  dashboardSidebar(
    width = 300,
    sidebarMenu(
      id = "tabs",
      menuItem("Data", tabName = "data", icon = icon("table")),
      menuItem("Statistika Deskriptif", tabName = "statdes", icon = icon("chart-bar")),
      menuItem("Ukuran Frekuensi", tabName = "freq", icon = icon("percent")),
      menuItem("Interpretasi Epidemiologi", tabName = "interp", icon = icon("stethoscope")),
      menuItem("Peta Visualisasi", tabName = "peta", icon = icon("globe")),
      menuItem("Tren Waktu", tabName = "tren", icon = icon("line-chart"))
    )
  ),
  
  dashboardBody(
    theme = theme_bs,
    tags$head(tags$style(HTML("
      body, .content-wrapper, .right-side { background-color: #FAFAFA; }
      .box { border-radius: 10px; border: 1px solid #ECECEC; box-shadow: 0 2px 6px rgba(0,0,0,0.04); }
      .box .box-title { color: #115e59; font-weight: 700; }
      .value-box { border-radius: 8px; }
    "))),
    
    tabItems(
      # DATA
      tabItem(tabName = "data",
              fluidRow(
                valueBoxOutput("vb_total_wilayah", width = 3),
                valueBoxOutput("vb_total_penduduk", width = 3),
                valueBoxOutput("vb_total_kasus", width = 3),
                valueBoxOutput("vb_total_meninggal", width = 3)
              ),
              fluidRow(
                box(title = "Data Lengkap (standarisasi)", status = "info", solidHeader = TRUE, width = 12,
                    DTOutput("table_all")
                )
              )
      ),
      
      # STATISTIKA DESKRIPTIF
      tabItem(tabName = "statdes",
              fluidRow(
                box(title = "Pilih Variabel dan Tahun", status = "primary", solidHeader = TRUE, width = 12,
                    fluidRow(
                      column(6, uiOutput("select_var_ui")),
                      column(6, uiOutput("year_filter_statdes"))
                    )
                )
              ),
              fluidRow(
                box(title = "Histogram", status = "success", solidHeader = TRUE, width = 6,
                    plotOutput("histogram_plot", height = "400px")
                ),
                box(title = "Boxplot (Klik untuk info outlier)", status = "info", solidHeader = TRUE, width = 6,
                    plotlyOutput("boxplot_plot", height = "400px")
                )
              ),
              fluidRow(
                box(title = "Info Data Terpilih", status = "warning", solidHeader = TRUE, width = 12,
                    htmlOutput("clicked_info")
                )
              ),
              fluidRow(
                box(title = "Statistika Deskriptif (variabel = baris)", status = "primary", solidHeader = TRUE, width = 12,
                    DTOutput("statdesc_tbl")
                )
              )
      ),
      
      # UKURAN FREKUENSI
      tabItem(tabName = "freq",
              fluidRow(
                box(title = "Filter Tahun", status = "primary", solidHeader = TRUE, width = 12,
                    uiOutput("year_filter_freq")
                )
              ),
              fluidRow(
                box(title = "Ringkasan Ukuran Frekuensi (agregat)", status = "primary", solidHeader = TRUE, width = 12,
                    htmlOutput("freq_summary")
                )
              ),
              fluidRow(
                box(title = "Top 10 Kabupaten/Kota - Prevalensi Tertinggi", status = "success", solidHeader = TRUE, width = 6,
                    plotlyOutput("top10_prevalensi_plot", height = "450px")
                ),
                box(title = "Top 10 Kabupaten/Kota - CFR Tertinggi", status = "danger", solidHeader = TRUE, width = 6,
                    plotlyOutput("top10_cfr_plot", height = "450px")
                )
              ),
              fluidRow(
                box(title = "Tabel Ukuran Frekuensi per Wilayah", status = "info", solidHeader = TRUE, width = 12,
                    DTOutput("freq_table")
                )
              )
      ),
      
      # INTERPRETASI
      tabItem(tabName = "interp",
              fluidRow(
                box(title = "Ringkasan Ukuran Epidemiologi", status = "primary", solidHeader = TRUE, width = 12,
                    htmlOutput("epi_summary")
                )
              ),
              fluidRow(
                box(title = "Grafik Perbandingan Ukuran Frekuensi Antar Wilayah", status = "info", solidHeader = TRUE, width = 12,
                    selectInput("epi_var_plot", "Pilih Ukuran:",
                                choices = c("Jumlah Kasus DBD" = "Jumlah_DBD",
                                            "Prevalensi (%)" = "Prevalensi_pct",
                                            "Case Fatality Rate (%)" = "CaseFatality_pct"),
                                selected = "Jumlah_DBD"),
                    plotOutput("epi_comparison_plot", height = "500px")
                )
              ),
              fluidRow(
                box(title = "Interpretasi Epidemiologi DBD Jawa Barat", status = "warning", solidHeader = TRUE, width = 12,
                    htmlOutput("epi_interpretation")
                )
              )
      ),
      
      # PETA
      tabItem(tabName = "peta",
              fluidRow(
                box(
                  title = "Peta Distribusi Variabel",
                  width = 4,
                  status = "primary",
                  solidHeader = TRUE,
                  helpText("Pilih variabel untuk divisualisasikan pada peta Jawa Barat:"),
                  uiOutput("map_var_ui"),
                  uiOutput("map_year_ui"),
                  selectInput("color_low", "Warna rendah:",
                              choices = c("yellow", "lightgreen", "lightblue", "white"),
                              selected = "yellow"),
                  selectInput("color_high", "Warna tinggi:",
                              choices = c("darkgreen", "red", "blue", "purple"),
                              selected = "red"),
                  actionButton("btn_load_map", "Muat Peta", icon = icon("map"), class = "btn btn-success"),
                  tags$hr(),
                  htmlOutput("map_info")
                ),
                box(
                  title = "Peta Distribusi Jawa Barat",
                  width = 8,
                  status = "info",
                  solidHeader = TRUE,
                  leafletOutput("map_leaflet", height = "600px")
                )
              ),
              fluidRow(
                box(
                  title = "Interpretasi Pola Spasial",
                  width = 12,
                  status = "warning",
                  solidHeader = TRUE,
                  htmlOutput("spatial_interpretation")
                )
              )
      ),
      
      # TREN WAKTU
      tabItem(tabName = "tren",
              fluidRow(
                box(
                  title = "Analisis Tren Waktu Kasus DBD",
                  width = 12,
                  status = "primary",
                  solidHeader = TRUE,
                  htmlOutput("tren_info")
                )
              ),
              fluidRow(
                box(
                  title = "Tren Total Kasus DBD Provinsi Jawa Barat",
                  width = 12,
                  status = "info",
                  solidHeader = TRUE,
                  plotlyOutput("tren_provinsi_plot", height = "400px")
                )
              ),
              fluidRow(
                box(
                  title = "Pilih Wilayah",
                  width = 4,
                  status = "info",
                  solidHeader = TRUE,
                  uiOutput("tren_wilayah_ui"),
                  checkboxInput("tren_show_avg", "Tampilkan rata-rata provinsi", value = TRUE),
                  checkboxInput("tren_show_trend", "Tampilkan garis tren", value = TRUE)
                ),
                box(
                  title = "Grafik Tren Kasus DBD per Wilayah (Klik titik untuk detail)",
                  width = 8,
                  status = "success",
                  solidHeader = TRUE,
                  plotlyOutput("tren_plot_interactive", height = "450px")
                )
              ),
              fluidRow(
                box(
                  title = "Detail Titik Terpilih",
                  width = 12,
                  status = "warning",
                  solidHeader = TRUE,
                  htmlOutput("tren_point_detail")
                )
              ),
              fluidRow(
                box(
                  title = "Tabel Data Tren",
                  width = 12,
                  status = "info",
                  solidHeader = TRUE,
                  DTOutput("tren_table")
                )
              ),
              fluidRow(
                box(
                  title = "Interpretasi Tren",
                  width = 12,
                  status = "warning",
                  solidHeader = TRUE,
                  htmlOutput("tren_interpretation")
                )
              )
      )
    )
  )
)

# ----------------------------
# SERVER
# ----------------------------
server <- function(input, output, session) {
  
  std_df <- reactiveVal(initial_data)
  jabar_shp <- reactiveVal(NULL)
  tren_df <- reactiveVal(data_tren)
  clicked_point <- reactiveVal(NULL)
  
  # Value boxes
  output$vb_total_wilayah <- renderValueBox({
    df <- std_df()
    if (is.null(df)) return(valueBox("—", "Jumlah Wilayah", icon = icon("map-marker-alt"), color = "olive"))
    valueBox(length(unique(df$Wilayah)), "Jumlah Wilayah", icon = icon("map-marker-alt"), color = "olive")
  })
  
  output$vb_total_penduduk <- renderValueBox({
    df <- std_df()
    if (is.null(df)) return(valueBox("—", "Total Penduduk", icon = icon("users"), color = "teal"))
    valueBox(comma(sum(df$Jumlah_Penduduk, na.rm = TRUE)), "Total Penduduk", icon = icon("users"), color = "teal")
  })
  
  output$vb_total_kasus <- renderValueBox({
    df <- std_df()
    if (is.null(df)) return(valueBox("—", "Total Kasus", icon = icon("virus"), color = "maroon"))
    valueBox(comma(sum(df$Jumlah_DBD, na.rm = TRUE)), "Total Kasus DBD", icon = icon("virus"), color = "maroon")
  })
  
  output$vb_total_meninggal <- renderValueBox({
    df <- std_df()
    if (is.null(df)) return(valueBox("—", "Total Meninggal", icon = icon("heartbeat"), color = "red"))
    valueBox(comma(sum(df$Kasus_Meninggal, na.rm = TRUE)), "Total Meninggal", icon = icon("heartbeat"), color = "red")
  })
  
  # Data table
  output$table_all <- renderDT({
    df <- std_df()
    if (is.null(df)) return(datatable(data.frame(Message = "Data tidak tersedia"), options = list(dom = 't')))
    
    df_display <- df %>% select(-Prevalensi_pct, -CaseFatality_pct)
    
    datatable(df_display, options = list(pageLength = 10, scrollX = TRUE))
  })
  
  # Variable selector UNTUK STATDES
  output$select_var_ui <- renderUI({
    df <- std_df()
    df_tren <- tren_df()
    if (is.null(df)) return(tags$div("Memuat data..."))
    
    # Ambil variabel numerik dari data utama
    num_vars <- names(df %>% select(where(is.numeric)))
    freq_cols <- c("Prevalensi_pct", "CaseFatality_pct")
    num_vars <- setdiff(num_vars, freq_cols)
    
    var_display_names <- sapply(num_vars, function(x) {
      tools::toTitleCase(gsub("_", " ", x))
    })
    
    names(num_vars) <- var_display_names
    
    selectInput("var_to_plot", "Pilih Variabel:", choices = num_vars, selected = num_vars[1])
  })
  
  # Filter tahun UNTUK STATDES (dinamis berdasarkan variabel)
  output$year_filter_statdes <- renderUI({
    df <- std_df()
    df_tren <- tren_df()
    req(input$var_to_plot)
    
    # Jika variabel adalah Jumlah_DBD dan ada data tren
    if(input$var_to_plot == "Jumlah_DBD" && !is.null(df_tren)) {
      year_cols <- names(df_tren)[names(df_tren) != "Wilayah"]
      selectInput("year_statdes", "Pilih Tahun:", 
                  choices = c("Semua Tahun", year_cols), 
                  selected = "Semua Tahun")
    } else {
      # Variabel lain hanya menampilkan tahun dari data utama
      if ("Tahun" %in% names(df)) {
        years <- sort(unique(df$Tahun))
        selectInput("year_statdes", "Pilih Tahun:", choices = years, selected = years[1])
      } else {
        tags$p("Tahun: 2024")
      }
    }
  })
  
  # Filter tahun untuk freq
  output$year_filter_freq <- renderUI({
    df <- std_df()
    if (is.null(df)) return(NULL)
    if ("Tahun" %in% names(df)) {
      years <- sort(unique(df$Tahun))
      selectInput("year_freq", "Pilih Tahun:", choices = c("Semua Tahun", years), selected = "Semua Tahun")
    } else {
      tags$p("Data tidak memiliki kolom tahun")
    }
  })
  
  # Data terfilter untuk statdes
  filtered_data_statdes <- reactive({
    df <- std_df()
    df_tren <- tren_df()
    req(df, input$var_to_plot)
    
    # Jika variabel adalah Jumlah_DBD, ambil dari data tren
    if(input$var_to_plot == "Jumlah_DBD" && !is.null(df_tren)) {
      if(!is.null(input$year_statdes)) {
        if(input$year_statdes == "Semua Tahun") {
          # Reshape ke format panjang
          df_long <- df_tren %>%
            pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_DBD") %>%
            mutate(Tahun = as.character(Tahun))
          return(df_long)
        } else {
          # Tahun tertentu
          selected_year <- input$year_statdes
          if(selected_year %in% names(df_tren)) {
            df_result <- df_tren %>%
              select(Wilayah, all_of(selected_year)) %>%
              rename(Jumlah_DBD = all_of(selected_year)) %>%
              mutate(Tahun = selected_year)
            return(df_result)
          }
        }
      }
    }
    
    # Filter normal untuk variabel lain
    if ("Tahun" %in% names(df) && !is.null(input$year_statdes)) {
      df <- df %>% filter(Tahun == input$year_statdes)
    }
    df
  })
  
  filtered_data_freq <- reactive({
    df <- std_df()
    req(df)
    if ("Tahun" %in% names(df) && !is.null(input$year_freq) && input$year_freq != "Semua Tahun") {
      df <- df %>% filter(Tahun == input$year_freq)
    }
    df
  })
  
  # Histogram
  output$histogram_plot <- renderPlot({
    df <- filtered_data_statdes()
    req(df, input$var_to_plot)
    var <- input$var_to_plot
    
    if(nrow(df) == 0 || all(is.na(df[[var]]))) {
      plot.new()
      text(0.5, 0.5, "Data tidak tersedia")
      return()
    }
    
    df_clean <- df %>% filter(!is.na(.data[[var]]))
    
    if(nrow(df_clean) < 2) {
      plot.new()
      text(0.5, 0.5, "Data tidak cukup untuk membuat histogram")
      return()
    }
    
    # Jika Jumlah_DBD dan ada data dari beberapa tahun
    if(var == "Jumlah_DBD" && "Tahun" %in% names(df) && length(unique(df$Tahun)) > 1) {
      ggplot(df_clean, aes(x = Jumlah_DBD, fill = Tahun)) +
        geom_histogram(bins = 30, color = "white", alpha = 0.7, position = "identity") +
        theme_minimal(base_size = 13) +
        labs(
          title = "Histogram Jumlah Kasus DBD",
          x = "Jumlah Kasus DBD",
          y = "Frekuensi",
          fill = "Tahun"
        ) +
        theme(
          plot.title = element_text(face = "bold", size = 14),
          legend.position = "right"
        ) +
        scale_fill_viridis_d()
    } else {
      ggplot(df_clean, aes(x = .data[[var]])) +
        geom_histogram(bins = 30, fill = "#8fd3c7", color = "white", alpha = 0.7) +
        theme_minimal(base_size = 13) +
        labs(
          title = paste("Histogram -", tools::toTitleCase(gsub("_", " ", var))),
          x = tools::toTitleCase(gsub("_", " ", var)),
          y = "Frekuensi"
        ) +
        theme(
          plot.title = element_text(face = "bold", size = 14)
        )
    }
  }, res = 96)
  
  # Boxplot
  output$boxplot_plot <- renderPlotly({
    df <- filtered_data_statdes()
    req(df, input$var_to_plot)
    var <- input$var_to_plot
    
    # Jika Jumlah_DBD dan ada beberapa tahun, buat boxplot per tahun
    if(var == "Jumlah_DBD" && "Tahun" %in% names(df) && length(unique(df$Tahun)) > 1) {
      plot_ly(df, x = ~Tahun, y = ~Jumlah_DBD, type = "box",
              color = ~Tahun,
              boxpoints = "outliers",
              text = ~paste("Wilayah:", Wilayah, "<br>Tahun:", Tahun, "<br>Nilai:", comma(Jumlah_DBD)),
              hoverinfo = "text") %>%
        layout(title = "Boxplot Jumlah Kasus DBD per Tahun",
               xaxis = list(title = "Tahun"),
               yaxis = list(title = "Jumlah Kasus DBD"),
               showlegend = FALSE)
    } else {
      plot_ly(df, y = ~get(var), type = "box",
              marker = list(color = "#2C7873"),
              line = list(color = "#0f3d3e"),
              name = var,
              boxpoints = "outliers",
              text = ~paste("Wilayah:", Wilayah, "<br>Nilai:", round(get(var), 2)),
              hoverinfo = "text") %>%
        layout(title = paste("Boxplot -", tools::toTitleCase(gsub("_", " ", var))),
               yaxis = list(title = tools::toTitleCase(gsub("_", " ", var))))
    }
  })
  
  output$clicked_info <- renderUI({
    df <- filtered_data_statdes()
    req(df, input$var_to_plot)
    
    event <- event_data("plotly_click")
    if (is.null(event)) {
      return(HTML("<p><i>Klik pada titik outlier di boxplot untuk melihat detail...</i></p>"))
    }
    
    var <- input$var_to_plot
    clicked_value <- event$y
    
    df$diff <- abs(df[[var]] - clicked_value)
    closest <- df %>% filter(!is.na(diff)) %>% arrange(diff) %>% slice(1)
    
    if (nrow(closest) > 0) {
      HTML(paste0(
        "<div style='font-size:14px; padding:10px; background:#f0f0f0; border-radius:5px;'>",
        "<h4 style='margin-top:0;'>Data Point yang Dipilih:</h4>",
        "<table style='width:100%;'>",
        "<tr><td><b>Wilayah:</b></td><td>", closest$Wilayah, "</td></tr>",
        "<tr><td><b>", tools::toTitleCase(gsub("_", " ", var)), ":</b></td><td>", comma(round(closest[[var]], 2)), "</td></tr>",
        if("Tahun" %in% names(closest)) paste0("<tr><td><b>Tahun:</b></td><td>", closest$Tahun, "</td></tr>") else "",
        "</table>",
        "</div>"
      ))
    } else {
      HTML("<p>Data tidak ditemukan</p>")
    }
  })
  
  # Statistika deskriptif
  output$statdesc_tbl <- renderDT({
    df <- filtered_data_statdes()
    if (is.null(df)) return(datatable(data.frame(Message="Memuat data..."), options = list(dom='t')))
    
    num_df <- df %>% select(where(is.numeric))
    freq_cols <- c("Prevalensi_pct", "CaseFatality_pct")
    num_df <- num_df[, !(names(num_df) %in% freq_cols), drop = FALSE]
    
    if (ncol(num_df) == 0) return(datatable(data.frame(Message="Tidak ada variabel numerik"), options = list(dom='t')))
    
    get_mode <- function(x) {
      x2 <- x[!is.na(x)]
      if (length(x2) == 0) return(NA_real_)
      ux <- unique(x2)
      tab <- tabulate(match(x2, ux))
      ux[which.max(tab)]
    }
    
    skewness <- function(x) {
      x <- x[!is.na(x)]
      n <- length(x)
      if (n < 3) return(NA_real_)
      m <- mean(x)
      s <- sd(x)
      if (s == 0) return(0)
      sum((x - m)^3) / (n * s^3)
    }
    
    kurtosis <- function(x) {
      x <- x[!is.na(x)]
      n <- length(x)
      if (n < 4) return(NA_real_)
      m <- mean(x)
      s <- sd(x)
      if (s == 0) return(0)
      sum((x - m)^4) / (n * s^4) - 3
    }
    
    stat <- tibble(
      Variabel = names(num_df),
      N = sapply(num_df, function(x) sum(!is.na(x))),
      Mean = sapply(num_df, function(x) round(mean(x, na.rm = TRUE), 3)),
      Median = sapply(num_df, function(x) round(median(x, na.rm = TRUE), 3)),
      Mode = sapply(num_df, function(x) {
        m <- get_mode(x)
        if (is.na(m)) NA else round(m, 3)
      }),
      SD = sapply(num_df, function(x) round(sd(x, na.rm = TRUE), 3)),
      Variance = sapply(num_df, function(x) round(var(x, na.rm = TRUE), 3)),
      Min = sapply(num_df, function(x) if (all(is.na(x))) NA else round(min(x, na.rm = TRUE), 3)),
      Q1 = sapply(num_df, function(x) if (all(is.na(x))) NA else round(quantile(x, probs = 0.25, na.rm = TRUE, type = 7), 3)),
      Q3 = sapply(num_df, function(x) if (all(is.na(x))) NA else round(quantile(x, probs = 0.75, na.rm = TRUE, type = 7), 3)),
      Max = sapply(num_df, function(x) if (all(is.na(x))) NA else round(max(x, na.rm = TRUE), 3)),
      IQR = sapply(num_df, function(x) {
        if (all(is.na(x))) return(NA_real_)
        q <- quantile(x, probs = c(0.25, 0.75), na.rm = TRUE, type = 7)
        round(as.numeric(q[2] - q[1]), 3)
      }),
      Range = sapply(num_df, function(x) {
        if (all(is.na(x))) return(NA_real_)
        round(max(x, na.rm = TRUE) - min(x, na.rm = TRUE), 3)
      }),
      CV = sapply(num_df, function(x) {
        m <- mean(x, na.rm = TRUE)
        s <- sd(x, na.rm = TRUE)
        if (is.na(m) || m == 0) return(NA_real_)
        round(s / m, 3)
      }),
      Skewness = sapply(num_df, function(x) round(skewness(x), 3)),
      Kurtosis = sapply(num_df, function(x) round(kurtosis(x), 3)),
      NA_Count = sapply(num_df, function(x) sum(is.na(x))),
      Pct_NA = sapply(num_df, function(x) round(mean(is.na(x)) * 100, 2))
    )
    
    stat$Variabel <- sapply(stat$Variabel, function(nm) {
      tools::toTitleCase(gsub("_", " ", nm))
    })
    
    datatable(stat, options = list(pageLength = 15, scrollX = TRUE))
  })
  
  # Frequencies
  output$freq_table <- renderDT({
    df <- filtered_data_freq()
    if (is.null(df)) return(datatable(data.frame(Message = "Memuat data..."), options = list(dom = 't')))
    fd <- df %>%
      select(Wilayah, Jumlah_DBD, Jumlah_Penduduk, Kasus_Meninggal,
             Prevalensi_pct, CaseFatality_pct) %>%
      arrange(desc(Prevalensi_pct))
    datatable(fd, options = list(pageLength = 15, scrollX = TRUE))
  })
  
  output$freq_summary <- renderUI({
    df <- filtered_data_freq()
    if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
    
    total_kasus <- sum(df$Jumlah_DBD, na.rm = TRUE)
    total_penduduk <- sum(df$Jumlah_Penduduk, na.rm = TRUE)
    total_meninggal <- sum(df$Kasus_Meninggal, na.rm = TRUE)
    
    prevalensi_agregat <- round((total_kasus / total_penduduk) * 100, 4)
    cfr_agregat <- round((total_meninggal / total_kasus) * 100, 2)
    
    if (all(is.na(df$Prevalensi_pct))) {
      txt_prev <- "<i>Tidak ada nilai prevalensi tersedia.</i>"
    } else {
      top <- df %>% filter(!is.na(Prevalensi_pct)) %>% arrange(desc(Prevalensi_pct)) %>% slice(1)
      txt_prev <- paste0("<b>Prevalensi tertinggi</b>: ", round(top$Prevalensi_pct,4),
                         "% — <b>", top$Wilayah, "</b>")
    }
    
    HTML(paste0(
      "<div style='font-size:15px; line-height:2;'>",
      "<table style='width:100%;'>",
      "<tr><td><b>Prevalensi Agregat:</b></td><td>", prevalensi_agregat, "%</td></tr>",
      "<tr><td><b>CFR Agregat:</b></td><td>", cfr_agregat, "%</td></tr>",
      "</table>",
      "<p style='margin-top:15px;'>", txt_prev, "</p>",
      "</div>"
    ))
  })
  
  # Plot top 10 prevalensi
  output$top10_prevalensi_plot <- renderPlotly({
    df <- filtered_data_freq()
    req(df)
    
    top10 <- df %>% 
      filter(!is.na(Prevalensi_pct)) %>% 
      arrange(desc(Prevalensi_pct)) %>% 
      slice_head(n = 10)
    
    plot_ly(top10, x = ~Prevalensi_pct, y = ~reorder(Wilayah, Prevalensi_pct), 
            type = "bar", orientation = "h",
            marker = list(color = "#2C7873"),
            text = ~paste("Prevalensi:", round(Prevalensi_pct, 4), "%"),
            hoverinfo = "text+y") %>%
      layout(title = "Top 10 Prevalensi Tertinggi",
             xaxis = list(title = "Prevalensi (%)"),
             yaxis = list(title = ""),
             margin = list(l = 150))
  })
  
  # Plot top 10 CFR
  output$top10_cfr_plot <- renderPlotly({
    df <- filtered_data_freq()
    req(df)
    
    top10 <- df %>% 
      filter(!is.na(CaseFatality_pct)) %>% 
      arrange(desc(CaseFatality_pct)) %>% 
      slice_head(n = 10)
    
    plot_ly(top10, x = ~CaseFatality_pct, y = ~reorder(Wilayah, CaseFatality_pct), 
            type = "bar", orientation = "h",
            marker = list(color = "#d9534f"),
            text = ~paste("CFR:", round(CaseFatality_pct, 2), "%"),
            hoverinfo = "text+y") %>%
      layout(title = "Top 10 CFR Tertinggi",
             xaxis = list(title = "Case Fatality Rate (%)"),
             yaxis = list(title = ""),
             margin = list(l = 150))
  })
  
  # INTERPRETASI EPIDEMIOLOGI
  output$epi_summary <- renderUI({
    df <- std_df()
    if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
    
    total_kasus <- sum(df$Jumlah_DBD, na.rm = TRUE)
    total_penduduk <- sum(df$Jumlah_Penduduk, na.rm = TRUE)
    total_meninggal <- sum(df$Kasus_Meninggal, na.rm = TRUE)
    
    prevalensi_agregat <- (total_kasus / total_penduduk) * 100
    cfr_agregat <- (total_meninggal / total_kasus) * 100
    
    top_kasus <- df %>% filter(!is.na(Jumlah_DBD)) %>% arrange(desc(Jumlah_DBD)) %>% slice(1)
    top_prev <- df %>% filter(!is.na(Prevalensi_pct)) %>% arrange(desc(Prevalensi_pct)) %>% slice(1)
    top_cfr <- df %>% filter(!is.na(CaseFatality_pct)) %>% arrange(desc(CaseFatality_pct)) %>% slice(1)
    
    HTML(paste0(
      "<div style='font-size:15px; line-height:2;'>",
      "<h4>Ukuran Frekuensi Penyakit DBD - Jawa Barat</h4>",
      "<table style='width:100%; border-collapse: collapse;'>",
      "<tr style='background:#f0f0f0;'><th style='padding:10px; text-align:left; border:1px solid #ddd;'>Ukuran</th>",
      "<th style='padding:10px; text-align:left; border:1px solid #ddd;'>Wilayah Tertinggi</th></tr>",
      
      "<tr><td style='padding:8px; border:1px solid #ddd;'><b>Jumlah Kasus DBD</b></td>",
      "<td style='padding:8px; border:1px solid #ddd;'>", top_kasus$Wilayah, " (", comma(top_kasus$Jumlah_DBD), " kasus)</td></tr>",
      
      "<tr style='background:#f9f9f9;'><td style='padding:8px; border:1px solid #ddd;'><b>Prevalensi</b><br><small>(persen)</small></td>",
      "<td style='padding:8px; border:1px solid #ddd;'>", top_prev$Wilayah, " (", round(top_prev$Prevalensi_pct, 4), "%)</td></tr>",
      
      "<tr><td style='padding:8px; border:1px solid #ddd;'><b>Case Fatality Rate</b><br><small>(persen)</small></td>",
      "<td style='padding:8px; border:1px solid #ddd;'>", top_cfr$Wilayah, " (", round(top_cfr$CaseFatality_pct, 2), "%)</td></tr>",
      
      "</table>",
      "</div>"
    ))
  })
  
  output$epi_comparison_plot <- renderPlot({
    df <- std_df()
    req(df, input$epi_var_plot)
    
    var_name <- input$epi_var_plot
    plot_data <- df %>% 
      filter(!is.na(.data[[var_name]])) %>%
      arrange(desc(.data[[var_name]])) %>%
      slice_head(n = 15)
    
    var_labels <- c(
      "Jumlah_DBD" = "Jumlah Kasus DBD",
      "Prevalensi_pct" = "Prevalensi (%)",
      "CaseFatality_pct" = "Case Fatality Rate (%)"
    )
    
    if(var_name == "Jumlah_DBD") {
      plot_data$label_text <- comma(plot_data[[var_name]])
    } else {
      plot_data$label_text <- as.character(round(plot_data[[var_name]], 2))
    }
    
    ggplot(plot_data, aes(x = reorder(Wilayah, .data[[var_name]]), y = .data[[var_name]])) +
      geom_col(fill = "#2C7873", alpha = 0.8) +
      geom_text(aes(label = label_text), hjust = -0.2, size = 3.5) +
      coord_flip() +
      theme_minimal(base_size = 13) +
      labs(
        title = paste("Top 15 Wilayah berdasarkan", var_labels[var_name]),
        x = "Wilayah",
        y = var_labels[var_name]
      ) +
      theme(
        plot.title = element_text(face = "bold", size = 15),
        panel.grid.major.y = element_blank()
      )
  }, res = 96)
  
  output$epi_interpretation <- renderUI({
    df <- std_df()
    if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
    
    total_kasus <- sum(df$Jumlah_DBD, na.rm = TRUE)
    total_penduduk <- sum(df$Jumlah_Penduduk, na.rm = TRUE)
    total_meninggal <- sum(df$Kasus_Meninggal, na.rm = TRUE)
    
    prevalensi_agregat <- (total_kasus / total_penduduk) * 100
    cfr_agregat <- (total_meninggal / total_kasus) * 100
    
    prevalensi_per100k <- round((total_kasus / total_penduduk) * 100000, 0)
    
    cfr_status <- if(cfr_agregat > 1) "TINGGI (>1%)" else if(cfr_agregat > 0.5) "SEDANG (0.5-1%)" else "RENDAH (<0.5%)"
    
    cfr_meaning <- if(cfr_agregat > 1) {
      paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong tinggi, menunjukkan bahwa dari setiap 100 orang yang terkena DBD, sekitar ", 
             round(cfr_agregat, 0), " orang meninggal dunia. Hal ini mengindikasikan perlunya peningkatan kualitas penanganan dan akses layanan kesehatan untuk penderita DBD.")
    } else if(cfr_agregat > 0.5) {
      paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong sedang, menunjukkan bahwa dari setiap 100 orang yang terkena DBD, sekitar ", 
             round(cfr_agregat, 1), " orang meninggal dunia. Perlu dilakukan pemantauan dan peningkatan kualitas layanan kesehatan.")
    } else {
      paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong rendah, menunjukkan bahwa tingkat kematian akibat DBD relatif terkendali. Namun tetap perlu dilakukan pencegahan dan penanganan yang optimal.")
    }
    
    HTML(paste0(
      "<div style='font-size:14px; line-height:1.9;'>",
      "<h4>Interpretasi Epidemiologi</h4>",
      "<p><b>Jumlah Kasus DBD:</b> ", comma(total_kasus), " kasus</p>",
      "<p><b>Prevalensi DBD:</b> ", round(prevalensi_agregat, 4), "%</p>",
      "<p><b>CFR:</b> ", round(cfr_agregat, 2), "% (", cfr_status, ")</p>",
      "<hr>",
      "<p style='text-align:justify;'><i><b>Interpretasi:</b> Angka prevalensi sebesar ", round(prevalensi_agregat, 4), 
      "% berarti dari setiap 100.000 penduduk, terdapat sekitar ", prevalensi_per100k, 
      " orang yang menderita penyakit DBD. Diperlukan upaya pengendalian yang intensif agar mengurangi ",
      "penyebaran penyakit DBD di wilayah Jawa Barat. Sedangkan ", cfr_meaning, "</i></p>",
      "</div>"
    ))
  })
  
  # PETA VISUALISASI
  output$map_var_ui <- renderUI({
    df <- std_df()
    df_tren <- tren_df()
    req(df)
    
    num_vars <- names(df)[sapply(df, is.numeric)]
    priority_vars <- c("Prevalensi_pct", "Jumlah_DBD", "Jumlah_Penduduk",
                       "CaseFatality_pct", "Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni")
    available_priority <- intersect(priority_vars, num_vars)
    other_vars <- setdiff(num_vars, priority_vars)
    ordered_vars <- c(available_priority, other_vars)
    
    if (length(ordered_vars) == 0) {
      return(tags$div("Tidak ada variabel numerik untuk divisualisasikan."))
    }
    selectInput("map_var", "Variabel:", choices = ordered_vars, selected = ordered_vars[1])
  })
  
  output$map_year_ui <- renderUI({
    df_tren <- tren_df()
    req(input$map_var)
    
    if(input$map_var == "Jumlah_DBD" && !is.null(df_tren)) {
      year_cols <- names(df_tren)[names(df_tren) != "Wilayah"]
      selectInput("map_year", "Pilih Tahun (untuk Jumlah DBD):", 
                  choices = year_cols, 
                  selected = year_cols[length(year_cols)])
    } else {
      NULL
    }
  })
  
  observeEvent(input$btn_load_map, {
    withProgress(message = 'Memuat peta Jawa Barat...', value = 0, {
      tryCatch({
        incProgress(0.3, detail = "Mengunduh data geografis...")
        
        indo <- gadm(country = "IDN", level = 2, path = tempdir())
        
        incProgress(0.5, detail = "Memfilter Jawa Barat...")
        jabar <- indo[indo$NAME_1 == "Jawa Barat" | indo$NAME_1 == "West Java", ]
        
        if (length(jabar) == 0) {
          showNotification("Data Jawa Barat tidak ditemukan!", type = "error")
          return()
        }
        
        incProgress(0.7, detail = "Memproses shapefile...")
        shp <- st_as_sf(jabar)
        shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "cimahi", "Kota Cimahi", shp$NAME_2)
        shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "depok", "Kota Depok", shp$NAME_2)
        shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "banjar", "Kota Banjar", shp$NAME_2)
        
        waduk_idx <- grep("waduk|cirata", tolower(shp$NAME_2))
        if(length(waduk_idx) > 0) {
          for(idx in waduk_idx) {
            waduk_geom <- shp[idx, ]
            centroid <- st_centroid(st_geometry(waduk_geom))
            coords <- st_coordinates(centroid)
            
            if(coords[2] > -6.7) {
              shp$NAME_2[idx] <- "Purwakarta"
            } else if(coords[1] > 107.5) {
              shp$NAME_2[idx] <- "Bandung Barat"
            } else {
              shp$NAME_2[idx] <- "Cianjur"
            }
          }
        }
        
        incProgress(1, detail = "Selesai!")
        jabar_shp(shp)
        showNotification("Peta berhasil dimuat!", type = "message")
      }, error = function(e) {
        showNotification(paste("Error memuat peta:", e$message), type = "error")
        jabar_shp(NULL)
      })
    })
  })
  
  output$map_leaflet <- renderLeaflet({
    req(input$map_var)
    df <- std_df()
    df_tren <- tren_df()
    shp <- jabar_shp()
    req(shp)
    
    if(input$map_var == "Jumlah_DBD" && !is.null(df_tren) && !is.null(input$map_year)) {
      selected_year <- input$map_year
      if(selected_year %in% names(df_tren)) {
        df <- df_tren %>%
          select(Wilayah, all_of(selected_year)) %>%
          rename(Jumlah_DBD = all_of(selected_year))
      }
    }
    
    req(df)
    
    df$Wilayah_Clean <- tolower(trimws(as.character(df$Wilayah)))
    df$Wilayah_Clean <- gsub("^kab\\.?\\s*", "", df$Wilayah_Clean)
    df$Wilayah_Clean <- gsub("^kabupaten\\s+", "", df$Wilayah_Clean)
    df$Wilayah_Clean <- gsub("\\s+", " ", df$Wilayah_Clean)
    df$Wilayah_Clean <- trimws(df$Wilayah_Clean)
    
    shp$Wilayah_Clean <- tolower(trimws(as.character(shp$NAME_2)))
    shp$Wilayah_Clean <- gsub("^kabupaten\\s+", "", shp$Wilayah_Clean)
    shp$Wilayah_Clean <- gsub("\\s+", " ", shp$Wilayah_Clean)
    shp$Wilayah_Clean <- trimws(shp$Wilayah_Clean)
    
    merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
    
    if (!(input$map_var %in% names(merged))) {
      return(leaflet() %>% addTiles() %>% setView(lng = 107.9, lat = -6.9, zoom = 9))
    }
    
    vals <- merged[[input$map_var]]
    valid_vals <- vals[!is.na(vals)]
    
    if (length(valid_vals) == 0) {
      return(leaflet() %>% addTiles() %>% setView(lng = 107.9, lat = -6.9, zoom = 9))
    }
    
    output$map_info <- renderUI({
      year_info <- if(input$map_var == "Jumlah_DBD" && !is.null(input$map_year)) {
        paste0("<li><strong>Tahun:</strong> ", input$map_year, "</li>")
      } else ""
      
      HTML(paste0(
        "<h5>Statistik Peta:</h5>",
        "<ul style='font-size:13px;'>",
        year_info,
        "<li><strong>Min:</strong> ", comma(round(min(valid_vals, na.rm=TRUE), 2)), "</li>",
        "<li><strong>Maks:</strong> ", comma(round(max(valid_vals, na.rm=TRUE), 2)), "</li>",
        "<li><strong>Rata-rata:</strong> ", comma(round(mean(valid_vals, na.rm=TRUE), 2)), "</li>",
        "</ul>"
      ))
    })
    
    pal <- colorNumeric(
      palette = colorRampPalette(c(input$color_low, input$color_high))(10),
      domain = merged[[input$map_var]],
      na.color = "#cccccc"
    )
    
    leaflet(merged) %>%
      addProviderTiles("CartoDB.Positron") %>%
      setView(lng = 107.9, lat = -6.9, zoom = 9) %>%
      addPolygons(
        fillColor = ~pal(get(input$map_var)),
        color = "grey40",
        weight = 1.5,
        opacity = 1,
        fillOpacity = 0.75,
        highlightOptions = highlightOptions(
          weight = 3,
          color = "#333",
          fillOpacity = 0.9,
          bringToFront = TRUE
        ),
        label = ~lapply(paste0(
          "<strong>", NAME_2, "</strong><br>",
          input$map_var, ": ",
          ifelse(is.na(get(input$map_var)),
                 "<span style='color:red;'>Tidak ada data</span>",
                 comma(round(get(input$map_var), 2)))
        ), htmltools::HTML)
      ) %>%
      addLegend(
        pal = pal,
        values = ~get(input$map_var),
        title = input$map_var,
        position = "bottomright",
        na.label = "Tidak ada data"
      )
  })
  
  output$spatial_interpretation <- renderUI({
    req(input$map_var)
    df <- std_df()
    shp <- jabar_shp()
    req(df, shp)
    
    df$Wilayah_Clean <- tolower(trimws(gsub("^(kab\\.?|kabupaten)\\s+", "", as.character(df$Wilayah))))
    df$Wilayah_Clean <- gsub("\\s+", " ", trimws(df$Wilayah_Clean))
    shp$Wilayah_Clean <- tolower(trimws(gsub("^kabupaten\\s+", "", as.character(shp$NAME_2))))
    shp$Wilayah_Clean <- gsub("\\s+", " ", trimws(shp$Wilayah_Clean))
    merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
    
    var_name <- input$map_var
    vals <- merged[[var_name]]
    valid_vals <- vals[!is.na(vals)]
    
    if (length(valid_vals) == 0) {
      return(HTML("<p>Tidak dapat memberikan interpretasi karena tidak ada data valid.</p>"))
    }
    
    q <- quantile(valid_vals, probs = c(0.25, 0.75), na.rm = TRUE)
    high_threshold <- q[2]
    low_threshold <- q[1]
    
    high_regions <- merged[!is.na(vals) & vals >= high_threshold, ]
    low_regions <- merged[!is.na(vals) & vals <= low_threshold, ]
    
    high_names <- if(nrow(high_regions) > 0) {
      paste(head(high_regions$NAME_2, 5), collapse = ", ")
    } else "Tidak ada"
    
    low_names <- if(nrow(low_regions) > 0) {
      paste(head(low_regions$NAME_2, 5), collapse = ", ")
    } else "Tidak ada"
    
    HTML(paste0(
      "<h4>Interpretasi Pola Spasial: ", tools::toTitleCase(gsub("_", " ", var_name)), "</h4>",
      "<p><strong>Wilayah TINGGI</strong> (Q3 = ", comma(round(high_threshold, 2)), "): ", high_names, "</p>",
      "<p><strong>Wilayah RENDAH</strong> (Q1 = ", comma(round(low_threshold, 2)), "): ", low_names, "</p>"
    ))
  })
  
  # TREN WAKTU
  output$tren_info <- renderUI({
    df <- tren_df()
    if (is.null(df)) {
      return(HTML("<p style='color:red;'>Data tren waktu belum tersedia.</p>"))
    }
    
    year_cols <- names(df)[names(df) != "Wilayah"]
    HTML(paste0(
      "<p style='font-size:14px;'>",
      "<strong>Data tren tersedia untuk:</strong><br>",
      "• Jumlah wilayah: ", nrow(df), "<br>",
      "• Periode: ", paste(year_cols, collapse = ", "),
      "</p>"
    ))
  })
  
  # Tren total provinsi dengan persentase perubahan
  output$tren_provinsi_plot <- renderPlotly({
    df <- tren_df()
    req(df)
    
    year_cols <- names(df)[names(df) != "Wilayah"]
    total_per_year <- data.frame(
      Tahun = year_cols,
      Total_Kasus = sapply(year_cols, function(y) sum(df[[y]], na.rm = TRUE))
    )
    total_per_year$Tahun <- as.numeric(total_per_year$Tahun)
    
    total_per_year$Pct_Change <- NA
    for(i in 2:nrow(total_per_year)) {
      if(total_per_year$Total_Kasus[i-1] > 0) {
        total_per_year$Pct_Change[i] <- ((total_per_year$Total_Kasus[i] - total_per_year$Total_Kasus[i-1]) / total_per_year$Total_Kasus[i-1]) * 100
      }
    }
    
    plot_ly(total_per_year, x = ~Tahun, y = ~Total_Kasus, type = "scatter", mode = "lines+markers",
            line = list(color = "#2C7873", width = 3),
            marker = list(size = 10, color = "#2C7873"),
            text = ~paste("<b>Tahun:", Tahun, "</b>",
                          "<br>Total Kasus:", comma(Total_Kasus),
                          "<br>Perubahan dari tahun lalu:", 
                          ifelse(is.na(Pct_Change), "N/A", 
                                 paste0(ifelse(Pct_Change > 0, "+", ""), 
                                        round(Pct_Change, 1), "%"))),
            hoverinfo = "text") %>%
      layout(
        title = "Tren Total Kasus DBD Provinsi Jawa Barat (2016-2024)",
        xaxis = list(title = "Tahun", dtick = 1),
        yaxis = list(title = "Total Kasus DBD"),
        hovermode = "closest"
      )
  })
  
  output$tren_wilayah_ui <- renderUI({
    df <- tren_df()
    if (is.null(df)) return(tags$div("Data tren belum dimuat"))
    
    wilayah_list <- sort(unique(df$Wilayah))
    selectInput("tren_wilayah", 
                "Pilih Wilayah (bisa pilih beberapa):",
                choices = wilayah_list,
                selected = wilayah_list[1:min(3, length(wilayah_list))],
                multiple = TRUE)
  })
  
  # Tren plot interaktif dengan persentase perubahan
  output$tren_plot_interactive <- renderPlotly({
    df <- tren_df()
    req(df, input$tren_wilayah)
    
    plot_data <- df %>%
      filter(Wilayah %in% input$tren_wilayah) %>%
      pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
      mutate(Tahun = as.numeric(Tahun)) %>%
      arrange(Wilayah, Tahun)
    
    # Hitung persentase perubahan per wilayah
    plot_data <- plot_data %>%
      group_by(Wilayah) %>%
      mutate(
        Pct_Change = ifelse(lag(Jumlah_Kasus) > 0,
                            ((Jumlah_Kasus - lag(Jumlah_Kasus)) / lag(Jumlah_Kasus)) * 100,
                            NA)
      ) %>%
      ungroup()
    
    if(input$tren_show_avg) {
      avg_data <- df %>%
        select(-Wilayah) %>%
        summarise(across(everything(), ~mean(., na.rm = TRUE))) %>%
        mutate(Wilayah = "RATA-RATA JABAR") %>%
        pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
        mutate(Tahun = as.numeric(Tahun)) %>%
        arrange(Tahun) %>%
        mutate(
          Pct_Change = ifelse(lag(Jumlah_Kasus) > 0,
                              ((Jumlah_Kasus - lag(Jumlah_Kasus)) / lag(Jumlah_Kasus)) * 100,
                              NA)
        )
      
      plot_data <- bind_rows(plot_data, avg_data)
    }
    
    p <- plot_ly()
    
    for(wil in unique(plot_data$Wilayah)) {
      wil_data <- plot_data %>% filter(Wilayah == wil)
      
      p <- p %>% add_trace(
        data = wil_data,
        x = ~Tahun,
        y = ~Jumlah_Kasus,
        type = "scatter",
        mode = "lines+markers",
        name = wil,
        text = ~paste("<b>", Wilayah, "</b>",
                      "<br>Tahun:", Tahun,
                      "<br>Jumlah Kasus:", comma(Jumlah_Kasus),
                      "<br>Perubahan dari tahun lalu:",
                      ifelse(is.na(Pct_Change), "N/A",
                             paste0(ifelse(Pct_Change > 0, "+", ""),
                                    round(Pct_Change, 1), "%"))),
        hoverinfo = "text",
        line = list(width = 2),
        marker = list(size = 8)
      )
    }
    
    p %>% layout(
      title = "Tren Kasus DBD Antar Tahun",
      xaxis = list(title = "Tahun", dtick = 1),
      yaxis = list(title = "Jumlah Kasus DBD"),
      hovermode = "closest",
      legend = list(orientation = "v", x = 1.05, y = 1)
    )
  })
  
  # Detail titik yang diklik
  output$tren_point_detail <- renderUI({
    event <- event_data("plotly_click", source = "tren_plot_interactive")
    
    if(is.null(event)) {
      return(HTML("<p><i>Klik pada titik di grafik untuk melihat detail perubahan...</i></p>"))
    }
    
    df <- tren_df()
    req(df, input$tren_wilayah)
    
    clicked_year <- event$x
    clicked_value <- event$y
    
    # Identifikasi wilayah yang diklik
    plot_data <- df %>%
      filter(Wilayah %in% input$tren_wilayah) %>%
      pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
      mutate(Tahun = as.numeric(Tahun))
    
    if(input$tren_show_avg) {
      avg_data <- df %>%
        select(-Wilayah) %>%
        summarise(across(everything(), ~mean(., na.rm = TRUE))) %>%
        mutate(Wilayah = "RATA-RATA JABAR") %>%
        pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
        mutate(Tahun = as.numeric(Tahun))
      
      plot_data <- bind_rows(plot_data, avg_data)
    }
    
    # Cari data yang paling dekat dengan klik
    plot_data$diff <- abs(plot_data$Jumlah_Kasus - clicked_value) + abs(plot_data$Tahun - clicked_year) * 100
    closest <- plot_data %>% arrange(diff) %>% slice(1)
    
    if(nrow(closest) > 0) {
      # Ambil data tahun sebelumnya untuk hitung perubahan
      prev_data <- plot_data %>%
        filter(Wilayah == closest$Wilayah, Tahun == closest$Tahun - 1)
      
      if(nrow(prev_data) > 0 && prev_data$Jumlah_Kasus > 0) {
        pct_change <- ((closest$Jumlah_Kasus - prev_data$Jumlah_Kasus) / prev_data$Jumlah_Kasus) * 100
        abs_change <- closest$Jumlah_Kasus - prev_data$Jumlah_Kasus
        
        change_text <- if(pct_change > 0) {
          paste0("<span style='color:red;'><b>NAIK ", round(pct_change, 1), "%</b></span> (+", comma(abs_change), " kasus)")
        } else {
          paste0("<span style='color:green;'><b>TURUN ", round(abs(pct_change), 1), "%</b></span> (", comma(abs_change), " kasus)")
        }
      } else {
        change_text <- "<i>Tidak ada data tahun sebelumnya</i>"
      }
      
      HTML(paste0(
        "<div style='font-size:14px; padding:15px; background:#f9f9f9; border-left:4px solid #2C7873; border-radius:5px;'>",
        "<h4 style='margin-top:0;'>Detail: ", closest$Wilayah, " - Tahun ", closest$Tahun, "</h4>",
        "<table style='width:100%; line-height:2;'>",
        "<tr><td><b>Jumlah Kasus:</b></td><td>", comma(closest$Jumlah_Kasus), " kasus</td></tr>",
        "<tr><td><b>Perubahan dari tahun lalu:</b></td><td>", change_text, "</td></tr>",
        "</table>",
        "</div>"
      ))
    } else {
      HTML("<p>Data tidak ditemukan</p>")
    }
  })
  
  output$tren_table <- renderDT({
    df <- tren_df()
    if (is.null(df)) return(datatable(data.frame(Message = "Data tren belum dimuat")))
    
    if(!is.null(input$tren_wilayah) && length(input$tren_wilayah) > 0) {
      table_data <- df %>%
        filter(Wilayah %in% input$tren_wilayah)
    } else {
      table_data <- df
    }
    
    table_data <- table_data %>%
      rowwise() %>%
      mutate(
        Rata_rata = round(mean(c_across(-Wilayah), na.rm = TRUE), 1),
        Total = sum(c_across(-Wilayah), na.rm = TRUE),
        Min = min(c_across(-Wilayah), na.rm = TRUE),
        Max = max(c_across(-Wilayah), na.rm = TRUE)
      ) %>%
      ungroup()
    
    datatable(table_data, options = list(pageLength = 27, scrollX = TRUE, scrollY = "400px"))
  })
  
  output$tren_interpretation <- renderUI({
    df <- tren_df()
    req(df, input$tren_wilayah)
    
    interpretations <- lapply(input$tren_wilayah, function(wil) {
      wil_data <- df %>%
        filter(Wilayah == wil) %>%
        select(-Wilayah) %>%
        pivot_longer(everything(), names_to = "Tahun", values_to = "Kasus") %>%
        mutate(Tahun = as.numeric(Tahun))
      
      if(nrow(wil_data) >= 2) {
        model <- lm(Kasus ~ Tahun, data = wil_data)
        slope <- coef(model)[2]
        
        trend_status <- if(slope > 50) {
          "<span style='color:red;'><b>Meningkat Signifikan</b></span>"
        } else if(slope > 0) {
          "<span style='color:orange;'><b>Meningkat</b></span>"
        } else if(slope > -50) {
          "<span style='color:blue;'><b>Menurun</b></span>"
        } else {
          "<span style='color:green;'><b>Menurun Signifikan</b></span>"
        }
        
        first_val <- wil_data$Kasus[1]
        last_val <- wil_data$Kasus[nrow(wil_data)]
        pct_change <- round(((last_val - first_val) / first_val) * 100, 1)
        
        paste0(
          "<li><b>", wil, ":</b> Tren ", trend_status, " (", 
          ifelse(pct_change > 0, "+", ""), pct_change, "% perubahan)</li>"
        )
      } else {
        paste0("<li><b>", wil, ":</b> Data tidak cukup</li>")
      }
    })
    
    HTML(paste0(
      "<div style='font-size:14px;'>",
      "<h4>Analisis Tren:</h4>",
      "<ul>", paste(interpretations, collapse = ""), "</ul>",
      "</div>"
    ))
  })
}

shinyApp(ui, server)
rsconnect::deployApp()

ACKNOWLEDGEMENT

AI digunakan untuk membantu membuat syntax dashboard agar tampilannya terlihat lebih menarik, membantu memunculkan ide, sebagai teman diskusi jika ditemukan hal-hal atau pernyataan di jurnal yang kemudian memunculkan banyak pertanyaan baru di otak saya, serta membantu membuat kalimat interpretasi yang efektif dan mudah dimengerti saat membacanya. Adapun untuk interpretasi aslinya merupakan interpretasi yang saya pahami sendiri dan kemudian dijabarkan kalimatnya agar lebih dapat dipahami.