207 Commits
Author SHA1 Message Date
Syaharani 013cb7b728 dashboard pasar 2026-10-21 10:14:49 +07:00
Althaf 4ab745cfe5 sumber informasi harga 2026-10-19 10:03:05 +07:00
Althaf c66eed56e2 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-10-16 15:11:49 +07:00
Syaharani 8be0906a2e Update profil petani 2026-10-16 15:48:42 +07:00
afriadirahman fcd79b7cfe Update dashboard Profil Detail Petani 2026-10-16 15:25:21 +07:00
Althaf bea9be8a39 resolve aksespasar 2026-10-16 15:11:39 +07:00
afriadirahman 5220b10de7 Update Config js 2026-10-16 10:38:30 +07:00
Syaharani 24725e3df1 update chart dashboard 2026-10-16 10:29:42 +07:00
Syaharani 60d32a88e9 update dashboard produksi 2026-10-16 10:04:56 +07:00
Althaf 4370d3c3d0 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-10-15 15:14:57 +07:00
Althaf 7699430301 new field akses pasar 2026-10-15 15:10:18 +07:00
fir2be df2b5a1312 hover and scroll on map 2026-10-15 14:12:03 +07:00
fir2be 2e8271d27a cars 2026-10-15 14:05:01 +07:00
fir2be 9b5c0a7309 perubahan query 2026-10-15 13:02:42 +07:00
fir2be 9d56b67998 sroll untuk 2026-10-15 11:31:57 +07:00
fir2be f95f43999a peta sebaran dan praktek budaya 2026-10-15 11:25:38 +07:00
Syaharani 3e172cf915 card dashboard produksi 2026-10-14 20:18:15 +07:00
Syaharani 3a9b3dbd7b dashboard produksi 2026-10-14 19:43:39 +07:00
Syaharani 80f0056efe dashboard produksi & ganti warna chart 2026-10-13 15:55:18 +07:00
afriadirahman aef27704f2 Update Karakteristik Petani 2026-10-12 10:22:01 +07:00
Syaharani f49bb0c2db dashboard produksi 2026-10-09 13:22:51 +07:00
afriadirahman 8a2c0292ac Update Header 2026-10-08 09:58:56 +07:00
afriadirahman 9ea324e553 Update Dashboard & Sidebar 2026-10-08 09:48:00 +07:00
afriadirahman 94c3288ea7 Update Dashboard Karakteristik Petani 2026-10-07 13:53:55 +07:00
Syaharani 8539bdcec4 update profil petani 2026-10-07 12:03:02 +07:00
Syaharani 77de648ded dashboard karakteristik petani 2026-10-07 10:20:20 +07:00
Syaharani 393cf0d0d1 karakteristik petani 2026-10-06 17:52:34 +07:00
Syaharani 7fdf9b400d solve jumlah lahan dan tanaman 2026-09-23 17:01:19 +07:00
Syaharani be4d46004e solve jumlah lahan dan tanaman 2026-09-23 16:36:14 +07:00
Syaharani fef5358402 solve filter jumlah lahan dan tanaman 2026-09-23 14:52:30 +07:00
Syaharani 02eed6da34 solve jumlah lahan dan tanaman 2026-09-23 14:37:08 +07:00
Syaharani 94d3f2a582 lower filter profil petani 2026-09-23 10:19:30 +07:00
Syaharani 471ca5ac0e filter profil petani 2026-09-23 10:13:13 +07:00
Althaf 8ec8f0c9d6 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-09-22 15:21:25 +07:00
Althaf 4d7d16e9f6 add migration riwayat petani 2026-09-22 15:20:45 +07:00
afriadirahman 1155b9b95b Update Header Pertanian 2026-09-22 15:12:32 +07:00
Syaharani 607533a327 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	app/Http/Controllers/Web/Produktivitas/PetaniController.php
2026-09-22 11:02:02 +07:00
Syaharani 6b25bff2c4 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-09-22 10:59:55 +07:00
Syaharani 71b4d42ea2 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-09-22 10:58:41 +07:00
Syaharani c42a2571f5 solve profil petani 2026-09-22 10:57:21 +07:00
Althaf 16903b21e3 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-09-18 16:22:57 +07:00
Althaf 1d4a44b8ac update destroy 2026-09-18 16:22:48 +07:00
Syaharani 539239b507 bumdes 2026-09-16 11:09:08 +07:00
afriadirahman a76b014056 Update Kelompok Tani & APP AGERA 2026-09-06 19:09:37 +07:00
Althaf d50c292cf7 instrumen koperasi 2026-09-03 13:17:16 +07:00
Althaf dacaec7a86 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-27 11:48:55 +07:00
Althaf 6967eafe98 add instrumen koperasi 2026-08-27 11:48:42 +07:00
Syaharani 118cda625a filter dashboard indikator pertanian 2026-08-20 17:24:31 +07:00
irwan.l.cahyono a3f7cb4669 filter segmen 2026-08-20 01:52:05 +07:00
irwan.l.cahyono ec009a2c55 indeks petani dan ajax 2026-08-20 01:10:56 +07:00
afriadirahman b63dafdaee Update Profile Petani 2026-08-20 01:01:27 +07:00
Althaf b97f682cd3 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-19 16:42:31 +07:00
Althaf 404019bab6 Added Score 2026-08-19 16:42:03 +07:00
Syaharani 609919ba30 solve dashboard survey petani 2026-08-19 16:21:42 +07:00
irwan.l.cahyono 047cb4a779 link ke profil 2026-08-19 10:37:17 +07:00
irwan.l.cahyono 530c297b53 revisi jumlahpetani 2026-08-19 01:57:52 +07:00
Syaharani 8fbf68e6ef solve keanggotaan kelompok tani 2026-08-18 14:05:46 +07:00
Syaharani ac8b370248 update dashboard indikator pertanian 2026-08-18 14:04:44 +07:00
Syaharani 88762f7afb update dashboard indikator pertanian 2026-08-13 17:00:00 +07:00
Syaharani b96de514e3 solve dashboard indikator pertanian 2026-08-13 16:31:11 +07:00
Syaharani 6830142a82 solve dashboard indikator pertanian 2026-08-13 16:07:55 +07:00
afriadirahman 4b26e82afb Optimize Demografi & Update Profil Petani 2026-08-13 15:31:04 +07:00
Syaharani b855a857b2 solve dashboard indikator pertanian 2026-08-13 13:07:03 +07:00
afriadirahman ba7520a23c Update Demografi Petani 2026-08-12 17:49:24 +07:00
afriadirahman 0b333ab4e9 Update Profil Detail Petani 2026-08-12 17:33:53 +07:00
Althaf 3b1f15d5b3 fixed aksespasar 2026-08-11 16:19:10 +07:00
Althaf 605506bba8 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-11 15:40:27 +07:00
afriadirahman c960ba14b1 Update Demografi Petani 2026-08-11 15:13:28 +07:00
Syaharani a4f38998d5 dashboard indikator pertanian 2026-08-07 16:09:24 +07:00
Syaharani 0d9f6c0b64 indikator pertanian 2026-08-07 15:56:37 +07:00
irwan.l.cahyono 81775b0c13 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-07 15:00:32 +07:00
irwan.l.cahyono ba1a318d09 soft deleted petani 2026-08-07 15:00:30 +07:00
afriadirahman 0761d64254 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-07 14:32:48 +07:00
afriadirahman 67c3c6c809 Update demografi dashboard 2026-08-07 14:32:40 +07:00
irwan.l.cahyono 04387268cd indeks petani 2026-08-07 14:31:16 +07:00
irwan.l.cahyono 946fabb9a5 lahan skor has petani 2026-08-07 14:17:06 +07:00
irwan.l.cahyono 0a0c86d648 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-07 12:59:26 +07:00
irwan.l.cahyono 8b21fab8b8 dashboard indeks petani 2026-08-07 12:59:25 +07:00
afriadirahman 16a0a3c56c Update Icon Demografi Dashboard 2026-08-07 11:33:08 +07:00
afriadirahman d4c6b64ba6 Update Sidebar Demografi Petani 2026-08-07 11:20:05 +07:00
afriadirahman b4089d72b3 Update Dashboard Demografi Petani 2026-08-07 11:17:53 +07:00
afriadirahman 01990ccec1 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-06 13:29:42 +07:00
irwan.l.cahyono 8aed88a31f dashboard indeks petani 2026-08-05 01:20:19 +07:00
irwan.l.cahyono 4aa0780802 dashboard profil petani komp 4 2026-08-03 13:08:01 +07:00
afriadirahman 4e41246382 Update Profile Petani Dashboard 2026-08-03 12:53:20 +07:00
Althaf 43fe9e5f7f Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-08-03 09:22:47 +07:00
irwan.l.cahyono c8d1994d3b dashboard profil petani 2026-07-30 15:23:34 +07:00
Althaf cf0f4b47b4 update akses pasar 2026-07-30 11:14:31 +07:00
irwan.l.cahyono 24a8016710 petani booted skor 2026-07-29 16:52:51 +07:00
irwan.l.cahyono 20e1eee6ff skor petani domain komponen hitung 2026-07-29 16:27:33 +07:00
afriadirahman c0432cd5f8 Update Sidebar Filter Fasilitator 2026-07-29 16:12:31 +07:00
irwan.l.cahyono 0ce0936a80 skor petani update 2026-07-29 16:05:43 +07:00
Syaharani 502c932f21 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-28 16:54:37 +07:00
Syaharani e60a66eeb7 bumdes: profil & add pengurus bumdes 2026-07-28 16:54:27 +07:00
afriadirahman 4475cd0098 Update Logo Sidebar 2026-07-28 16:18:41 +07:00
afriadirahman 67c111a2b3 Update Logo Agromonev & Agropro 2026-07-28 16:10:47 +07:00
Syaharani 3bcaa3ea50 solve survey produktivitas 2026-07-27 10:14:36 +07:00
Syaharani bed9a6f620 solve 2026-07-23 18:26:24 +07:00
Althaf 712b10e405 Merge branches 'main' and 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-22 17:06:45 +07:00
Althaf 6cfcf46ba6 use assets avatars 2026-07-22 17:06:31 +07:00
Syaharani b1b9828514 update jumlah tanaman 2026-07-22 16:54:57 +07:00
Syaharani 7af7e1bdaf update jumlah lahan 2026-07-22 16:40:24 +07:00
Syaharani bd05fc7f0f update hama dan penyakit 2026-07-22 16:23:32 +07:00
Syaharani 17b5687db8 update user profile 2026-07-22 15:55:07 +07:00
Syaharani e7f48b6500 solve lahan & tanaman 2026-07-22 15:49:25 +07:00
Syaharani a5884d0f8c solve akses pasar 2026-07-22 15:22:56 +07:00
Syaharani 9576c6295e solve sosial ekonomi 2026-07-22 14:41:05 +07:00
irwan.l.cahyono 079e300d5d Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-22 14:39:10 +07:00
irwan.l.cahyono 2c94316439 petani lahan hapus 2026-07-22 14:38:10 +07:00
Syaharani 690b53e5a2 Merge origin/main 2026-07-22 14:08:34 +07:00
irwan.l.cahyono 50c9adfdc7 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-22 13:56:56 +07:00
irwan.l.cahyono 696c7dede8 hama dan penyakit lahan 2026-07-22 13:56:55 +07:00
Syaharani 5967e7d6f3 solve pendidikan & pelatihan 2026-07-22 13:28:58 +07:00
Syaharani 4c66d72dc5 solve praktik budidaya 2026-07-22 13:27:35 +07:00
Althaf c43483a9f8 default profile-iconize & fixed overlapping tab 2026-07-22 13:24:39 +07:00
Syaharani e02a34a527 solve lahan & tanaman 2026-07-22 11:58:51 +07:00
Althaf 713355ce0a add text-heading to adjust icon color theme 2026-07-22 10:35:36 +07:00
Althaf 12f8d0bbda integrate image cropper for avatar upload/delete option/retouch 2026-07-22 10:10:06 +07:00
Althaf 8afe7f9b38 solve view-controller dropzone 2026-07-21 11:49:32 +07:00
Althaf fbf84eb4cb enhance UX alerts 2026-07-20 16:45:00 +07:00
Althaf e04093ad3f Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-20 16:36:29 +07:00
Althaf 3e1d3a33ee fix: resolve existing photos preview in Dropzone Lahan&Tanaman 2026-07-20 16:36:12 +07:00
irwan.l.cahyono e316d064ef auth dan user profile 2026-07-17 11:19:09 +07:00
irwan.l.cahyono 0c289ec244 dashboard indeks 2026-07-17 01:19:12 +07:00
irwan.l.cahyono 089f1b515d tanaman penaung di lahan 2026-07-16 15:22:42 +07:00
irwan.l.cahyono 4c647ea8ce Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-16 13:46:45 +07:00
irwan.l.cahyono 419404e273 null jumlah tanaman 2026-07-16 13:46:43 +07:00
Althaf 837e542851 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-16 11:42:40 +07:00
Althaf 9378d15d71 edit foto tambah lahan 2026-07-16 11:41:35 +07:00
irwan.l.cahyono 0381e9bf0e Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-15 15:00:14 +07:00
irwan.l.cahyono 3a70445e9a petani lahan required not 2026-07-15 15:00:13 +07:00
afriadirahman d2ed2c58fd Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-15 14:37:48 +07:00
afriadirahman 9b201efa06 update Praktik budidaya migration 2026-07-15 14:37:39 +07:00
irwan.l.cahyono 2b3a0b7c58 tanggal jqueryvalidation 2026-07-15 14:05:25 +07:00
irwan.l.cahyono a187fb1095 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-15 11:44:39 +07:00
irwan.l.cahyono 6b44c11181 header dan index 2026-07-15 11:44:38 +07:00
afriadirahman 090153e53a Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-15 11:30:37 +07:00
afriadirahman a79a93d291 Update komoditas Utama 2026-07-15 11:30:10 +07:00
irwan.l.cahyono 62f0f1bdbc tgl lahir edit 2026-07-15 10:55:11 +07:00
irwan.l.cahyono f0acd8c7ed dashboard komoditas 2026-07-15 10:41:27 +07:00
irwan.l.cahyono 3908b0be71 komoditas utama 2026-07-15 10:37:38 +07:00
irwan.l.cahyono 05cc3be9d3 enum pendidikan 2026-07-15 01:34:47 +07:00
irwan.l.cahyono f4c83d348a filter fasil 2026-07-15 01:19:12 +07:00
irwan.l.cahyono 61bd8dc388 redirect web 2026-07-15 01:11:19 +07:00
irwan.l.cahyono 002d6044b4 profil petani dashboard 2026-07-15 01:06:10 +07:00
irwan.l.cahyono 30196278d7 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-15 00:18:55 +07:00
irwan.l.cahyono 08c19c0714 skor petani 2026-07-15 00:18:00 +07:00
afriadirahman d6c036e19a Update Header Survey Petani 2026-07-14 23:35:15 +07:00
afriadirahman 981bc0d494 Update Master Komoditas 2026-07-14 23:05:16 +07:00
afriadirahman 857da1e900 Update Master Komoditas & Varietas 2026-07-14 22:34:38 +07:00
irwan.l.cahyono 66fc7227e6 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-14 17:13:55 +07:00
irwan.l.cahyono 3edfc57ba7 skor petani praktik budidaya 2026-07-14 17:13:52 +07:00
Althaf 4a560ebc4e Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-14 15:41:40 +07:00
Althaf d4eb27e583 foto tambah lahan 2026-07-14 15:41:16 +07:00
irwan.l.cahyono d1c0f90afd sidebar 2026-07-14 15:39:22 +07:00
irwan.l.cahyono a1dadd1b1b petani lahan tanaman edit 2026-07-14 14:50:33 +07:00
irwan.l.cahyono 2c28bb06db asset avatar 2026-07-14 11:15:23 +07:00
irwan.l.cahyono 96a53c8754 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-14 10:49:08 +07:00
irwan.l.cahyono fcc5d7dcc5 header skor 2026-07-14 10:44:35 +07:00
afriadirahman e383aacbeb Update Dashboard Survey Petani 2026-07-14 10:34:46 +07:00
irwan.l.cahyono 5b776b2ae8 header 2026-07-14 09:20:46 +07:00
irwan.l.cahyono c4e3e460b1 hidden show 2026-07-13 16:45:23 +07:00
irwan.l.cahyono a7dc1dc58b dashboard survey 2026-07-13 16:43:27 +07:00
irwan.l.cahyono d01c737364 web fasil 2026-07-13 16:39:46 +07:00
irwan.l.cahyono 8d50757b51 foto petani 2026-07-13 16:29:00 +07:00
irwan.l.cahyono f146cb2608 sideba petani profil 2026-07-13 16:22:00 +07:00
irwan.l.cahyono 095d84ec7f sidebar env 2026-07-13 16:13:54 +07:00
irwan.l.cahyono f259c6728a register 2026-07-13 15:53:50 +07:00
irwan.l.cahyono a241a62136 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-13 14:03:24 +07:00
irwan.l.cahyono 4755c8a482 dashboard profil petani 2026-07-13 14:03:19 +07:00
afriadirahman 7672877299 update Model Petani 2026-07-13 10:19:04 +07:00
irwan.l.cahyono 92e8efb247 skor petani 2026-07-13 10:18:32 +07:00
irwan.l.cahyono 599269410b foto lahan 2026-07-13 01:04:55 +07:00
irwan.l.cahyono c5b5721b31 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-12 23:37:37 +07:00
irwan.l.cahyono 2bf2a68a3a perbaikan petani dan lahan 2026-07-12 23:37:36 +07:00
afriadirahman b445392e9f Update Pendidikan & Pelatihan 2026-07-12 23:00:58 +07:00
afriadirahman bcc15d5240 Update Create User Register 2026-07-12 22:51:23 +07:00
afriadirahman d33bc8a42b Update Pendidikan Pelatihan 2026-07-11 15:19:44 +07:00
Syaharani df5bfeb67f update user profile 2026-07-10 17:53:28 +07:00
Syaharani b0e1f3f131 update menu fasilitator 2026-07-10 17:47:59 +07:00
irwan.l.cahyono a19871366a skorpetani 2026-07-10 14:18:55 +07:00
afriadirahman b4b851c7a9 Update enums kebutuhan Air 2026-07-10 13:53:40 +07:00
afriadirahman e6f6257434 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-10 13:49:24 +07:00
afriadirahman 469e57ba28 Kebutuhan air Permusim enums 2026-07-10 13:49:09 +07:00
Syaharani d06da22ae5 update kondisi tanaman 2026-07-10 13:43:29 +07:00
Syaharani 350df6adcf update kondisi tanaman 2026-07-10 13:39:28 +07:00
irwan.l.cahyono 1788efaed5 skor petani 2026-07-10 09:47:13 +07:00
irwan.l.cahyono d49bd4953c petani lahan dan skor 2026-07-10 08:47:48 +07:00
afriadirahman 26f6e83328 Update Praktik Budidaya 2026-07-10 02:48:23 +07:00
Althaf 79f6426125 bring to final akses pasar 2026-07-09 22:30:31 +07:00
Althaf 9557d1ec64 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-09 18:15:19 +07:00
Althaf 7bab43df71 resolve user create 2026-07-09 18:15:12 +07:00
Syaharani 316b256ae0 update login & register 2026-07-09 18:14:12 +07:00
Syaharani bfabab96c5 update field sosial ekonomi 2026-07-09 18:13:53 +07:00
Althaf bd9b0873a3 refinement registrasi-verifikasi role & rbac 2026-07-09 17:42:55 +07:00
Syaharani 3464a802e2 update skoring 2026-07-09 17:30:19 +07:00
afriadirahman 6c95c462cb Update Praktik Budidaya 2026-07-09 16:29:18 +07:00
Syaharani 626fdba065 update sosial ekonomi 2026-07-09 14:37:53 +07:00
afriadirahman 2152079b26 Update Praktik Budidaya 2026-07-08 22:24:23 +07:00
afriadirahman 6559a97f43 Update add Praktik Budidaya 2026-07-08 21:10:50 +07:00
afriadirahman 05c2e53378 Update Migration Input Update Praktik Budidaya 2026-07-08 20:57:27 +07:00
Syaharani 3df381a66c register fasilitator 2026-07-08 17:00:50 +07:00
Syaharani 6fac76127d skoring prtani 2026-07-08 17:00:34 +07:00
irwan.l.cahyono a1d9b34d93 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-08 01:37:17 +07:00
irwan.l.cahyono c01ba6a906 perbaikan add header conflict 2026-07-08 01:37:15 +07:00
afriadirahman 4976948bc6 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-07-07 23:24:54 +07:00
afriadirahman 734c3fba65 Update Praktik Budidaya 2026-07-07 23:24:48 +07:00
223 changed files with 40971 additions and 2650 deletions
@@ -0,0 +1,28 @@
<?php
namespace App\Enums\AksesPasar;
enum PraktikFermentasi: string
{
case RUTIN_SENDIRI = 'Ya, rutin — dilakukan sendiri/individual';
case RUTIN_KELOMPOK = 'Ya, rutin — bersama kelompok tani/kolektif';
case PERNAH_SENDIRI = 'Pernah mencoba — sendiri';
case PERNAH_KELOMPOK = 'Pernah mencoba — bersama kelompok';
case BELUM_PERNAH = 'Belum pernah';
public static function options(): array
{
return [
['value' => self::RUTIN_SENDIRI->value, 'label' => 'Ya, rutin — dilakukan sendiri/individual'],
['value' => self::RUTIN_KELOMPOK->value, 'label' => 'Ya, rutin — bersama kelompok tani/kolektif'],
['value' => self::PERNAH_SENDIRI->value, 'label' => 'Pernah mencoba — sendiri'],
['value' => self::PERNAH_KELOMPOK->value, 'label' => 'Pernah mencoba — bersama kelompok'],
['value' => self::BELUM_PERNAH->value, 'label' => 'Belum pernah'],
];
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Enums\AksesPasar;
enum SaluranTerdahulu: string
{
case TENGKULAK = 'Tengkulak–pedagang lokal';
case KOPERASI = 'Koperasi';
case BUMDES = 'BUMDes';
case LANGSUNG_PEMBELI = 'Langsung ke pembeli–eksportir';
case LAINNYA = 'Lainnya';
public static function options(): array
{
return [
['value' => self::TENGKULAK->value, 'label' => 'Tengkulak–pedagang lokal'],
['value' => self::KOPERASI->value, 'label' => 'Koperasi'],
['value' => self::BUMDES->value, 'label' => 'BUMDes'],
['value' => self::LANGSUNG_PEMBELI->value, 'label' => 'Langsung ke pembeli–eksportir'],
['value' => self::LAINNYA->value, 'label' => 'Lainnya'],
];
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Enums\AksesPasar;
enum SkorKesadaranPremium: int
{
case BELUM_PERNAH_DENGAR = 1;
case PERNAH_BELUM_PAHAM = 2;
case PERNAH_SEDIKIT_PAHAM = 3;
case SANGAT_PAHAM_BELUM_AKSES = 4;
case SUDAH_AKSES_KONSISTEN = 5;
public static function options(): array
{
return [
['value' => self::BELUM_PERNAH_DENGAR->value, 'label' => 'Belum pernah dengar'],
['value' => self::PERNAH_BELUM_PAHAM->value, 'label' => 'Pernah dengar tapi belum paham syarat'],
['value' => self::PERNAH_SEDIKIT_PAHAM->value, 'label' => 'Pernah dengar & sedikit paham syarat'],
['value' => self::SANGAT_PAHAM_BELUM_AKSES->value, 'label' => 'Sangat paham syarat tapi belum pernah akses'],
['value' => self::SUDAH_AKSES_KONSISTEN->value, 'label' => 'Sudah akses & dapat harga premium konsisten'],
];
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Enums\AksesPasar;
enum SkorPengalaman: int
{
case BELUM_PERNAH = 1;
case PERNAH_BERHENTI_TIDAK_ADA_AKSES = 2;
case PERNAH_BERHENTI_MUNGKIN_KEMBALI = 3;
case PERNAH_SESEKALI_JUAL = 4;
case PERNAH_MASIH_AKTIF = 5;
public static function options(): array
{
return [
['value' => self::BELUM_PERNAH->value, 'label' => 'Belum pernah coba saluran lain'],
['value' => self::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value, 'label' => 'Pernah, sudah berhenti & tidak ada akses'],
['value' => self::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value, 'label' => 'Pernah, berhenti tapi masih mungkin kembali'],
['value' => self::PERNAH_SESEKALI_JUAL->value, 'label' => 'Pernah & sesekali masih jual ke sana'],
['value' => self::PERNAH_MASIH_AKTIF->value, 'label' => 'Pernah & masih punya akses aktif'],
];
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Enums\AksesPasar;
enum TingkatKepercayaanPembeli: int
{
case SANGAT_PERCAYA = 1;
case CUKUP_PERCAYA = 2;
case RAGU_RAGU = 3;
case TIDAK_PERCAYA = 4;
public static function options(): array
{
return [
['value' => self::SANGAT_PERCAYA->value, 'label' => 'Sangat percaya, berani langsung coba'],
['value' => self::CUKUP_PERCAYA->value, 'label' => 'Cukup percaya, tapi hati hati'],
['value' => self::RAGU_RAGU->value, 'label' => 'Ragu ragu'],
['value' => self::TIDAK_PERCAYA->value, 'label' => 'Tidak percaya, enggan mencoba'],
];
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Enums\Bumdes;
enum JabatanBumdes: string
{
case DIREKTUR = 'direktur';
case DIREKTUR_KETUA = 'direktur_ketua';
case SEKRETARIS = 'sekretaris';
case BENDAHARA = 'bendahara';
case KEPALA_UNIT_USAHA = 'kepala_unit_usaha';
case PENGAWAS = 'pengawas';
case PENASIHAT = 'penasihat';
case ANGGOTA = 'anggota';
case STAF_OPERASIONAL = 'staf_operasional';
case LAINNYA = 'lainnya';
public function label(): string
{
return match ($this) {
self::DIREKTUR => 'Direktur/Ketua',
self::DIREKTUR_KETUA => 'Direktur/Ketua',
self::SEKRETARIS => 'Sekretaris',
self::BENDAHARA => 'Bendahara',
self::KEPALA_UNIT_USAHA => 'Kepala Unit Usaha',
self::PENGAWAS => 'Pengawas',
self::PENASIHAT => 'Penasihat',
self::ANGGOTA => 'Anggota',
self::STAF_OPERASIONAL => 'Staf Operasional',
self::LAINNYA => 'Lainnya',
};
}
public static function options(): array
{
return collect([
self::DIREKTUR_KETUA,
self::SEKRETARIS,
self::BENDAHARA,
self::LAINNYA,
])->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+17
View File
@@ -7,4 +7,21 @@ enum KeparahanSerangan: string
case RINGAN = 'ringan';
case SEDANG = 'sedang';
case BERAT = 'berat';
public function label(): string
{
return match($this) {
self::RINGAN => 'Ringan, hanya beberapa tanaman',
self::SEDANG => 'Sedang, sebagian tanaman terdampak',
self::BERAT => 'Parah, sebagian besar tanaman terdampak',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+19 -1
View File
@@ -7,6 +7,24 @@ enum KondisiTanaman: string
case BAIK = 'baik';
case SEDANG = 'sedang';
case BURUK = 'buruk';
case SAKIT= 'sakit';
//case SAKIT= 'sakit';
case MATI = 'mati';
public function label(): string
{
return match($this) {
self::BAIK => 'Baik, tumbuh lebat, banyak buah',
self::SEDANG => 'Sedang, cukup tumbuh, ada masalah',
self::BURUK => 'Buruk, banyak tanaman tua dan lemah/tidak berbuah',
self::MATI => 'Mati, tanaman telah mati, tidak dapat tumbuh maupun berbuah',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Enums\Koperasi;
enum PosisiKoperasi: string
{
case PENGURUS = 'Pengurus koperasi';
case ANGGOTA = 'Anggota koperasi';
case PENDAMPING = 'Pendamping koperasi';
}
+6 -6
View File
@@ -19,13 +19,13 @@ enum Pendidikan: string
{
return match($this) {
self::TIDAK_SEKOLAH => 'Tidak Sekolah',
self::SD => 'SD atau Sederajat',
self::SMP => 'SMP atau Sederajat',
self::SMA => 'SMA atau Sederajat',
self::SD => 'SD',
self::SMP => 'SMP',
self::SMA => 'SMA/SMK',
self::DIPLOMA => 'Diploma 1-3 (D1-D3)',
self::SARJANA => 'Sarjana (S1) atau sederajat',
self::MAGISTER => 'Magister (S2) atau sederajat',
self::DOKTOR => 'Doktor (S3) atau sederajat',
self::SARJANA => 'Sarjana (S1)',
self::MAGISTER => 'Magister (S2)',
self::DOKTOR => 'Doktor (S3)',
self::PESANTREN => 'Pesantren, Semiari, Wihara, atau Pendidikan Keagamaan Formal',
self::LAINNYA => 'Lainnya',
};
@@ -0,0 +1,26 @@
<?php
namespace App\Enums\PendidikanPelatihan;
enum PelatihanGAP: string
{
case PEMANGKASAN = 'Pemangkasan atau perawatan tanaman';
case PEMUPUKAN = 'Pemupukan atau nutrisi tanah';
case PENGENDALIAN = 'Pengendalian hama dan penyakit';
case PRAKTIK = 'Praktik Berkebun yang Baik (GAP)';
case PENGOLAHAN = 'Pengolahan/pascapanen';
case PENYERBUKAN = 'Penyerbukan manual (Vanili)';
public function label(): string
{
return $this->value;
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+18
View File
@@ -8,4 +8,22 @@ enum PenyakitTanaman: string
case DAUN_MENGUNING = 'daun_menguning';
case DAUN_BERCAK = 'daun_bercak';
case TANAMAN_LAYU = 'tanaman_layu';
public function label(): string
{
return match ($this) {
self::BUSUK_BUAH => 'Busuk Buah',
self::DAUN_MENGUNING => 'Daun Menguning',
self::DAUN_BERCAK => 'Daun Bercak',
self::TANAMAN_LAYU => 'Tanaman Layu',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum Bulan: string
{
case JANUARI = 'Januari';
case FEBRUARI = 'Februari';
case MARET = 'Maret';
case APRIL = 'April';
case MEI = 'Mei';
case JUNI = 'Juni';
case JULI = 'Juli';
case AGUSTUS = 'Agustus';
case SEPTEMBER = 'September';
case OKTOBER = 'Oktober';
case NOVEMBER = 'November';
case DESEMBER = 'Desember';
public function label(): string
{
return $this->value;
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum CaraSortasi: string
{
case DIPILAH = 'Dipilah manual dengan tangan (memilih satu per satu buah/biji yang bagus dan membuang yang rusak)';
case ALAT_BANTU = 'Menggunakan alat bantu sederhana (tampah, ayakan, atau bak untuk memisahkan kotoran ringan)';
case PERENDAMAN = 'Metode perendaman/tenggelam (memisahkan buah mengapung dan tenggelam)';
case GRADING_KRITERIA = 'Grading berdasarkan kriteria (ukuran, warna, atau tingkat kematangan)';
case PEMBERSIHAN = 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)';
public function label(): string
{
return match($this) {
self::DIPILAH => 'Dipilah manual dengan tangan (memilih satu per satu buah/biji yang bagus dan membuang yang rusak)',
self::ALAT_BANTU => 'Menggunakan alat bantu sederhana (tampah, ayakan, atau bak untuk memisahkan kotoran ringan)',
self::PERENDAMAN => 'Metode perendaman/tenggelam (memisahkan buah mengapung dan tenggelam)',
self::GRADING_KRITERIA => 'Grading berdasarkan kriteria (ukuran, warna, atau tingkat kematangan)',
self::PEMBERSIHAN => 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum DasarPenentuanPanen: int
{
case KEBUTUHAN_UANG = 1;
case UMUR_TANPA_KONDISI_BUAH = 2;
case BUAH_JATUH = 3;
case TINGKAT_KEPADATAN = 4;
case WARNA_KULIT_BUAH = 5;
public function label(): string
{
return match($this) {
self::KEBUTUHAN_UANG => 'Kebutuhan uang mendesak, tanpa mempertimbangkan kematangan',
self::UMUR_TANPA_KONDISI_BUAH => 'Umur/jadwal tetap, tanpa melihat kondisi buah langsung',
self::BUAH_JATUH => 'Buah jatuh/matang pohon (sudah terlalu matang saat dipetik)',
self::TINGKAT_KEPADATAN => 'Bersuara saat diketuk atau tingkat kepadatan buah',
self::WARNA_KULIT_BUAH => 'Warna kulit buah (indikator paling akurat dan konsisten untuk menentukan kematangan optimal)',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum EstimasiKehilanganHasil: string
{
case TIDAK_ADA = 'Tidak ada kehilangan';
case SEDIKIT = 'Sedikit, sekitar 25 % dari total panen';
case LUMAYAN = 'Lumayan, sekitar 50% dari total panen';
case BANYAK = 'Banyak, sekitar 75% dari total panen';
case HAMPIR_SEMUA = 'Hampir semua rusak, lebih dari 75%';
case TIDAK_TAHU = 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)';
public function label(): string
{
return match($this) {
self::TIDAK_ADA => 'Tidak ada kehilangan',
self::SEDIKIT => 'Sedikit, sekitar 25 % dari total panen',
self::LUMAYAN => 'Lumayan, sekitar 50% dari total panen',
self::BANYAK => 'Banyak, sekitar 75% dari total panen',
self::HAMPIR_SEMUA => 'Hampir semua rusak, lebih dari 75%',
self::TIDAK_TAHU => 'Tidak tahu/sulit memperkirakan',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -8,7 +8,7 @@ enum FrekuensiPemangkasanPendamping: string
case TIDAK_RUTIN = 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya';
case SATU_KALI_TAHUN = 'Memangkas 1 kali/tahun, tahu jenis tekniknya';
case DUA_KALI_TAHUN = 'Memangkas 2 kali/tahun, tahu jenis tekniknya';
case LEBIH_DUA_KALI_TAHUN = 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin';
case LEBIH_DUA_KALI_TAHUN = 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin';
public function label(): string
{
@@ -17,7 +17,7 @@ enum FrekuensiPemangkasanPendamping: string
self::TIDAK_RUTIN => 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya',
self::SATU_KALI_TAHUN => 'Memangkas 1 kali/tahun, tahu jenis tekniknya',
self::DUA_KALI_TAHUN => 'Memangkas 2 kali/tahun, tahu jenis tekniknya',
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin',
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin',
};
}
@@ -17,7 +17,7 @@ enum FrekuensiPemangkasanUtama: int
self::TIDAK_RUTIN => 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya',
self::SATU_KALI_TAHUN => 'Memangkas 1 kali/tahun, tahu jenis tekniknya',
self::DUA_KALI_TAHUN => 'Memangkas 2 kali/tahun, tahu jenis tekniknya',
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin',
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin',
};
}
@@ -16,8 +16,8 @@ enum MetodePenangananHamaPenyakit: int
self::TIDAK_ADA => 'Tidak ada penanganan',
self::CARA_TRADISIONAL => 'Cara tradisional (doa/ritual)',
self::PESTISIDA_SANITASI => 'Pestisida atau sanitasi',
self::KOMBINASI => 'Kombinasi pestisada dan sanitasi',
self::PHT => 'Pengendalian hama terpadu(PHT)',
self::KOMBINASI => 'Kombinasi pestisida dan sanitasi',
self::PHT => 'Pengendalian hama terpadu (PHT)',
};
}
@@ -13,11 +13,11 @@ enum PenangananGulma: int
public function label(): string
{
return match($this) {
self::DIBIARKAN => 'Dibiarkan di kebun',
self::DICABUT => 'Dikumpulkan dan dibuang',
self::DISEMPROT => 'Dijadikan kompos',
self::RUTIN_DICABUT_TANPA_JADWAL => 'Dikumpulkan dan dibuang',
self::RUTIN_TERJADWAL => 'Dijadikan kompos',
self::DIBIARKAN => 'Dibiarkan saja, tidak pernah ditangani',
self::DICABUT => 'Dicabut/dibersihkan sesekali, tidak terjadwal',
self::DISEMPROT => 'Disemprot herbisida sesekali, tidak terjadwal',
self::RUTIN_DICABUT_TANPA_JADWAL => 'Rutin dicabut/dipangkas, tapi tanpa jadwal yang jelas',
self::RUTIN_TERJADWAL => 'Rutin terjadwal dengan metode yang konsisten (dicabut/dipangkas/dikelola sebagai mulsa)',
};
}
@@ -0,0 +1,31 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum PraktikSortasi: int
{
case TIDAK_PERNAH = 1;
case SANGAT_SEDERHANA = 2;
case TIDAK_SETIAP_PANEN = 3;
case SETIAP_PANEN = 4;
case KETAT = 5;
public function label(): string
{
return match($this) {
self::TIDAK_PERNAH => 'Tidak pernah melakukan sortasi sama sekali',
self::SANGAT_SEDERHANA => 'Sortasi sangat sederhana, hanya membuang yang jelas-jelas rusak/busuk',
self::TIDAK_SETIAP_PANEN => 'Sortasi dilakukan tapi tidak setiap panen (kadang dilakukan, kadang tidak)',
self::SETIAP_PANEN => 'Sortasi dilakukan setiap panen, tapi standarnya belum konsisten',
self::KETAT => 'Sortasi ketat dengan standar yang jelas dan konsisten setiap panen',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum TindakanPanenReject: string
{
case BUANG = 'Dibuang';
case JUAL = 'Dijual ke tempat lain';
public function label(): string
{
return match($this) {
self::BUANG => 'Dibuang',
self::JUAL => 'Dijual ke tempat lain',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Enums\PraktikBudidaya;
enum TingkatPanenReject: int
{
case SERING = 1;
case CUKUP_SERING = 2;
case KADANG = 3;
case JARANG = 4;
case TIDAK_ADA = 5;
public function label(): string
{
return match($this) {
self::SERING => 'Sering ditolak, lebih dari 50% hasil panen biasanya ditolak/tidak laku karena mutu',
self::CUKUP_SERING => 'Cukup sering, sekitar 25–50% hasil panen ditolak/tidak laku',
self::KADANG => 'Kadang ditolak, sekitar 10–25% hasil panen',
self::JARANG => 'Jarang ditolak, kurang dari 10% hasil panen',
self::TIDAK_ADA => 'Tidak ada yang ditolak, semua hasil panen sesuai standar pasar utama',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
+17
View File
@@ -7,4 +7,21 @@ enum SeranganHama: string
case ULAT = 'ulat';
case BELALANG = 'belalang';
case KUTU = 'kutu';
public function label(): string
{
return match ($this) {
self::ULAT => 'Ulat',
self::BELALANG => 'Belalang',
self::KUTU => 'Kutu',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum AlasanKreditDitolak: string
{
case TIDAK_PUNYA_JAMINAN_AGUNAN = 'tidak_punya_jaminan_agunan';
case PROSES_TERLALU_RUMIT = 'proses_terlalu_rumit';
case TAKUT_TIDAK_BISA_BAYAR = 'takut_tidak_bisa_bayar';
case TIDAK_TAHU_CARANYA = 'tidak_tahu_caranya';
case LAINNYA = 'lainnya';
public function label(): string
{
return match ($this) {
self::TIDAK_PUNYA_JAMINAN_AGUNAN => 'Tidak punya jaminan-agunan',
self::PROSES_TERLALU_RUMIT => 'Proses terlalu rumit',
self::TAKUT_TIDAK_BISA_BAYAR => 'Takut tidak bisa bayar',
self::TIDAK_TAHU_CARANYA => 'Tidak tahu caranya',
self::LAINNYA => 'Lainnya',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Enums\SosialEkonomi;
enum BisaBeliPupukBersubsidi: string
{
case YA = 'ya';
case TIDAK = 'tidak';
public function label(): string
{
return match ($this) {
self::YA => 'Ya',
self::TIDAK => 'Tidak',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Enums\SosialEkonomi;
enum JenisAsetProduktif: string
{
case TERNAK = 'ternak';
case KENDARAAN = 'kendaraan';
case ALAT_PERTANIAN = 'alat_pertanian';
case LAINNYA = 'lainnya';
public function label(): string
{
return match ($this) {
self::TERNAK => 'Ternak',
self::KENDARAAN => 'Kendaraan',
self::ALAT_PERTANIAN => 'Alat pertanian',
self::LAINNYA => 'Lainnya',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum KategoriPendapatanKomoditas: string
{
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KeaktifanPertemuanKelompok: int
{
case TIDAK_PERNAH_HADIR = 1;
case JARANG_HADIR = 2;
case SERING_HADIR_LEBIH_BANYAK_DIAM = 3;
case HAMPIR_SELALU_HADIR_KADANG_DISKUSI = 4;
case SELALU_HADIR_AKTIF_DISKUSI = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_PERNAH_HADIR => 'Tidak pernah hadir',
self::JARANG_HADIR => 'Jarang hadir',
self::SERING_HADIR_LEBIH_BANYAK_DIAM => 'Sering hadir tapi lebih banyak diam',
self::HAMPIR_SELALU_HADIR_KADANG_DISKUSI => 'Hampir selalu hadir & kadang diskusi',
self::SELALU_HADIR_AKTIF_DISKUSI => 'Selalu hadir & aktif diskusi',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KebebasanMemilihPembeli: int
{
case TIDAK_BEBAS_TERIKAT_SATU_PEMBELI = 1;
case AGAK_TERIKAT_ADA_TEKANAN = 2;
case AGAK_BEBAS_CENDERUNG_PEMBELI_SAMA = 3;
case CUKUP_BEBAS_SESEKALI_PINDAH = 4;
case SANGAT_BEBAS_PINDAH_KAPAN_SAJA = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_BEBAS_TERIKAT_SATU_PEMBELI => 'Tidak bebas, terikat satu pembeli',
self::AGAK_TERIKAT_ADA_TEKANAN => 'Agak terikat, ada tekanan',
self::AGAK_BEBAS_CENDERUNG_PEMBELI_SAMA => 'Agak bebas, cenderung pembeli sama',
self::CUKUP_BEBAS_SESEKALI_PINDAH => 'Cukup bebas, sesekali pindah',
self::SANGAT_BEBAS_PINDAH_KAPAN_SAJA => 'Sangat bebas, pindah kapan saja',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KedekatanSesamaAnggota: string
{
case SANGAT_DEKAT = 'sangat_dekat';
case CUKUP_DEKAT = 'cukup_dekat';
case SEBATAS_KENAL = 'sebatas_kenal';
case TIDAK_BEGITU_KENAL = 'tidak_begitu_kenal';
public function label(): string
{
return match ($this) {
self::SANGAT_DEKAT => 'Sangat dekat',
self::CUKUP_DEKAT => 'Cukup dekat',
self::SEBATAS_KENAL => 'Sebatas kenal',
self::TIDAK_BEGITU_KENAL => 'Tidak begitu kenal',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KemauanIkutProgram: int
{
case TIDAK_MAU = 1;
case RAGU_RAGU = 2;
case MAU_PERLU_DIPASTIKAN_MANFAAT = 3;
case MAU_LIHAT_PROGRAM_DULU = 4;
case MAU_LANGSUNG_IKUT = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_MAU => 'Tidak mau',
self::RAGU_RAGU => 'Ragu-ragu',
self::MAU_PERLU_DIPASTIKAN_MANFAAT => 'Mau, perlu dipastikan manfaatnya',
self::MAU_LIHAT_PROGRAM_DULU => 'Mau, ingin lihat programnya dulu',
self::MAU_LANGSUNG_IKUT => 'Mau, langsung ikut',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KemauanMencobaPraktikBaru: int
{
case TIDAK_MAU = 1;
case RAGU_RAGU = 2;
case MAU_LIHAT_HASIL_DULU = 3;
case MAU_LANGSUNG_COBA = 4;
case MAU_COBA_SAMPAI_BERHASIL = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_MAU => 'Tidak mau',
self::RAGU_RAGU => 'Ragu-ragu',
self::MAU_LIHAT_HASIL_DULU => 'Mau, lihat hasil dulu',
self::MAU_LANGSUNG_COBA => 'Mau langsung coba',
self::MAU_COBA_SAMPAI_BERHASIL => 'Mau coba sampai berhasil',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,38 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KeterikatanHutangPanen: int
{
case TIDAK_BERSEDIA_MENJAWAB = 0;
case HUTANG_BESAR_HAMPIR_SELURUH_PANEN_TERIKAT = 1;
case HUTANG_LEBIH_DARI_SEPARUH_PANEN_TERIKAT = 2;
case HUTANG_SEBAGIAN_KECIL_TERIKAT = 3;
case HUTANG_KECIL_TIDAK_MENGIKAT_PANEN = 4;
case TIDAK_ADA = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
self::HUTANG_BESAR_HAMPIR_SELURUH_PANEN_TERIKAT => 'Ada hutang besar, hampir seluruh panen terikat',
self::HUTANG_LEBIH_DARI_SEPARUH_PANEN_TERIKAT => 'Ada hutang, >separuh panen terikat',
self::HUTANG_SEBAGIAN_KECIL_TERIKAT => 'Ada hutang, sebagian kecil terikat',
self::HUTANG_KECIL_TIDAK_MENGIKAT_PANEN => 'Ada hutang kecil, tidak mengikat panen',
self::TIDAK_ADA => 'Tidak ada',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KeterlibatanProgramKeuanganFormal: int
{
case TIDAK_PERNAH_MENERIMA = 1;
case PERNAH_SATU_TIDAK_BERMANFAAT = 2;
case SEDANG_MENERIMA_SATU = 3;
case SEDANG_MENERIMA_LEBIH_DARI_SATU = 4;
case AKTIF_BEBERAPA_PROGRAM_BERMANFAAT = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_PERNAH_MENERIMA => 'Tidak pernah menerima',
self::PERNAH_SATU_TIDAK_BERMANFAAT => 'Pernah 1 program, tidak lagi & tidak bermanfaat',
self::SEDANG_MENERIMA_SATU => 'Sedang menerima 1 program',
self::SEDANG_MENERIMA_LEBIH_DARI_SATU => 'Sedang menerima >1 program',
self::AKTIF_BEBERAPA_PROGRAM_BERMANFAAT => 'Aktif beberapa program & merasakan manfaat nyata',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum KondisiAsetProduktif: int
{
case TIDAK_ADA = 1;
case ADA_SATU_KURANG_BAIK = 2;
case ADA_SATU_BAIK = 3;
case ADA_DUA_LEBIH_SEBAGIAN_KURANG_BAIK = 4;
case ADA_DUA_LEBIH_SEMUA_BAIK = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_ADA => 'Tidak ada',
self::ADA_SATU_KURANG_BAIK => 'Ada 1 jenis, kondisi kurang baik',
self::ADA_SATU_BAIK => 'Ada 1 jenis, kondisi baik',
self::ADA_DUA_LEBIH_SEBAGIAN_KURANG_BAIK => 'Ada 2+ jenis, sebagian kurang baik',
self::ADA_DUA_LEBIH_SEMUA_BAIK => 'Ada 2+ jenis, semua kondisi baik',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum ManfaatKelompokTani: int
{
case TIDAK_ADA_MANFAAT = 1;
case MANFAAT_SOSIAL_SAJA = 2;
case SEDIKIT_MANFAAT_PRODUKSI_PEMASARAN = 3;
case MANFAAT_CUKUP_TERASA = 4;
case MANFAAT_NYATA_SIGNIFIKAN = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_ADA_MANFAAT => 'Tidak ada manfaat',
self::MANFAAT_SOSIAL_SAJA => 'Manfaat sosial saja',
self::SEDIKIT_MANFAAT_PRODUKSI_PEMASARAN => 'Sedikit manfaat produksi-pemasaran',
self::MANFAAT_CUKUP_TERASA => 'Manfaat cukup terasa',
self::MANFAAT_NYATA_SIGNIFIKAN => 'Manfaat nyata & signifikan',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Enums\SosialEkonomi;
enum MinatPembeliHargaStabil: string
{
case MAU_HARGA_STABIL_LEBIH_PENTING = 'mau_harga_stabil_lebih_penting';
case MAU_TAPI_INGIN_BANDINGKAN_DULU = 'mau_tapi_ingin_bandingkan_dulu';
case RAGU_RAGU = 'ragu_ragu';
case TIDAK_MAU_CARI_HARGA_TERBAIK = 'tidak_mau_cari_harga_terbaik';
public function label(): string
{
return match ($this) {
self::MAU_HARGA_STABIL_LEBIH_PENTING => 'Mau-harga stabil lebih penting',
self::MAU_TAPI_INGIN_BANDINGKAN_DULU => 'Mau tapi ingin bandingkan dulu',
self::RAGU_RAGU => 'Ragu-ragu',
self::TIDAK_MAU_CARI_HARGA_TERBAIK => 'Tidak mau-cari harga terbaik tiap jual',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum OrientasiRencanaJangkaPanjang: int
{
case BELUM_KEPIKIRAN = 1;
case INGIN_TETAP_SEPERTI_SEKARANG = 2;
case INGIN_BERUBAH_BELUM_TAHU_CARANYA = 3;
case INGIN_SUDAH_MULAI_DIPIKIRKAN = 4;
case PUNYA_RENCANA_KONKRET = 5;
public function label(): string
{
return match ($this) {
self::BELUM_KEPIKIRAN => 'Belum kepikiran',
self::INGIN_TETAP_SEPERTI_SEKARANG => 'Ingin tetap seperti sekarang',
self::INGIN_BERUBAH_BELUM_TAHU_CARANYA => 'Ada keinginan berubah tapi belum tahu caranya',
self::INGIN_SUDAH_MULAI_DIPIKIRKAN => 'Ada keinginan & sudah mulai dipikirkan',
self::PUNYA_RENCANA_KONKRET => 'Sudah punya rencana konkret',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PanutanPetaniSukses: int
{
case TIDAK_ADA = 1;
case ADA_DENGAR_DARI_ORANG_LAIN = 2;
case TAHU_SIAPA_TIDAK_KENAL = 3;
case KENAL_BELUM_DISKUSI = 4;
case KENAL_PERNAH_DISKUSI_LANGSUNG = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_ADA => 'Tidak ada',
self::ADA_DENGAR_DARI_ORANG_LAIN => 'Ada, dengar dari orang lain saja',
self::TAHU_SIAPA_TIDAK_KENAL => 'Tahu siapa, tapi tidak kenal',
self::KENAL_BELUM_DISKUSI => 'Kenal, belum pernah diskusi',
self::KENAL_PERNAH_DISKUSI_LANGSUNG => 'Kenal & pernah diskusi langsung',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PemahamanMutuHarga: int
{
case TIDAK_TAHU = 1;
case PERNAH_DENGAR_TIDAK_YAKIN = 2;
case TAHU_BEDA_BELUM_BUKTIKAN = 3;
case TAHU_PERNAH_HARGA_LEBIH_TINGGI = 4;
case TAHU_BETUL_BISA_JELASKAN_RINCI = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_TAHU => 'Tidak tahu',
self::PERNAH_DENGAR_TIDAK_YAKIN => 'Pernah dengar, tidak yakin',
self::TAHU_BEDA_BELUM_BUKTIKAN => 'Tahu ada beda, belum buktikan',
self::TAHU_PERNAH_HARGA_LEBIH_TINGGI => 'Tahu & pernah dapat harga lebih tinggi',
self::TAHU_BETUL_BISA_JELASKAN_RINCI => 'Tahu betul & bisa jelaskan rinci',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PengalamanKreditKebun: int
{
case TIDAK_PERNAH_MENCOBA = 1;
case PERNAH_COBA_DITOLAK = 2;
case SEDANG_PROSES = 3;
case BERHASIL_MASIH_PELUNASAN = 4;
case BERHASIL_SUDAH_LUNAS = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_PERNAH_MENCOBA => 'Tidak pernah mencoba',
self::PERNAH_COBA_DITOLAK => 'Pernah coba tapi ditolak',
self::SEDANG_PROSES => 'Sedang dalam proses',
self::BERHASIL_MASIH_PELUNASAN => 'Berhasil, masih proses pelunasan',
self::BERHASIL_SUDAH_LUNAS => 'Berhasil & sudah lunas',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PengetahuanHargaHasilOlah: string
{
case TIDAK_TAHU = 'tidak_tahu';
case PERNAH_DENGAR_TIDAK_TAHU_PENGARUH = 'pernah_dengar_tidak_tahu_pengaruh';
case TAHU_BEDA_HARGA_BELUM_COBA = 'tahu_beda_harga_belum_coba';
case PERNAH_JUAL_HASIL_OLAH_HARGA_LEBIH_TINGGI = 'pernah_jual_hasil_olah_harga_lebih_tinggi';
public function label(): string
{
return match ($this) {
self::TIDAK_TAHU => 'Tidak tahu',
self::PERNAH_DENGAR_TIDAK_TAHU_PENGARUH => 'Pernah dengar tapi tidak tahu pengaruh ke harga',
self::TAHU_BEDA_HARGA_BELUM_COBA => 'Tahu ada beda harga tapi belum coba',
self::PERNAH_JUAL_HASIL_OLAH_HARGA_LEBIH_TINGGI => 'Sudah pernah jual hasil olah dengan harga lebih tinggi',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PeranDalamKelompok: int
{
case ANGGOTA_PASIF = 1;
case ANGGOTA_BIASA_KADANG_DILIBATKAN = 2;
case ANGGOTA_BIASA_CUKUP_AKTIF = 3;
case PENGURUS_BELUM_BANYAK_TANGGUNG_JAWAB = 4;
case KETUA_PENGURUS_AKTIF = 5;
public function label(): string
{
return match ($this) {
self::ANGGOTA_PASIF => 'Anggota pasif',
self::ANGGOTA_BIASA_KADANG_DILIBATKAN => 'Anggota biasa, kadang dilibatkan',
self::ANGGOTA_BIASA_CUKUP_AKTIF => 'Anggota biasa, cukup aktif',
self::PENGURUS_BELUM_BANYAK_TANGGUNG_JAWAB => 'Pengurus, belum banyak menjalankan tanggung jawab',
self::KETUA_PENGURUS_AKTIF => 'Ketua-pengurus aktif & menjalankan tanggung jawab nyata',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum PeriodePendapatan: string
{
@@ -0,0 +1,30 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PernahBantuanNgoPerusahaan: string
{
case PERNAH = 'pernah';
case TIDAK_PERNAH = 'tidak_pernah';
public function label(): string
{
return match ($this) {
self::PERNAH => 'Pernah',
self::TIDAK_PERNAH => 'Tidak pernah',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PernahJualHargaLebihTinggi: string
{
case BELUM_PERNAH = 'belum_pernah';
case PERNAH = 'pernah';
public function label(): string
{
return match ($this) {
self::BELUM_PERNAH => 'Belum pernah',
self::PERNAH => 'Pernah',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum PihakMembantuKonflik: string
{
@@ -0,0 +1,32 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PinjamanTokohAdatPanen: string
{
case TIDAK_ADA = 'tidak_ada';
case ADA = 'ada';
case TIDAK_BERSEDIA_MENJAWAB = 'tidak_bersedia_menjawab';
public function label(): string
{
return match ($this) {
self::TIDAK_ADA => 'Tidak ada',
self::ADA => 'Ada',
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace App\Enums\SosialEkonomi;
enum PraktikIjon: int
{
case TIDAK_BERSEDIA_MENJAWAB = 0;
case HAMPIR_SELURUH_PANEN_TERIKAT = 1;
case SEBAGIAN_BESAR_TERIKAT = 2;
case SESEKALI_SEBAGIAN_KECIL = 3;
case PERNAH_SUDAH_LAMA_TIDAK_LAGI = 4;
case TIDAK_PERNAH = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
self::HAMPIR_SELURUH_PANEN_TERIKAT => 'Hampir seluruh panen terikat ijon',
self::SEBAGIAN_BESAR_TERIKAT => 'Sebagian besar terikat',
self::SESEKALI_SEBAGIAN_KECIL => 'Sesekali-sebagian kecil',
self::PERNAH_SUDAH_LAMA_TIDAK_LAGI => 'Pernah, sudah lama tidak lagi',
self::TIDAK_PERNAH => 'Tidak pernah',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum SkorKecukupanPenghasilan: int
{
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum SkorKejelasanStatusLahan: int
{
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum SkorKemampuanMenabung: int
{
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum SkorPeriodePaceklik: int
{
@@ -0,0 +1,32 @@
<?php
namespace App\Enums\SosialEkonomi;
enum StatusKartuTani: string
{
case BELUM_PUNYA = 'belum_punya';
case SUDAH_PUNYA_BELUM_AKTIF = 'sudah_punya_belum_aktif';
case SUDAH_PUNYA_AKTIF_DIGUNAKAN = 'sudah_punya_aktif_digunakan';
public function label(): string
{
return match ($this) {
self::BELUM_PUNYA => 'Belum punya',
self::SUDAH_PUNYA_BELUM_AKTIF => 'Sudah punya tapi belum aktif',
self::SUDAH_PUNYA_AKTIF_DIGUNAKAN => 'Sudah punya & aktif digunakan',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum StatusKepemilikanLahanSosialEkonomi: string
{
@@ -1,6 +1,6 @@
<?php
namespace App\Enums;
namespace App\Enums\SosialEkonomi;
enum StatusKonflik: string
{
@@ -0,0 +1,32 @@
<?php
namespace App\Enums\SosialEkonomi;
enum StatusProgramKeuanganFormal: string
{
case SEDANG_MENERIMA = 'sedang_menerima';
case PERNAH_TAPI_TIDAK_LAGI = 'pernah_tapi_tidak_lagi';
case BELUM_PERNAH = 'belum_pernah';
public function label(): string
{
return match ($this) {
self::SEDANG_MENERIMA => 'Sedang Menerima',
self::PERNAH_TAPI_TIDAK_LAGI => 'Pernah tapi Tidak Lagi',
self::BELUM_PERNAH => 'Belum Pernah',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Enums\SosialEkonomi;
enum StatusRekeningTabungan: int
{
case TIDAK_PUNYA_REKENING = 1;
case PUNYA_TIDAK_AKTIF = 2;
case PUNYA_KADANG_DIGUNAKAN = 3;
case PUNYA_CUKUP_RUTIN = 4;
case AKTIF_RUTIN_DIGUNAKAN = 5;
public function label(): string
{
return match ($this) {
self::TIDAK_PUNYA_REKENING => 'Tidak punya rekening',
self::PUNYA_TIDAK_AKTIF => 'Punya tapi tidak aktif',
self::PUNYA_KADANG_DIGUNAKAN => 'Punya, kadang digunakan',
self::PUNYA_CUKUP_RUTIN => 'Punya, cukup rutin',
self::AKTIF_RUTIN_DIGUNAKAN => 'Aktif & rutin digunakan',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Enums\SosialEkonomi;
enum TekananJualSegera: string
{
case TIDAK_ADA = 'tidak_ada';
case ADA = 'ada';
public function label(): string
{
return match ($this) {
self::TIDAK_ADA => 'Tidak ada',
self::ADA => 'Ada',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Enums\SosialEkonomi;
enum TerdaftarKelompokTani: string
{
case YA = 'ya';
case TIDAK = 'tidak';
public function label(): string
{
return match ($this) {
self::YA => 'Ya',
self::TIDAK => 'Tidak',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Enums\SosialEkonomi;
enum UsahaMutuTambahanHarga: string
{
case SEBANDING_COCOK_DICOBA = 'sebanding_cocok_dicoba';
case TIDAK_YAKIN = 'tidak_yakin';
case TIDAK_SEBANDING_TERLALU_BANYAK_USAHA = 'tidak_sebanding_terlalu_banyak_usaha';
public function label(): string
{
return match ($this) {
self::SEBANDING_COCOK_DICOBA => 'Sebanding-cocok dicoba',
self::TIDAK_YAKIN => 'Tidak yakin',
self::TIDAK_SEBANDING_TERLALU_BANYAK_USAHA => 'Tidak sebanding-terlalu banyak usaha',
};
}
public static function options(): array
{
return collect(self::cases())->map(fn ($case) => [
'value' => $case->value,
'label' => $case->label(),
])->toArray();
}
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
@@ -13,7 +13,7 @@ use App\Http\Resources\UserResource;
class AuthController extends Controller
{
public function login(Request $request): JSONResponse
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => ['required', 'email'],
@@ -27,6 +27,13 @@ class AuthController extends Controller
}
$user = Auth::user();
if ($user->status === 'pending') {
return response()->json([
'message' => 'Akun Anda masih berstatus pending. Silakan tunggu verifikasi dari Super Admin.'
], 403);
}
$user->tokens()->delete();
$token = $user->createToken('mobile-token')->plainTextToken;
@@ -37,14 +44,13 @@ class AuthController extends Controller
]);
}
public function signup(Request $request): JSONResponse
public function signup(Request $request): JsonResponse
{
$credentials = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'username' => ['required', 'string', 'max:255'],
'role' => ['string'],
'uid' => ['required','string', 'unique:users'],
]);
@@ -53,35 +59,27 @@ class AuthController extends Controller
'email' => $credentials['email'],
'password' => Hash::make($credentials['password']),
'username' => $credentials['username'],
'role' => $credentials['role'],
'uid' => $credentials['uid'],
'role' => null,
'status' => 'pending',
]);
$profile = Profile::create([
'nama' => $credentials['name'],
'email' => $credentials['email'],
'role' => $credentials['role'],
'user_uid' => $credentials['uid'],
'user_id' => $user->id,
'created_by' => $user->id,
'updated_by' => $user->id,
'role' => null,
]);
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Email dan/atau password tidak sesuai'
], 401);
}
$user = Auth::user();
$user->tokens()->delete();
$token = $user->createToken('mobile-token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
'message' => 'Registrasi berhasil. Akun Anda berstatus pending dan sedang menunggu verifikasi Super Admin.'
], 201);
}
public function me(Request $request): JSONResponse
@@ -16,7 +16,7 @@ use App\Enums\StatusLahan;
class LahanController extends Controller
{
/**
/**
* Display a listing of the resource.
*/
public function index(Request $request): AnonymousResourceCollection
@@ -97,11 +97,12 @@ class LahanController extends Controller
'nama' => ['required', 'string', 'max:255'],
'status_kepemilikan' => ['required', new Enum(StatusLahan::class)],
'desa_kelurahan_id' => ['nullable', 'numeric', 'exists:master_desa_kelurahan,id'],
'path' => ['nullable', 'array'],
'path.*' => ['array'],
'path' => ['nullable', 'string'],
'luas_lahan' => ['nullable', 'numeric'],
'uid' => ['required', 'string'],
'profile_id' => $profileRule,
'foto' => ['nullable', 'array'],
'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], // Max 5MB
]);
$targetProfileId = $user->role === 'fasilitator'
@@ -120,10 +121,22 @@ class LahanController extends Controller
? $profile->desa_kelurahan_id
: ($validated['desa_kelurahan_id'] ?? $profile->desa_kelurahan_id);
$fotoPaths = [];
if ($request->hasFile('foto')) {
foreach ($request->file('foto') as $file) {
$fotoPaths[] = $file->store('foto_lahan', 'public');
}
}
$lahan = Lahan::create([
...$validated,
'profile_id' => $profile->id,
'nama' => $validated['nama'],
'status_kepemilikan' => $validated['status_kepemilikan'],
'desa_kelurahan_id' => $desaKelurahanId,
'path' => $validated['path'] ?? null,
'luas_lahan' => $validated['luas_lahan'] ?? null,
'uid' => $validated['uid'],
'profile_id' => $profile->id,
'foto' => $fotoPaths,
]);
return (new LahanResource($lahan))
@@ -153,7 +166,7 @@ class LahanController extends Controller
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id): JsonResponse
public function update(Request $request, string $id): JsonResponse
{
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
abort(403);
@@ -173,9 +186,26 @@ class LahanController extends Controller
'path' => ['nullable', 'array'],
'path.*' => ['array'],
'luas_lahan' => ['nullable', 'numeric'],
'foto' => ['nullable', 'array'],
'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'],
]);
$lahan->update($validated);
$fotoPaths = $lahan->foto ?? [];
if ($request->hasFile('foto')) {
foreach ($request->file('foto') as $file) {
$fotoPaths[] = $file->store('foto_lahan', 'public');
}
}
$lahan->update([
'nama' => $validated['nama'],
'status_kepemilikan' => $validated['status_kepemilikan'],
'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? $lahan->desa_kelurahan_id,
'path' => $validated['path'] ?? null,
'luas_lahan' => $validated['luas_lahan'] ?? null,
'foto' => $fotoPaths,
]);
return (new LahanResource($lahan))->response();
}
@@ -0,0 +1,580 @@
<?php
namespace App\Http\Controllers\Web\Bumdes;
use App\Enums\Bumdes\JabatanBumdes;
use App\Enums\Pendidikan;
use App\Http\Controllers\Controller;
use App\Models\Bumdes\KelembagaanAktorBumdes;
use App\Models\Bumdes\PengurusBumdes;
use App\Models\Wilayah\Provinsi;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class PengurusBumdesController extends Controller
{
public function index(Request $request)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$query = PengurusBumdes::with([
...$this->wilayahRelationsWithoutProgramScope(),
'creator:id,name',
])
->orderBy('created_at', 'desc');
if ($request->search) {
$query->where(function ($query) use ($request) {
$query->where('nama', 'like', '%' . $request->search . '%')
->orWhere('jabatan', 'like', '%' . $request->search . '%')
->orWhere('jabatan_lainnya', 'like', '%' . $request->search . '%');
});
}
if (auth()->user()->role->value === 'fasilitator') {
$query->where('created_by', auth()->id());
}
$summaryQuery = clone $query;
$totalPengurus = (clone $summaryQuery)->count();
$totalLakiLaki = (clone $summaryQuery)->where('jenis_kelamin', true)->count();
$totalPerempuan = (clone $summaryQuery)->where('jenis_kelamin', false)->count();
$totalDesaKelurahan = (clone $summaryQuery)->distinct('desa_kelurahan_id')->count('desa_kelurahan_id');
$totalJabatan = (clone $summaryQuery)->distinct('jabatan')->count('jabatan');
$pengurus = $query->paginate($request->per_page ?? 10);
$userRole = auth()->user()->role->value;
$showEnumerator = in_array($userRole, ['super_admin', 'admin']);
return view('bumdes.profil.index', compact(
'pengurus',
'totalPengurus',
'totalLakiLaki',
'totalPerempuan',
'totalDesaKelurahan',
'totalJabatan',
'showEnumerator'
));
}
public function create()
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$provinsi = $this->provinsiOptions();
$jabatan = JabatanBumdes::options();
$pendidikan = Pendidikan::options();
return view('bumdes.profil.add', compact('provinsi', 'jabatan', 'pendidikan'));
}
public function store(Request $request)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$validatedData = $this->validatedData($request);
$pengurus = PengurusBumdes::create([
...$this->formatData($validatedData),
'uid' => 'BMD-' . strtoupper(Str::random(10)),
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil ditambahkan.');
}
public function show(string $id)
{
return redirect()->route('bumdes.edit', $id);
}
public function edit(string $id)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$pengurus = $this->pengurusQuery()
->with($this->wilayahRelationsWithoutProgramScope())
->findOrFail($id);
$provinsi = $this->provinsiOptions();
$jabatan = JabatanBumdes::options();
$pendidikan = Pendidikan::options();
return view('bumdes.profil.edit', compact('pengurus', 'provinsi', 'jabatan', 'pendidikan'));
}
public function update(Request $request, string $id)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$pengurus = $this->pengurusQuery()->findOrFail($id);
$validatedData = $this->validatedData($request);
$pengurus->update([
...$this->formatData($validatedData),
'updated_by' => auth()->id(),
]);
return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil diupdate.');
}
public function destroy(string $id)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$pengurus = $this->pengurusQuery()->findOrFail($id);
$pengurus->update(['deleted_by' => auth()->id()]);
$pengurus->delete();
return response()->json(['message' => 'Data pengurus BUMDes berhasil dihapus.']);
}
public function kelembagaanAktor(string $id)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$pengurus = $this->pengurusQuery()
->with([
...$this->wilayahRelationsWithoutProgramScope(),
'kelembagaanAktor',
])
->findOrFail($id);
$kelembagaanAktor = $pengurus->kelembagaanAktor;
return view('bumdes.kelembagaan-aktor.index', array_merge(
compact('pengurus', 'kelembagaanAktor'),
$this->kelembagaanAktorOptions()
));
}
public function storeKelembagaanAktor(Request $request, string $id)
{
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$pengurus = $this->pengurusQuery()->findOrFail($id);
$validatedData = $this->validatedKelembagaanAktorData($request);
KelembagaanAktorBumdes::updateOrCreate(
['pengurus_bumdes_id' => $pengurus->id],
[
...$this->formatKelembagaanAktorData($validatedData),
'created_by' => $pengurus->kelembagaanAktor?->created_by ?? auth()->id(),
'updated_by' => auth()->id(),
]
);
return redirect()
->route('bumdes.kelembagaan-aktor.index', $pengurus->id)
->with('success', 'Data kelembagaan dan aktor BUMDes berhasil disimpan.');
}
private function pengurusQuery()
{
$query = PengurusBumdes::query();
if (auth()->user()->role->value === 'fasilitator') {
$query->where('created_by', auth()->id());
}
return $query;
}
private function provinsiOptions()
{
return Provinsi::withoutGlobalScope('program')
->select('id', 'kode', 'nama')
->orderBy('kode', 'asc')
->get();
}
private function wilayahRelationsWithoutProgramScope(): array
{
return [
'desaKelurahan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kecamatan_id'),
'desaKelurahan.kecamatan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kabupaten_kota_id'),
'desaKelurahan.kecamatan.kabupatenKota' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'provinsi_id'),
'desaKelurahan.kecamatan.kabupatenKota.provinsi' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama'),
];
}
private function validatedData(Request $request): array
{
return $request->validate([
'nama' => ['required', 'string', 'max:255'],
'jenis_kelamin' => ['required', 'boolean'],
'tempat_lahir' => ['required', 'string', 'max:255'],
'tanggal_lahir' => ['required', 'date_format:d-m-Y'],
'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)],
'alamat' => ['required', 'string', 'max:500'],
'desa_kelurahan_id' => ['required', 'exists:master_desa_kelurahan,id'],
'nomor_telepon' => ['nullable', 'digits_between:11,13'],
'jabatan' => ['required', Rule::enum(JabatanBumdes::class)],
'jabatan_lainnya' => ['nullable', 'required_if:jabatan,' . JabatanBumdes::LAINNYA->value, 'string', 'max:255'],
'lama_menjabat_tahun' => ['required', 'integer', 'min:0', 'max:80'],
], [
'nama.required' => 'Nama harus diisi.',
'jenis_kelamin.required' => 'Jenis kelamin harus dipilih.',
'tempat_lahir.required' => 'Tempat lahir harus diisi.',
'tanggal_lahir.required' => 'Tanggal lahir harus diisi.',
'tanggal_lahir.date_format' => 'Tanggal lahir harus menggunakan format tanggal yang valid.',
'pendidikan_terakhir.required' => 'Pendidikan terakhir harus dipilih.',
'alamat.required' => 'Alamat harus diisi.',
'desa_kelurahan_id.required' => 'Desa kelurahan harus dipilih.',
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.',
'jabatan.required' => 'Jabatan/peran harus dipilih.',
'jabatan_lainnya.required_if' => 'Jabatan lainnya harus diisi.',
'lama_menjabat_tahun.required' => 'Lama menjabat harus diisi.',
'lama_menjabat_tahun.integer' => 'Lama menjabat harus berupa angka tahun.',
]);
}
private function formatData(array $data): array
{
$data['tanggal_lahir'] = Carbon::createFromFormat('d-m-Y', $data['tanggal_lahir'])->format('Y-m-d');
$data['tanggal_mulai_menjabat'] = Carbon::now()
->subYears((int) $data['lama_menjabat_tahun'])
->format('Y-m-d');
if (($data['jabatan'] ?? null) !== JabatanBumdes::LAINNYA->value) {
$data['jabatan_lainnya'] = null;
}
return $data;
}
private function validatedKelembagaanAktorData(Request $request): array
{
$this->normalizeKelembagaanAktorInput($request);
$maxYear = now()->year + 1;
return $request->validate([
'status_badan_hukum' => ['nullable', Rule::in(array_keys($this->statusBadanHukumOptions()))],
'nomor_dasar_hukum' => ['nullable', 'string', 'max:255'],
'tahun_dasar_hukum' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
'status_keaktifan' => ['nullable', Rule::in(array_keys($this->statusKeaktifanOptions()))],
'alasan_tidak_aktif' => ['nullable', 'string'],
'unit_usaha' => ['nullable', 'array'],
'unit_usaha.*' => ['string', Rule::in(array_keys($this->unitUsahaOptions()))],
'unit_usaha_jasa_lainnya' => ['nullable', 'string', 'max:255'],
'status_unit_komoditas' => ['nullable', Rule::in(array_keys($this->statusUnitKomoditasOptions()))],
'nama_unit_komoditas' => ['nullable', 'string', 'max:255'],
'status_ad_art' => ['nullable', Rule::in(array_keys($this->statusAdArtOptions()))],
'tahun_ad_art' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
'pengurus_inti' => ['nullable', 'array'],
'pengurus_inti.*.jabatan' => ['nullable', 'string', 'max:255'],
'pengurus_inti.*.nama' => ['nullable', 'string', 'max:255'],
'pengurus_inti.*.aktif' => ['nullable', 'boolean'],
'memiliki_staf' => ['nullable', 'boolean'],
'jumlah_staf' => ['nullable', 'integer', 'min:0'],
'unit_usaha_staf' => ['nullable', 'string'],
'modal_awal' => ['nullable', 'integer', 'min:0'],
'tahun_modal_awal' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
'pernah_penambahan_modal' => ['nullable', 'boolean'],
'penambahan_modal' => ['nullable', 'array'],
'penambahan_modal.*.tahun' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
'penambahan_modal.*.jumlah' => ['nullable', 'integer', 'min:0'],
'aset_produktif' => ['nullable', 'array'],
'aset_produktif.*.aset' => ['nullable', 'string', 'max:255'],
'aset_produktif.*.dipilih' => ['nullable', 'boolean'],
'aset_produktif.*.jumlah' => ['nullable', 'string', 'max:255'],
'aset_produktif.*.kondisi' => ['nullable', 'string', 'max:255'],
'aset_produktif_lainnya' => ['nullable', 'string', 'max:255'],
'status_kepemilikan_aset' => ['nullable', Rule::in(array_keys($this->statusKepemilikanAsetOptions()))],
'infrastruktur_pascapanen' => ['nullable', 'array'],
'infrastruktur_pascapanen.*.infrastruktur' => ['nullable', 'string', 'max:255'],
'infrastruktur_pascapanen.*.dipilih' => ['nullable', 'boolean'],
'infrastruktur_pascapanen.*.jumlah' => ['nullable', 'string', 'max:255'],
'infrastruktur_pascapanen.*.kondisi' => ['nullable', Rule::in(array_keys($this->kondisiInfrastrukturOptions()))],
'infrastruktur_pascapanen_lainnya' => ['nullable', 'string', 'max:255'],
], $this->kelembagaanAktorValidationMessages(), $this->kelembagaanAktorValidationAttributes());
}
private function normalizeKelembagaanAktorInput(Request $request): void
{
foreach (['modal_awal'] as $field) {
if ($request->has($field)) {
$request->merge([$field => preg_replace('/\D/', '', (string) $request->input($field))]);
}
}
$penambahanModal = collect($request->input('penambahan_modal', []))
->map(function ($row) {
if (isset($row['jumlah'])) {
$row['jumlah'] = preg_replace('/\D/', '', (string) $row['jumlah']);
}
return $row;
})
->all();
if ($request->has('penambahan_modal')) {
$request->merge(['penambahan_modal' => $penambahanModal]);
}
}
private function formatKelembagaanAktorData(array $data): array
{
$payload = array_merge([
'status_badan_hukum' => null,
'nomor_dasar_hukum' => null,
'tahun_dasar_hukum' => null,
'status_keaktifan' => null,
'alasan_tidak_aktif' => null,
'unit_usaha' => [],
'unit_usaha_jasa_lainnya' => null,
'status_unit_komoditas' => null,
'nama_unit_komoditas' => null,
'status_ad_art' => null,
'tahun_ad_art' => null,
'pengurus_inti' => [],
'memiliki_staf' => null,
'jumlah_staf' => null,
'unit_usaha_staf' => null,
'modal_awal' => null,
'tahun_modal_awal' => null,
'pernah_penambahan_modal' => null,
'penambahan_modal' => [],
'aset_produktif' => [],
'aset_produktif_lainnya' => null,
'status_kepemilikan_aset' => null,
'infrastruktur_pascapanen' => [],
'infrastruktur_pascapanen_lainnya' => null,
], $data);
$payload['unit_usaha'] = array_values($payload['unit_usaha'] ?? []);
$payload['pengurus_inti'] = $this->filterRows($payload['pengurus_inti'] ?? [], ['nama', 'aktif']);
$payload['penambahan_modal'] = $this->filterRows($payload['penambahan_modal'] ?? [], ['tahun', 'jumlah']);
$payload['aset_produktif'] = $this->filterRows($payload['aset_produktif'] ?? [], ['dipilih', 'jumlah', 'kondisi']);
$payload['infrastruktur_pascapanen'] = $this->filterRows($payload['infrastruktur_pascapanen'] ?? [], ['dipilih', 'jumlah', 'kondisi']);
if (($payload['status_keaktifan'] ?? null) !== 'tidak_aktif_vakum') {
$payload['alasan_tidak_aktif'] = null;
}
if (! in_array('jasa_lainnya', $payload['unit_usaha'], true)) {
$payload['unit_usaha_jasa_lainnya'] = null;
}
if (($payload['status_unit_komoditas'] ?? null) !== 'ya') {
$payload['nama_unit_komoditas'] = null;
}
if (($payload['status_ad_art'] ?? null) !== 'ada_lengkap') {
$payload['tahun_ad_art'] = null;
}
if (! (bool) ($payload['memiliki_staf'] ?? false)) {
$payload['jumlah_staf'] = null;
$payload['unit_usaha_staf'] = null;
}
if (! (bool) ($payload['pernah_penambahan_modal'] ?? false)) {
$payload['penambahan_modal'] = null;
}
if (! collect($payload['aset_produktif'] ?? [])->contains(fn ($row) => ($row['aset'] ?? null) === 'lainnya')) {
$payload['aset_produktif_lainnya'] = null;
}
if (! collect($payload['infrastruktur_pascapanen'] ?? [])->contains(fn ($row) => ($row['infrastruktur'] ?? null) === 'lainnya')) {
$payload['infrastruktur_pascapanen_lainnya'] = null;
}
return $payload;
}
private function filterRows(array $rows, array $valueKeys): array
{
return collect($rows)
->map(function ($row) {
return collect($row)
->map(fn ($value) => $value === '' ? null : $value)
->all();
})
->filter(function ($row) use ($valueKeys) {
foreach ($valueKeys as $key) {
if (filled($row[$key] ?? null)) {
return true;
}
}
return false;
})
->values()
->all();
}
private function kelembagaanAktorOptions(): array
{
return [
'statusBadanHukumOptions' => $this->statusBadanHukumOptions(),
'statusKeaktifanOptions' => $this->statusKeaktifanOptions(),
'unitUsahaOptions' => $this->unitUsahaOptions(),
'statusUnitKomoditasOptions' => $this->statusUnitKomoditasOptions(),
'statusAdArtOptions' => $this->statusAdArtOptions(),
'pengurusIntiRows' => $this->pengurusIntiRows(),
'asetProduktifRows' => $this->asetProduktifRows(),
'statusKepemilikanAsetOptions' => $this->statusKepemilikanAsetOptions(),
'infrastrukturPascapanenRows' => $this->infrastrukturPascapanenRows(),
'kondisiInfrastrukturOptions' => $this->kondisiInfrastrukturOptions(),
];
}
private function statusBadanHukumOptions(): array
{
return [
'sudah_berbadan_hukum' => 'Sudah berbadan hukum (terdaftar Kemenkumham)',
'proses_pendaftaran' => 'Proses pendaftaran',
'belum_berbadan_hukum' => 'Belum berbadan hukum',
'tidak_tahu' => 'Tidak tahu',
];
}
private function statusKeaktifanOptions(): array
{
return [
'aktif_penuh' => 'Aktif beroperasi penuh',
'aktif_terbatas' => 'Aktif tapi terbatas (sebagian unit usaha berjalan)',
'tidak_aktif_vakum' => 'Tidak aktif/vakum',
'berdiri_belum_beroperasi' => 'Sudah berdiri tapi belum pernah beroperasi',
];
}
private function unitUsahaOptions(): array
{
return [
'simpan_pinjam' => 'Simpan pinjam',
'perdagangan_toko_desa' => 'Perdagangan/toko desa',
'pengelolaan_air_bersih' => 'Pengelolaan air bersih',
'wisata_desa_agrowisata' => 'Wisata desa/agrowisata',
'pengelolaan_hasil_pertanian_komoditas' => 'Pengelolaan hasil pertanian/komoditas',
'penyewaan_alat_aset' => 'Penyewaan alat/aset',
'jasa_lainnya' => 'Jasa lainnya',
];
}
private function statusUnitKomoditasOptions(): array
{
return [
'ya' => 'Ya, ada unit khusus',
'ditangani_pengurus_inti' => 'Tidak ada unit khusus, ditangani langsung pengurus inti',
'direncanakan' => 'Belum ada, masih direncanakan',
];
}
private function statusAdArtOptions(): array
{
return [
'ada_lengkap' => 'Ada, lengkap',
'ada_tidak_lengkap' => 'Ada tapi tidak lengkap',
'tidak_ada' => 'Tidak ada',
'tidak_tahu' => 'Tidak tahu',
];
}
private function pengurusIntiRows(): array
{
return [
'penasihat_kepala_desa' => 'Penasihat (Kepala Desa)',
'ketua_pengawas' => 'Ketua Pengawas',
'direktur_ketua_bumdes' => 'Direktur/Ketua BUMDes',
'sekretaris' => 'Sekretaris',
'bendahara' => 'Bendahara',
'kepala_unit_usaha_komoditas' => 'Kepala Unit Usaha Komoditas',
'lainnya' => 'Lainnya',
];
}
private function asetProduktifRows(): array
{
return [
'bangunan_kantor' => 'Bangunan / kantor',
'gudang_penyimpanan' => 'Gudang penyimpanan',
'kendaraan_operasional_angkut' => 'Kendaraan operasional/angkut',
'lahan_usaha' => 'Lahan usaha',
'alat_pengolahan_komoditas' => 'Alat pengolahan komoditas',
'lainnya' => 'Lainnya',
];
}
private function statusKepemilikanAsetOptions(): array
{
return [
'milik_bumdes_sendiri' => 'Milik BUMDes sendiri',
'milik_desa_dipinjamkan_dikelola' => 'Milik desa (dipinjamkan/dikelola)',
'sewa_pinjam_pihak_lain' => 'Sewa/pinjam pihak lain',
];
}
private function infrastrukturPascapanenRows(): array
{
return [
'gudang_penyimpanan_hasil_panen' => 'Gudang penyimpanan hasil panen',
'alat_mesin_pengering' => 'Alat/mesin pengering',
'mesin_pulper_huller' => 'Mesin pulper/huller',
'timbangan' => 'Timbangan',
'alat_sortasi' => 'Alat sortasi',
'lainnya' => 'Lainnya',
];
}
private function kondisiInfrastrukturOptions(): array
{
return [
'baik_berfungsi' => 'Baik dan berfungsi',
'rusak_tidak_terpakai' => 'Rusak/tidak terpakai',
];
}
private function kelembagaanAktorValidationMessages(): array
{
return [
'integer' => ':attribute harus berupa angka.',
'digits' => ':attribute harus terdiri dari :digits digit.',
'min' => ':attribute tidak boleh kurang dari :min.',
'max' => ':attribute tidak valid.',
'in' => ':attribute tidak valid.',
'array' => ':attribute harus berupa daftar data.',
'boolean' => ':attribute harus dipilih.',
];
}
private function kelembagaanAktorValidationAttributes(): array
{
return [
'status_badan_hukum' => 'Status badan hukum',
'nomor_dasar_hukum' => 'Nomor dasar hukum',
'tahun_dasar_hukum' => 'Tahun dasar hukum',
'status_keaktifan' => 'Status keaktifan',
'unit_usaha' => 'Unit usaha',
'status_unit_komoditas' => 'Unit usaha komoditas',
'status_ad_art' => 'Dokumen AD/ART',
'memiliki_staf' => 'Status staf/karyawan',
'jumlah_staf' => 'Jumlah staf',
'modal_awal' => 'Modal awal',
'tahun_modal_awal' => 'Tahun modal awal',
'pernah_penambahan_modal' => 'Status penambahan modal',
'status_kepemilikan_aset' => 'Status kepemilikan aset',
];
}
}
@@ -0,0 +1,509 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\PendapatanKomoditas;
use App\Models\Pertanian\SosialEkonomi;
class DemografiPetaniController extends Controller
{
public function index(Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
return redirect()->route('dashboard.karakteristik-petani', array_merge($request->query(), [
'tab' => 'demografi-petani',
]));
}
public function karakteristik(Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$activeTab = $request->query('tab') === 'indeks-petani'
? 'indeks-petani'
: 'demografi-petani';
$demografiData = $this->contentData($request);
$indeksData = app(IndeksPetaniController::class)->contentData($request);
$tableFilters = [
'gender' => ['Gender', ['1' => 'Laki-laki', '0' => 'Perempuan']],
'umur' => ['Kelompok Umur', ['kurang_30' => '< 30 tahun', '30_39' => '30–39 tahun', '40_49' => '40–49 tahun', '50_59' => '50–59 tahun', '60_keatas' => '≥ 60 tahun']],
'pendidikan' => ['Pendidikan', ['tidak_sekolah' => 'Tidak Sekolah', 'sd' => 'SD', 'smp' => 'SMP', 'sma' => 'SMA', 'diploma' => 'Diploma', 's1_keatas' => 'Sarjana ke atas']],
'jumlah_tanggungan' => ['Jumlah Tanggungan', ['0' => '0 orang', '1_2' => '1–2 orang', '3_4' => '3–4 orang', '5_keatas' => '≥ 5 orang']],
'kelompok_tani' => ['Keanggotaan Kelompok Tani', ['terdaftar' => 'Terdaftar', 'tidak_terdaftar' => 'Tidak Terdaftar', 'belum_terisi' => 'Belum Terisi']],
'komoditas' => ['Komoditas', $indeksData['komoditas']->pluck('nama', 'id')->all()],
'luas_lahan' => ['Luas Lahan', ['kurang_05' => '< 0,5 ha', '05_1' => '0,5–1 ha', '1_2' => '> 1–2 ha', 'lebih_2' => '> 2 ha']],
'status_kepemilikan_lahan' => ['Status Kepemilikan Lahan', ['sendiri' => 'Milik Pribadi', 'sewa' => 'Sewa', 'adat' => 'Tanah Adat']],
'penghasilan' => ['Rentang Pendapatan', ['kurang_1' => '< Rp1 juta', '1_3' => 'Rp1–3 juta', '3_5' => '> Rp3–5 juta', '5_10' => '> Rp5–10 juta', 'lebih_10' => '> Rp10 juta']],
];
return view('dashboard.karakteristik-petani', [
'activeTab' => $activeTab,
'tableFilters' => $tableFilters,
'dashboardFilters' => $request->only([
'provinsi_id',
'kabupatenKota_id',
'kecamatan_id',
'desaKelurahan_id',
'komoditas_id',
'segmen',
]),
'indeksFilterAction' => route('dashboard.karakteristik-petani'),
] + $demografiData + $indeksData);
}
public function contentData(Request $request): array
{
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
$petaniCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)->selectRaw("
COUNT(*) as total,
COUNT(*) FILTER (WHERE jenis_kelamin = true) as laki,
COUNT(*) FILTER (WHERE jenis_kelamin = false) as perempuan
")->first();
$totalPetani = $petaniCounts->total;
$petaniLakilaki = $petaniCounts->laki;
$petaniPerempuan = $petaniCounts->perempuan;
$totalKomoditas = Komoditas::query()
->where('is_utama', true)
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
->count();
$totalLuasLahan = Lahan::has('petani')
->whereIn('petani_id', clone $filteredPetaniIds)
->sum('luas_lahan');
$ratarataLuasLahan = $totalLuasLahan && $totalPetani ? $totalLuasLahan / $totalPetani : 0;
$umurCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
->whereNotNull('tanggal_lahir')->selectRaw("
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) < 30) as kurang_30,
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 30 AND 39) as umur_30_39,
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 40 AND 49) as umur_40_49,
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 50 AND 59) as umur_50_59,
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60) as umur_60_keatas
")->first();
$pendidikanCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
->whereNotNull('pendidikan_terakhir')
->select('pendidikan_terakhir', DB::raw('COUNT(*) as total'))
->groupBy('pendidikan_terakhir')
->pluck('total', 'pendidikan_terakhir');
$pendidikan = [
'tidak_sekolah' => (int) ($pendidikanCounts['01_tidak_sekolah'] ?? 0),
'sd' => (int) ($pendidikanCounts['02_sd'] ?? 0),
'smp' => (int) ($pendidikanCounts['03_smp'] ?? 0),
'sma' => (int) ($pendidikanCounts['04_sma'] ?? 0),
'diploma' => (int) ($pendidikanCounts['05_diploma'] ?? 0),
's1_keatas' => (int) ($pendidikanCounts['06_sarjana'] ?? 0) + (int) ($pendidikanCounts['07_magister'] ?? 0),
];
$jumlahTanggunganBuckets = SosialEkonomi::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->whereNotNull('jumlah_tanggungan')
->selectRaw("
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan = 0) as tanggungan_0,
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 1 AND 2) as tanggungan_1_2,
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 3 AND 4) as tanggungan_3_4,
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan >= 5) as tanggungan_5_keatas
")
->first();
$jumlahTanggungan = [
'0' => (int) ($jumlahTanggunganBuckets->tanggungan_0 ?? 0),
'1_2' => (int) ($jumlahTanggunganBuckets->tanggungan_1_2 ?? 0),
'3_4' => (int) ($jumlahTanggunganBuckets->tanggungan_3_4 ?? 0),
'5_keatas' => (int) ($jumlahTanggunganBuckets->tanggungan_5_keatas ?? 0),
];
$kelompokTaniBuckets = Petani::query()
->whereIn('petani.id', clone $filteredPetaniIds)
->leftJoin('sosial_ekonomi', function ($join) {
$join->on('sosial_ekonomi.petani_id', '=', 'petani.id')
->whereNull('sosial_ekonomi.deleted_at');
})
->selectRaw("
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'ya') as terdaftar,
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'tidak') as tidak_terdaftar,
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani IS NULL OR sosial_ekonomi.terdaftar_kelompok_tani NOT IN ('ya', 'tidak')) as belum_terisi
")
->first();
$kelompokTani = [
'terdaftar' => (int) ($kelompokTaniBuckets->terdaftar ?? 0),
'tidak_terdaftar' => (int) ($kelompokTaniBuckets->tidak_terdaftar ?? 0),
'belum_terisi' => (int) ($kelompokTaniBuckets->belum_terisi ?? 0),
];
$desaSasaranRows = Petani::query()
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
->whereIn('petani.id', clone $filteredPetaniIds)
->selectRaw('petani.desa_kelurahan_id as desa_id')
->selectRaw("COALESCE(desa.nama, 'Tidak diketahui') as desa")
->selectRaw('COUNT(*) as total')
->groupBy('petani.desa_kelurahan_id', 'desa.nama')
->orderByDesc('total')
->orderBy('desa')
->get();
$komoditasUtama = Komoditas::query()
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
->withCount(['petani' => fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds)])
->orderByDesc('petani_count')
->get(['id', 'nama']);
$luasLahanBuckets = DB::query()->fromSub(
Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->select('petani_id')
->selectRaw('luas_lahan'),
'agg'
)->selectRaw("
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan < 0.5) as kurang_05,
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan >= 0.5 AND luas_lahan <= 1) as luas_05_1,
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 1 AND luas_lahan <= 2) as luas_1_2,
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 2) as lebih_2
")->first();
$luasLahan = [
'kurang_05' => $luasLahanBuckets->kurang_05,
'05_1' => $luasLahanBuckets->luas_05_1,
'1_2' => $luasLahanBuckets->luas_1_2,
'lebih_2' => $luasLahanBuckets->lebih_2,
];
$statusKepemilikanLahanRows = Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->whereNotNull('status_kepemilikan')
->selectRaw('status_kepemilikan as status')
->selectRaw('COUNT(DISTINCT petani_id) as total')
->groupBy('status_kepemilikan')
->orderByDesc('total')
->get();
$pendapatanPerPetaniQuery = PendapatanKomoditas::query()
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
->whereNotNull('sosial_ekonomi.petani_id')
->whereIn('sosial_ekonomi.petani_id', clone $filteredPetaniIds)
->groupBy('sosial_ekonomi.petani_id')
->selectRaw('SUM(pendapatan_komoditas.pendapatan) as total_pendapatan');
$penghasilanBuckets = DB::query()->fromSub(
clone $pendapatanPerPetaniQuery,
'agg'
)->selectRaw("
AVG(total_pendapatan) as rata_rata,
COUNT(*) FILTER (WHERE total_pendapatan < 1000000) as kurang_1,
COUNT(*) FILTER (WHERE total_pendapatan >= 1000000 AND total_pendapatan <= 3000000) as p1_3,
COUNT(*) FILTER (WHERE total_pendapatan > 3000000 AND total_pendapatan <= 5000000) as p3_5,
COUNT(*) FILTER (WHERE total_pendapatan > 5000000 AND total_pendapatan <= 10000000) as p5_10,
COUNT(*) FILTER (WHERE total_pendapatan > 10000000) as lebih_10
")->first();
$ratarataPenghasilan = (float) ($penghasilanBuckets->rata_rata ?? 0);
$penghasilan = [
'kurang_1' => $penghasilanBuckets->kurang_1,
'1_3' => $penghasilanBuckets->p1_3,
'3_5' => $penghasilanBuckets->p3_5,
'5_10' => $penghasilanBuckets->p5_10,
'lebih_10' => $penghasilanBuckets->lebih_10,
];
$data = [
'total_petani' => $totalPetani,
'petani_lakilaki' => $petaniLakilaki,
'petani_perempuan' => $petaniPerempuan,
'total_komoditas' => $totalKomoditas,
'total_luas_lahan' => $totalLuasLahan,
'ratarata_luas_lahan' => $ratarataLuasLahan,
'ratarata_penghasilan' => $ratarataPenghasilan,
'kelompok_umur' => [
'kurang_30' => $umurCounts->kurang_30,
'30_39' => $umurCounts->umur_30_39,
'40_49' => $umurCounts->umur_40_49,
'50_59' => $umurCounts->umur_50_59,
'60_keatas' => $umurCounts->umur_60_keatas,
],
'pendidikan' => $pendidikan,
'jumlah_tanggungan' => $jumlahTanggungan,
'kelompok_tani' => $kelompokTani,
'desa_sasaran' => [
'total' => $desaSasaranRows->count(),
'ids' => $desaSasaranRows->pluck('desa_id')->values(),
'labels' => $desaSasaranRows->pluck('desa')->values(),
'series' => $desaSasaranRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
],
'komoditas_utama' => [
'ids' => $komoditasUtama->pluck('id')->values(),
'labels' => $komoditasUtama->pluck('nama')->values(),
'series' => $komoditasUtama->pluck('petani_count')->map(fn ($v) => (int) $v)->values(),
],
'luas_lahan' => $luasLahan,
'status_kepemilikan_lahan' => [
'keys' => $statusKepemilikanLahanRows->pluck('status')->values(),
'labels' => $statusKepemilikanLahanRows
->pluck('status')
->map(fn ($status) => $this->statusLahanLabel((string) $status))
->values(),
'series' => $statusKepemilikanLahanRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
],
'penghasilan' => $penghasilan,
];
return compact('data');
}
private function filteredPetaniIdsQuery(Request $request)
{
$query = Petani::query()
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
->leftJoin('master_kecamatan as kecamatan', 'desa.kecamatan_id', '=', 'kecamatan.id')
->leftJoin('master_kabupaten_kota as kabupaten', 'kecamatan.kabupaten_kota_id', '=', 'kabupaten.id')
->when($request->filled('provinsi_id'), fn ($query) => $query->where('kabupaten.provinsi_id', $request->provinsi_id))
->when($request->filled('kabupatenKota_id'), fn ($query) => $query->where('kecamatan.kabupaten_kota_id', $request->kabupatenKota_id))
->when($request->filled('kecamatan_id'), fn ($query) => $query->where('desa.kecamatan_id', $request->kecamatan_id))
->when($request->filled('desaKelurahan_id'), fn ($query) => $query->where('petani.desa_kelurahan_id', $request->desaKelurahan_id))
->when($request->filled('komoditas_id'), fn ($query) => $query->where('petani.komoditas_id', $request->komoditas_id));
if ($request->filled('segmen')) {
$query->whereHas('skor', function ($query) use ($request) {
match ($request->segmen) {
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)
->where('skor_kesiapan_adopsi', '<', 4),
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)
->where('skor_kesiapan_adopsi', '<', 3),
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)
->where('skor_kesiapan_adopsi', '<', 2),
'Belum Lengkap' => $query->where(function ($query) {
$query->whereNull('skor_kesiapan_adopsi')
->orWhere('skor_kesiapan_adopsi', '<', 1);
}),
default => null,
};
});
}
return $query->select('petani.id');
}
private function statusLahanLabel(string $status): string
{
return match ($status) {
'sendiri' => 'Milik Pribadi',
'sewa' => 'Sewa',
'adat' => 'Tanah Adat',
default => ucwords(str_replace('_', ' ', $status)),
};
}
public function data(Request $request)
{
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
$pendapatanSub = PendapatanKomoditas::query()
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
->whereColumn('sosial_ekonomi.petani_id', 'petani.id')
->selectRaw('SUM(pendapatan_komoditas.pendapatan)');
$query = Petani::with('komoditas')
->whereIn('petani.id', clone $filteredPetaniIds)
->withSum('lahans as total_luas_lahan', 'luas_lahan')
->addSelect(['total_pendapatan' => $pendapatanSub]);
if ($request->filled('search')) {
$query->where('nama', 'ILIKE', "%{$request->search}%");
}
if ($request->has('gender') && $request->gender !== '') {
$query->where('jenis_kelamin', $request->gender);
}
if ($request->filled('umur')) {
match ($request->umur) {
'kurang_30' => $query->whereNotNull('tanggal_lahir')
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) < 30"),
'30_39' => $query->whereNotNull('tanggal_lahir')
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 30 AND 39"),
'40_49' => $query->whereNotNull('tanggal_lahir')
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 40 AND 49"),
'50_59' => $query->whereNotNull('tanggal_lahir')
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 50 AND 59"),
'60_keatas' => $query->whereNotNull('tanggal_lahir')
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60"),
default => null,
};
}
if ($request->filled('pendidikan')) {
$pendidikanMap = [
'tidak_sekolah' => ['01_tidak_sekolah'],
'sd' => ['02_sd'],
'smp' => ['03_smp'],
'sma' => ['04_sma'],
'diploma' => ['05_diploma'],
's1_keatas' => ['06_sarjana', '07_magister'],
];
if (isset($pendidikanMap[$request->pendidikan])) {
$query->whereIn('pendidikan_terakhir', $pendidikanMap[$request->pendidikan]);
}
}
if ($request->filled('jumlah_tanggungan')) {
$tanggunganQuery = SosialEkonomi::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->whereNotNull('jumlah_tanggungan')
->select('petani_id')
->distinct();
match ($request->jumlah_tanggungan) {
'0' => $tanggunganQuery->where('jumlah_tanggungan', 0),
'1_2' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [1, 2]),
'3_4' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [3, 4]),
'5_keatas' => $tanggunganQuery->where('jumlah_tanggungan', '>=', 5),
default => null,
};
$query->whereIn('petani.id', $tanggunganQuery);
}
if ($request->filled('kelompok_tani')) {
match ($request->kelompok_tani) {
'terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->where('terdaftar_kelompok_tani', 'ya')
->select('petani_id')
->distinct()),
'tidak_terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->where('terdaftar_kelompok_tani', 'tidak')
->select('petani_id')
->distinct()),
'belum_terisi' => $query->whereNotIn('petani.id', SosialEkonomi::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->whereIn('terdaftar_kelompok_tani', ['ya', 'tidak'])
->select('petani_id')
->distinct()),
default => null,
};
}
if ($request->filled('komoditas')) {
$query->where('komoditas_id', $request->komoditas);
}
if ($request->filled('penghasilan')) {
[$min, $max] = match ($request->penghasilan) {
'kurang_1' => [null, 999999],
'1_3' => [1000000, 3000000],
'3_5' => [3000001, 5000000],
'5_10' => [5000001, 10000000],
'lebih_10' => [10000001, null],
default => [null, null],
};
$query->whereIn('id', function ($sub) use ($min, $max) {
$sub->from('pendapatan_komoditas')
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
->whereNotNull('sosial_ekonomi.petani_id')
->groupBy('sosial_ekonomi.petani_id')
->select('sosial_ekonomi.petani_id')
->when($min !== null, fn ($q) => $q->havingRaw('SUM(pendapatan_komoditas.pendapatan) >= ?', [$min]))
->when($max !== null, fn ($q) => $q->havingRaw('SUM(pendapatan_komoditas.pendapatan) <= ?', [$max]));
});
}
if ($request->filled('luas_lahan')) {
$lahanQuery = Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->select('petani_id')
->distinct();
match ($request->luas_lahan) {
'kurang_05' => $lahanQuery->where('luas_lahan', '<', 0.5),
'05_1' => $lahanQuery->whereBetween('luas_lahan', [0.5, 1]),
'1_2' => $lahanQuery->where('luas_lahan', '>', 1)->where('luas_lahan', '<=', 2),
'lebih_2' => $lahanQuery->where('luas_lahan', '>', 2),
default => null,
};
$query->whereIn('petani.id', $lahanQuery);
}
if ($request->filled('status_kepemilikan_lahan')) {
$query->whereIn('petani.id', Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->where('status_kepemilikan', $request->status_kepemilikan_lahan)
->select('petani_id')
->distinct());
}
$daftarPetani = $query->orderBy('id')->paginate(10);
$pendidikanLabels = [
'01_tidak_sekolah' => 'Tidak Sekolah',
'02_sd' => 'SD',
'03_smp' => 'SMP',
'04_sma' => 'SMA',
'05_diploma' => 'Diploma',
'06_sarjana' => 'Sarjana',
'07_magister' => 'Magister',
];
$rows = collect($daftarPetani->items())->map(function ($petani) use ($pendidikanLabels) {
$umur = $petani->tanggal_lahir ? \Carbon\Carbon::parse($petani->tanggal_lahir)->age : null;
$pendidikanRaw = $petani->getRawOriginal('pendidikan_terakhir');
$pendapatan = $petani->total_pendapatan;
$rentangPenghasilan = match (true) {
$pendapatan === null => '-',
$pendapatan < 1000000 => '< Rp1 juta',
$pendapatan <= 3000000 => 'Rp1 - 3 juta',
$pendapatan <= 5000000 => 'Rp3 - 5 juta',
$pendapatan <= 10000000 => 'Rp5 - 10 juta',
default => '> Rp10 juta',
};
return [
'id' => $petani->id,
'nama' => $petani->nama ?? '-',
'jenis_kelamin' => $petani->jenis_kelamin,
'gender' => $petani->jenis_kelamin == 1 ? 'Laki-laki' : 'Perempuan',
'umur' => $umur,
'komoditas' => $petani->komoditas?->nama ?? '-',
'luas_lahan' => (float) ($petani->total_luas_lahan ?? 0),
'pendidikan' => $pendidikanLabels[$pendidikanRaw] ?? '-',
'rentang_penghasilan' => $rentangPenghasilan,
];
});
return response()->json([
'data' => $rows,
'pagination' => [
'current_page' => $daftarPetani->currentPage(),
'last_page' => $daftarPetani->lastPage(),
'per_page' => $daftarPetani->perPage(),
'total' => $daftarPetani->total(),
'from' => $daftarPetani->firstItem(),
'to' => $daftarPetani->lastItem(),
],
]);
}
}
@@ -0,0 +1,590 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Http\JsonResponse;
use Illuminate\Database\Eloquent\Builder;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\SkorPetani;
class IndeksPetaniController extends Controller
{
private function filters(Request $request): array
{
return [
'desaKelurahan_id' => $request->desaKelurahan_id,
'kecamatan_id' => $request->kecamatan_id,
'kabupatenKota_id' => $request->kabupatenKota_id,
'provinsi_id' => $request->provinsi_id,
'komoditas_id' => $request->komoditas_id,
'segmen' => $request->segmen,
];
}
private function petaniQuery(array $filters): Builder
{
$petaniQuery = Petani::query()
->leftJoin('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
->leftJoin('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
->leftJoin('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
->leftJoin('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.id')
->whereNull('petani.deleted_at')
->when($filters['provinsi_id'], fn($q, $id) => $q->where('master_kabupaten_kota.provinsi_id', $id))
->when($filters['kabupatenKota_id'], fn($q, $id) => $q->where('master_kecamatan.kabupaten_kota_id', $id))
->when($filters['kecamatan_id'], fn($q, $id) => $q->where('master_desa_kelurahan.kecamatan_id', $id))
->when($filters['desaKelurahan_id'], fn($q, $id) => $q->where('petani.desa_kelurahan_id', $id))
->when($filters['komoditas_id'], fn($q, $id) => $q->where('petani.komoditas_id', $id));
if ($filters['segmen']) {
$petaniQuery->whereHas('skor', fn (Builder $query) => $this->applySegmentFilter($query, $filters['segmen']));
}
return $petaniQuery;
}
private function skorPetaniQuery(array $filters, array $komoditas): Builder
{
$skor = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->leftJoin('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
->leftJoin('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
->leftJoin('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
->leftJoin('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.id')
->whereNull('petani.deleted_at')
->when($filters['provinsi_id'], fn($q, $id) => $q->where('master_kabupaten_kota.provinsi_id', $id))
->when($filters['kabupatenKota_id'], fn($q, $id) => $q->where('master_kecamatan.kabupaten_kota_id', $id))
->when($filters['kecamatan_id'], fn($q, $id) => $q->where('master_desa_kelurahan.kecamatan_id', $id))
->when($filters['desaKelurahan_id'], fn($q, $id) => $q->where('petani.desa_kelurahan_id', $id))
->whereIn('petani.komoditas_id', $komoditas);
return $this->applySegmentFilter($skor, $filters['segmen']);
}
private function applySegmentFilter(Builder $query, ?string $segment): Builder
{
$ranges = [
'Penggerak' => [4, null],
'Perintis' => [3, 4],
'Pemerhati' => [2, 3],
'Pemula' => [1, 2],
];
if (isset($ranges[$segment])) {
[$min, $max] = $ranges[$segment];
return $query->where('skor_kesiapan_adopsi', '>=', $min)
->when($max !== null, fn (Builder $query) => $query->where('skor_kesiapan_adopsi', '<', $max));
}
if ($segment === 'Belum Lengkap') {
$query->where(fn (Builder $query) => $query->whereNull('skor_kesiapan_adopsi')
->orWhere('skor_kesiapan_adopsi', '<', 1));
}
return $query;
}
public function index(Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
return redirect()->route('dashboard.karakteristik-petani', array_merge($request->query(), [
'tab' => 'indeks-petani',
]));
}
public function contentData(Request $request): array
{
$filters = $this->filters($request);
$segments = $this->segments();
$colors = collect($segments)
->pluck('color')
->map(fn($c) => "var(--bs-{$c})")
->values();
$komoditas = Komoditas::where('is_utama', true)->get(['id', 'nama']);
$petaniQuery = $this->petaniQuery(array_fill_keys(array_keys($filters), null));
$desaKelurahan = $petaniQuery->clone()
->selectRaw("
master_desa_kelurahan.id as desa_kelurahan_id,
master_desa_kelurahan.nama as desa_kelurahan,
master_kecamatan.id as kecamatan_id,
master_kecamatan.nama as kecamatan,
master_kabupaten_kota.id as kabupaten_id,
master_kabupaten_kota.nama as kabupaten,
master_provinsi.id as provinsi_id,
master_provinsi.nama as provinsi
")
->whereNotNull('master_provinsi.id')
->distinct()
->get();
$provinsi = $desaKelurahan
->unique('provinsi_id')
->sortBy('provinsi')
->values();
$wilayah = $desaKelurahan
->groupBy('provinsi_id')
->map(function ($provinsi) {
return [
'id' => $provinsi->first()->provinsi_id,
'nama' => $provinsi->first()->provinsi,
'kabupaten' => $provinsi
->groupBy('kabupaten_id')
->map(function ($kabupaten) {
return [
'id' => $kabupaten->first()->kabupaten_id,
'nama' => $kabupaten->first()->kabupaten,
'kecamatan' => $kabupaten
->groupBy('kecamatan_id')
->map(function ($kecamatan) {
return [
'id' => $kecamatan->first()->kecamatan_id,
'nama' => $kecamatan->first()->kecamatan,
'desa' => $kecamatan->map(function ($desa) {
return [
'id' => $desa->desa_kelurahan_id,
'nama' => $desa->desa_kelurahan,
];
})->values(),
];
})->values(),
];
})->values(),
];
})->values();
return array_merge($filters,
compact(
'komoditas',
'desaKelurahan',
'provinsi',
'wilayah',
'segments',
'colors',
));
}
public function summary(Request $request): JsonResponse
{
$filters = $this->filters($request);
$petaniQuery = $this->petaniQuery($filters);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$counts = $petaniQuery->clone()
->selectRaw('COUNT(*) as petani')
->selectRaw('COUNT(DISTINCT petani.komoditas_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as komoditas')
->selectRaw('COUNT(DISTINCT petani.desa_kelurahan_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as desa')
->selectRaw('COUNT(DISTINCT master_kecamatan.id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as kecamatan')
->first();
$filteredPetaniIds = $petaniQuery->clone()->select('petani.id');
$lahan = Lahan::query()
->whereIn('petani_id', $filteredPetaniIds)
->whereIn('komoditas_id', $komoditas->pluck('id'))
->selectRaw('COALESCE(SUM(luas_lahan), 0) as total, AVG(luas_lahan) as rata_rata')
->first();
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$avgSkor = $skorQuery->clone()->avg('skor_kesiapan_adopsi');
if ($avgSkor >= 4.0) {
$avgSegmen = 'Penggerak';
} elseif ($avgSkor >= 3.0) {
$avgSegmen = 'Perintis';
} elseif ($avgSkor >= 2.0) {
$avgSegmen = 'Pemerhati';
} elseif ($avgSkor >= 1.0) {
$avgSegmen = 'Pemula';
} else {
$avgSegmen = 'Belum Lengkap';
}
return response()->json([
'petani' => (int) $counts->petani,
'komoditas' => (int) $counts->komoditas,
'sum_lahan' => (float) $lahan->total,
'avg_lahan' => (float) $lahan->rata_rata,
'desa' => (int) $counts->desa,
'kecamatan' => (int) $counts->kecamatan,
'skor' => $avgSkor,
'segmen' => $avgSegmen
]);
}
private function segments(): array
{
$segments = [
[
'name' => 'Penggerak',
'range' => '4.0 - 5.0',
'icon' => 'tabler-crown',
'color' => 'success',
],
[
'name' => 'Perintis',
'range' => '3.0 - 3.9',
'icon' => 'tabler-flag',
'color' => 'info',
],
[
'name' => 'Pemerhati',
'range' => '2.0 - 2.9',
'icon' => 'tabler-eye',
'color' => 'warning',
],
[
'name' => 'Pemula',
'range' => '1.0 - 1.9',
'icon' => 'tabler-seedling',
'color' => 'danger',
],
];
return $segments;
}
private function cases(): string
{
$case = "
CASE
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
ELSE 'Belum Lengkap'
END
";
return $case;
}
public function segmen(Request $request): JsonResponse
{
$filters = $this->filters($request);
$segments = $this->segments();
$case = $this->cases();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$countSegmentasi = $skorQuery->clone()
->selectRaw("$case AS segmen, COUNT(*) AS total")
->groupByRaw($case)
->get();
$series = collect($segments)
->map(function ($segment) use ($countSegmentasi) {
return $countSegmentasi
->where('segmen', $segment['name'])
->sum('total');
})
->values();
$countPetani = $series->sum();
$persentase = collect($segments)
->map(function ($segment) use ($countSegmentasi, $countPetani) {
$total = $countSegmentasi
->where('segmen', $segment['name'])
->sum('total');
return $countPetani > 0
? ($total / $countPetani) * 100
: 0;
})
->values();
return response()->json([
'series' => $series,
'persentase' => $persentase,
]);
}
public function segmenByKomoditas(Request $request): JsonResponse
{
$filters = $this->filters($request);
$segments = $this->segments();
$case = $this->cases();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$countSegmentasiByKomoditas = $skorQuery->clone()
->selectRaw("
petani.komoditas_id,
$case AS segmen,
COUNT(petani.id) AS total
")
->groupBy('petani.komoditas_id')
->groupByRaw($case)
->get();
$seriesSegmentasiKomoditas = collect($segments)->map(function ($segment) use ($countSegmentasiByKomoditas, $komoditas) {
return [
'name' => $segment['name'],
'data' => $komoditas->map(function ($item) use ($countSegmentasiByKomoditas, $segment) {
return optional(
$countSegmentasiByKomoditas->first(function ($row) use ($item, $segment) {
return $row->komoditas_id == $item->id
&& $row->segmen == $segment['name'];
})
)->total ?? 0;
})->values(),
];
})->values();
return response()->json([
'categories' => $komoditas->pluck('nama')->values(),
'series' => $seriesSegmentasiKomoditas,
]);
}
public function dimensi(Request $request): JsonResponse
{
$filters = $this->filters($request);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$avgDimensi = $skorQuery->clone()
->selectRaw("
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
->first();
return response()->json([
'radarDimensi' => [
round($avgDimensi->skor_d2_sistem_praktik_budidaya, 2),
round($avgDimensi->skor_d4_sosial_ekonomi, 2),
round($avgDimensi->skor_d6_rantai_nilai_akses_pasar, 2),
]
]);
}
public function komponen(Request $request): JsonResponse
{
$filters = $this->filters($request);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$avgKomponen = $skorQuery->clone()
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
->first();
return response()->json([
'radarKomponen' => [
round($avgKomponen->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2),
round($avgKomponen->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2),
round($avgKomponen->skor_komp_3_d2_praktik_budidaya_gap, 2),
round($avgKomponen->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2),
round($avgKomponen->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2),
round($avgKomponen->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2)
]
]);
}
private function villageData(array $filters, bool $includePath = true): \Illuminate\Support\Collection
{
$case = $this->cases();
$petaniQuery = $this->petaniQuery($filters);
$desaKelurahan = $petaniQuery->clone()
->selectRaw("
master_desa_kelurahan.id as desa_kelurahan_id,
master_desa_kelurahan.nama as desa_kelurahan,
master_kecamatan.id as kecamatan_id,
master_kecamatan.nama as kecamatan,
master_kabupaten_kota.id as kabupaten_id,
master_kabupaten_kota.nama as kabupaten,
master_provinsi.id as provinsi_id,
master_provinsi.nama as provinsi
")
->when($includePath, fn (Builder $query) => $query->addSelect('master_desa_kelurahan.path as path'))
->distinct()
->get();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
$mapSegmentasi = $skorQuery->clone()
->selectRaw("petani.desa_kelurahan_id,
$case AS segmen,
COUNT(petani.id) AS total
")
->groupBy('petani.desa_kelurahan_id')
->groupByRaw($case)
->get();
$mapAvgSkorSegmentasi = $skorQuery->clone()
->selectRaw("petani.desa_kelurahan_id,
COUNT(petani.id) AS jumlah_petani,
avg(skor_kesiapan_adopsi) AS skor,
CASE
WHEN AVG(skor_kesiapan_adopsi) >= 4 THEN 'Penggerak'
WHEN AVG(skor_kesiapan_adopsi) >= 3 THEN 'Perintis'
WHEN AVG(skor_kesiapan_adopsi) >= 2 THEN 'Pemerhati'
WHEN AVG(skor_kesiapan_adopsi) >= 1 THEN 'Pemula'
ELSE 'Belum Lengkap'
END AS segmen
")
->groupBy('petani.desa_kelurahan_id')
->get();
$segmentasiGroups = $mapSegmentasi->groupBy('desa_kelurahan_id');
$jumlahSegmenByDesa = $segmentasiGroups
->map(fn ($items) => $items->pluck('total', 'segmen'));
$segmenDominan = $segmentasiGroups
->map(function ($items) {
return $items->reduce(fn ($largest, $item) => $largest === null || $item->total > $largest->total ? $item : $largest);
});
$mapAvgSkorSegmentasi->each(function ($item) use ($segmenDominan, $jumlahSegmenByDesa) {
$dominan = $segmenDominan->get($item->desa_kelurahan_id);
$item->segmen_dominan = $dominan?->segmen;
$item->jumlah_dominan = $dominan?->total;
$item->jumlah_segmen = $jumlahSegmenByDesa->get($item->desa_kelurahan_id, collect());
});
$segmentasiByDesa = $mapAvgSkorSegmentasi->keyBy('desa_kelurahan_id');
$desaLookup = $desaKelurahan->keyBy('desa_kelurahan_id');
$mapDesaSegmentasi = $segmentasiByDesa->map(function ($segmentasi, $desaId) use ($desaLookup) {
$desa = $desaLookup->get($desaId);
if (!$desa) {
return null;
}
$desa->segmentasi = $segmentasi;
return $desa;
})->filter()->values();
return $mapDesaSegmentasi;
}
public function map(Request $request): JsonResponse
{
return response()->json($this->villageData($this->filters($request)));
}
public function desa(Request $request): JsonResponse
{
$segmentStyles = collect($this->segments())->keyBy('name');
$mapDesaSegmentasi = $this->villageData($this->filters($request), false);
$segmentNames = ['Pemula', 'Perintis', 'Pemerhati', 'Penggerak', 'Belum Lengkap'];
$rows = $mapDesaSegmentasi->map(function ($desa) use ($segmentStyles, $segmentNames) {
$total = (int) $desa->segmentasi->jumlah_petani;
$style = $segmentStyles[$desa->segmentasi->segmen] ?? ['color' => 'secondary'];
$styleDominan = $segmentStyles[$desa->segmentasi->segmen_dominan] ?? ['color' => 'secondary'];
return [
'desa' => $desa->desa_kelurahan,
'jumlah_petani' => $desa->segmentasi->jumlah_petani,
'segmen' => $desa->segmentasi->segmen,
'skor' => $desa->segmentasi->skor,
'segmen_dominan' => $desa->segmentasi->segmen_dominan,
'jumlah_dominan' => $desa->segmentasi->jumlah_dominan,
'color' => $style['color'],
'color_dominan' => $styleDominan['color'],
'segments' => collect($segmentNames)
->mapWithKeys(function ($name) use ($desa, $segmentStyles, $total) {
$count = (int) $desa->segmentasi->jumlah_segmen->get($name, 0);
return [strtolower(str_replace(' ', '_', $name)) => [
'count' => $count,
'percentage' => $total > 0 ? round($count / $total * 100, 2) : 0,
'color' => $segmentStyles[$name]['color'] ?? 'secondary',
]];
})->all(),
];
});
return response()->json($rows);
}
public function data(Request $request): JsonResponse
{
$filters = $this->filters($request);
$segments = $this->segments();
$segmentStyles = collect($segments)->keyBy('name');
$petaniQuery = $this->petaniQuery($filters);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$daftarPetani = $petaniQuery->clone()
->select('petani.*')
->with(['desaKelurahan', 'komoditas', 'skor'])
->withSum('lahans as total_luas_lahan', 'luas_lahan')
->whereIn('komoditas_id', $komoditas->pluck('id'))
->get();
$data = $daftarPetani
->map(function ($item) use ($segmentStyles) {
$segmen = $item->skor
? $item->segmen()
: null;
$style = $segmentStyles[$segmen]
?? ['color' => 'secondary'];
return [
'id' => $item->id,
'nama' => $item->nama,
'desa' => $item->desaKelurahan?->nama,
'desa_kelurahan_id' => $item->desa_kelurahan_id,
'komoditas' => $item->komoditas?->nama,
'luas_lahan' => (float) ($item->total_luas_lahan ?? 0),
'skor' => $item->skor
? $item->skor->skor_kesiapan_adopsi
: null,
'segmen' => $segmen,
'color' => $style['color'],
'url' => route('dashboard.profil-petani', $item->id),
];
})
->values();
return response()->json($data);
}
}
@@ -0,0 +1,769 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Enums\KeparahanSerangan;
use App\Enums\PenyakitTanaman;
use App\Enums\PraktikBudidaya\PemberianPupuk;
use App\Enums\SeranganHama;
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
use App\Http\Controllers\Controller;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\Produksi;
use App\Models\Pertanian\SkorPetani;
use App\Models\Pertanian\SosialEkonomi;
use App\Models\Pertanian\SurveyProduktivitas;
use App\Models\Wilayah\DesaKelurahan;
use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class IndikatorPertanianController extends Controller
{
public function index(Request $request)
{
$kabupatenOptions = $this->kabupatenOptions();
$ngadaOption = collect($kabupatenOptions)->first(
fn ($option) => str_contains(strtolower($option['label']), 'ngada')
);
$initialFilters = $this->selectedFilters($request, $ngadaOption['value'] ?? 'all');
$totalPetani = $this->applyPetaniFilters(Petani::query(), $initialFilters)->count();
$totalLuasLahan = ((float) $this->applyLahanFilters(Lahan::query(), $initialFilters)->sum('luas_lahan')) / 10000;
$rataRataLuasLahan = $totalPetani > 0
? $totalLuasLahan / $totalPetani
: 0;
$totalProduksiKg = $this->totalProduksiKg($initialFilters);
$rataRataProduksiKg = $totalPetani > 0
? $totalProduksiKg / $totalPetani
: 0;
$rataRataProduktivitas = $totalLuasLahan > 0
? $totalProduksiKg / $totalLuasLahan
: 0;
$filterOptions = [
[
'key' => 'kabupaten',
'label' => 'Kabupaten',
'options' => $kabupatenOptions,
'icon' => 'tabler-map-pin',
'readonly' => true,
],
[
'key' => 'kecamatan',
'label' => 'Kecamatan',
'options' => $this->kecamatanOptions(),
],
[
'key' => 'desa',
'label' => 'Desa',
'options' => $this->desaOptions(),
],
[
'key' => 'komoditas',
'label' => 'Komoditas',
'options' => $this->komoditasOptions(),
],
[
'key' => 'segmentasi',
'label' => 'Segmentasi',
'options' => $this->segmentasiOptions(),
],
];
$summaryData = [
[
'value' => number_format($totalPetani, 0, ',', '.'),
'label' => 'Petani Terdata',
'description' => 'Total petani yang masuk sistem',
'icon' => 'tabler-users',
'variant' => 'primary',
],
[
'value' => number_format($totalLuasLahan, 2, ',', '.').' ha',
'label' => 'Total Luas Lahan',
'description' => 'Rata-rata '.number_format($rataRataLuasLahan, 2, ',', '.').' ha/petani',
'icon' => 'tabler-mountain',
'variant' => 'success',
],
[
'value' => number_format($totalProduksiKg, 0, ',', '.').' kg',
'label' => 'Total Produksi (Semua Komoditas)',
'description' => 'Rata-rata '.number_format($rataRataProduksiKg, 0, ',', '.').' kg/petani',
'icon' => 'tabler-wheat',
'variant' => 'warning',
],
[
'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha',
'label' => 'Rata-rata Produktivitas',
'description' => 'Semua komoditas berdasarkan total produksi dan luas lahan',
'icon' => 'tabler-chart-bar',
'variant' => 'info',
],
];
return view('dashboard.indikator-pertanian', [
'title' => 'Indikator Pertanian',
'initialFilters' => $initialFilters,
'filterOptions' => $filterOptions,
'summaryData' => $summaryData,
'chartData' => [
'petaniPerKomoditas' => $this->petaniPerKomoditasChart($initialFilters),
'luasLahanPerKecamatan' => $this->luasLahanPerKecamatanChart($initialFilters),
'kondisiTanaman' => $this->kondisiTanamanChart($initialFilters),
'statusKepemilikanLahan' => $this->statusKepemilikanLahanChart($initialFilters),
'seranganHama' => $this->seranganHamaChart($initialFilters),
'penyakitTanaman' => $this->penyakitTanamanChart($initialFilters),
'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters),
'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($initialFilters),
'praktikPemupukan' => $this->praktikPemupukanChart($initialFilters),
'produksiPerKomoditas' => $this->produksiPerKomoditasChart($initialFilters),
'produktivitasPerKomoditas' => $this->produktivitasPerKomoditasChart($initialFilters),
'luasPanenPerKomoditas' => $this->luasPanenPerKomoditasChart($initialFilters),
'statusPanen' => $this->statusPanenChart($initialFilters),
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($initialFilters, 'ha_tahun'),
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($initialFilters, 'pohon_tahun'),
],
]);
}
private function selectedFilters(Request $request, string $defaultKabupaten): array
{
$filters = [
'kabupaten' => $defaultKabupaten,
'kecamatan' => $this->validFilterValue($request->query('kecamatan'), Kecamatan::class),
'desa' => $this->validFilterValue($request->query('desa'), DesaKelurahan::class),
'komoditas' => $this->validFilterValue($request->query('komoditas'), Komoditas::class),
'segmentasi' => $this->validSegmentasi((string) $request->query('segmentasi', 'all')),
];
if ($filters['desa'] !== 'all') {
$desa = DesaKelurahan::query()->find($filters['desa']);
if (! $desa || ($filters['kecamatan'] !== 'all' && (string) $desa->kecamatan_id !== $filters['kecamatan'])) {
$filters['desa'] = 'all';
}
}
if ($filters['kecamatan'] === 'all') {
$filters['desa'] = 'all';
}
return $filters;
}
private function validFilterValue(mixed $value, string $modelClass): string
{
if (! $value || $value === 'all') {
return 'all';
}
return $modelClass::query()->whereKey($value)->exists()
? (string) $value
: 'all';
}
private function validSegmentasi(string $value): string
{
return in_array($value, [
'Penggerak',
'Perintis',
'Pemerhati',
'Pemula',
'Belum tersegmentasi',
'all',
], true) ? $value : 'all';
}
private function kabupatenOptions(): array
{
return $this->withAllOption(
'Semua Kabupaten',
KabupatenKota::query()
->orderBy('nama')
->get(['id', 'kode', 'nama'])
->map(fn (KabupatenKota $kabupaten) => [
'value' => (string) $kabupaten->id,
'label' => $this->kabupatenLabel($kabupaten),
])
->values()
->all()
);
}
private function kecamatanOptions(): array
{
return $this->withAllOption(
'Semua Kecamatan',
Kecamatan::query()
->orderBy('nama')
->get(['id', 'nama'])
->map(fn (Kecamatan $kecamatan) => [
'value' => (string) $kecamatan->id,
'label' => $kecamatan->nama,
])
->values()
->all()
);
}
private function desaOptions(): array
{
return $this->withAllOption(
'Semua Desa',
DesaKelurahan::query()
->orderBy('nama')
->get(['id', 'nama', 'kecamatan_id'])
->map(fn (DesaKelurahan $desa) => [
'value' => (string) $desa->id,
'label' => $desa->nama,
'parent' => (string) $desa->kecamatan_id,
])
->values()
->all()
);
}
private function komoditasOptions(): array
{
return $this->withAllOption(
'Semua Komoditas',
Komoditas::query()
->orderBy('nama')
->get(['id', 'nama'])
->map(fn (Komoditas $komoditas) => [
'value' => (string) $komoditas->id,
'label' => $komoditas->nama,
])
->values()
->all()
);
}
private function segmentasiOptions(): array
{
// Aggregate bucket counts in SQL instead of pulling every skor_kesiapan_adopsi
// value into PHP just to map/filter/unique it.
$row = SkorPetani::query()->selectRaw("
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 4) as penggerak,
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 3 AND skor_kesiapan_adopsi < 4) as perintis,
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 2 AND skor_kesiapan_adopsi < 3) as pemerhati,
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 1 AND skor_kesiapan_adopsi < 2) as pemula,
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi IS NOT NULL AND skor_kesiapan_adopsi < 1) as belum_tersegmentasi
")->first();
$order = [
'Penggerak' => $row->penggerak,
'Perintis' => $row->perintis,
'Pemerhati' => $row->pemerhati,
'Pemula' => $row->pemula,
'Belum tersegmentasi' => $row->belum_tersegmentasi,
];
$segments = collect($order)
->filter(fn ($count) => $count > 0)
->keys()
->map(fn ($segment) => [
'value' => $segment,
'label' => $segment,
])
->values()
->all();
return $this->withAllOption('Semua Segmentasi', $segments);
}
private function totalProduksiKg(array $filters): float
{
$expr = $this->kgCaseExpr('jumlah_panen', 'satuan');
return (float) ($this->applyProduksiFilters(Produksi::query(), $filters)
->whereNotNull('jumlah_panen')
->selectRaw("SUM({$expr}) as total")
->value('total') ?? 0);
}
private function petaniPerKomoditasChart(array $filters): array
{
$rows = $this->applyPetaniFilters(Petani::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'petani.komoditas_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('COUNT(petani.id) as total')
->groupBy('k.id', 'k.nama')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
];
}
private function luasLahanPerKecamatanChart(array $filters): array
{
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id')
->leftJoin('master_kecamatan as k', 'k.id', '=', 'd.kecamatan_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('SUM(map_lahan.luas_lahan) / 10000 as total')
->groupBy('k.id', 'k.nama')
->havingRaw('SUM(map_lahan.luas_lahan) > 0')
->orderByDesc('total')
->limit(8)
->get()
->filter(fn ($row) => round((float) $row->total, 2) > 0)
->values();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function kondisiTanamanChart(array $filters): array
{
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
->whereNull('map_tanaman.deleted_at')
->whereNotNull('map_tanaman.kondisi_tanaman')
->selectRaw('map_tanaman.kondisi_tanaman as label')
->selectRaw('COUNT(map_tanaman.id) as total')
->groupBy('map_tanaman.kondisi_tanaman')
->orderByDesc('total')
->get();
if ($rows->isEmpty()) {
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNull('deleted_at')
->whereNotNull('kondisi_tanaman')
->selectRaw('kondisi_tanaman as label')
->selectRaw('COUNT(map_lahan.id) as total')
->groupBy('kondisi_tanaman')
->orderByDesc('total')
->get();
}
return [
'labels' => $rows->pluck('label')->map(fn ($label) => $this->shortEnumLabel((string) $label))->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
];
}
private function seranganHamaChart(array $filters): array
{
return $this->tanamanAttributeChart(
$filters,
'serangan_hama',
fn ($label) => $this->enumLabel(SeranganHama::class, (string) $label)
);
}
private function penyakitTanamanChart(array $filters): array
{
return $this->tanamanAttributeChart(
$filters,
'penyakit_tanaman',
fn ($label) => $this->enumLabel(PenyakitTanaman::class, (string) $label)
);
}
private function keparahanSeranganChart(array $filters): array
{
return $this->tanamanAttributeChart(
$filters,
'keparahan_serangan',
fn ($label) => $this->keparahanSeranganLabel((string) $label)
);
}
private function statusKepemilikanLahanChart(array $filters): array
{
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('status_kepemilikan')
->selectRaw('status_kepemilikan as label')
->selectRaw('COUNT(map_lahan.id) as total')
->groupBy('status_kepemilikan')
->orderByDesc('total')
->get();
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
}
private function statusHakKelolaLahanChart(array $filters): array
{
$rows = $this->applySosialEkonomiFilters(SosialEkonomi::query(), $filters)
->whereNotNull('status_kepemilikan_lahan')
->selectRaw('status_kepemilikan_lahan as label')
->selectRaw('COUNT(sosial_ekonomi.id) as total')
->groupBy('status_kepemilikan_lahan')
->orderByDesc('total')
->get();
return $this->chartFromRows(
$rows,
fn ($label) => $this->enumLabel(StatusKepemilikanLahanSosialEkonomi::class, (string) $label)
);
}
private function praktikPemupukanChart(array $filters): array
{
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->whereNotNull('pemberian_pupuk')
->selectRaw('pemberian_pupuk as label')
->selectRaw('COUNT(praktik_budidaya.id) as total')
->groupBy('pemberian_pupuk')
->orderBy('pemberian_pupuk')
->get();
return $this->chartFromRows(
$rows,
fn ($label) => $this->pemberianPupukLabel((int) $label)
);
}
private function produksiPerKomoditasChart(array $filters): array
{
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->whereNotNull('jumlah_panen')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw("SUM({$expr}) as total")
->groupBy('k.id', 'k.nama')
->get()
->filter(fn ($row) => round((float) $row->total, 2) > 0)
->sortByDesc('total')
->values();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function produktivitasPerKomoditasChart(array $filters): array
{
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('AVG(produksi.produktivitas) FILTER (WHERE produksi.produktivitas > 0) as avg_prod')
->selectRaw("SUM({$expr}) as total_kg")
->selectRaw('SUM(produksi.luas_panen) as total_luas')
->groupBy('k.id', 'k.nama')
->get()
->map(function ($row) {
$avgProd = (float) $row->avg_prod;
$totalLuas = (float) $row->total_luas;
$totalKg = (float) $row->total_kg;
$value = $avgProd > 0
? $avgProd
: ($totalLuas > 0 ? $totalKg / $totalLuas : 0);
return (object) ['label' => $row->label, 'total' => round($value, 2)];
})
->filter(fn ($row) => $row->total > 0)
->sortByDesc('total')
->values();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->values()->all(),
];
}
private function luasPanenPerKomoditasChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->whereNotNull('luas_panen')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('SUM(produksi.luas_panen) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('SUM(produksi.luas_panen) > 0')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function statusPanenChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->whereNotNull('status_panen')
->selectRaw("COALESCE(status_panen, 'Belum ditentukan') as label")
->selectRaw('COUNT(produksi.id) as total')
->groupBy('status_panen')
->orderByDesc('total')
->get();
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
}
private function surveyProduktivitasByUnitChart(array $filters, string $unit): array
{
$rows = $this->applySurveyProduktivitasFilters(SurveyProduktivitas::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'survey_produktivitas.komoditas_id')
->where('satuan', $unit)
->whereNotNull('kuantitas')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('AVG(survey_produktivitas.kuantitas) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('AVG(survey_produktivitas.kuantitas) > 0')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function chartFromRows($rows, callable $labelResolver): array
{
return [
'labels' => $rows
->pluck('label')
->map(fn ($label) => $labelResolver($label))
->values()
->all(),
'series' => $rows
->pluck('total')
->map(fn ($value) => (int) $value)
->values()
->all(),
];
}
private function tanamanAttributeChart(array $filters, string $field, callable $labelResolver): array
{
$qualifiedField = 'map_tanaman.'.$field;
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
->whereNull('map_tanaman.deleted_at')
->whereNotNull($qualifiedField)
->selectRaw($qualifiedField.' as label')
->selectRaw('COUNT(map_tanaman.id) as total')
->groupBy($qualifiedField)
->orderByDesc('total')
->get();
if ($rows->isEmpty()) {
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull($field)
->selectRaw($field.' as label')
->selectRaw('COUNT(map_lahan.id) as total')
->groupBy($field)
->orderByDesc('total')
->get();
}
return $this->chartFromRows($rows, $labelResolver);
}
private function applyPetaniFilters($query, array $filters)
{
return $query
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
})
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
$query->whereHas('skor', function ($skor) use ($filters) {
$this->applySegmentasiRange($skor, $filters['segmentasi']);
});
});
}
private function applyLahanFilters($query, array $filters)
{
return $query
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
})
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
$query->whereIn('petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
});
}
private function applyProduksiFilters($query, array $filters)
{
return $query
->when($filters['kecamatan'] !== 'all' || $filters['desa'] !== 'all' || $filters['segmentasi'] !== 'all', function ($query) use ($filters) {
$query->whereHas('lahan', function ($lahan) use ($filters) {
$this->applyLahanFilters($lahan, $filters);
});
})
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
}
private function applySosialEkonomiFilters($query, array $filters)
{
return $query->whereHas('petani', function ($petani) use ($filters) {
$this->applyPetaniFilters($petani, $filters);
});
}
private function applyPraktikBudidayaFilters($query, array $filters)
{
return $query->whereHas('petani', function ($petani) use ($filters) {
$this->applyPetaniFilters($petani, $filters);
});
}
private function applySurveyProduktivitasFilters($query, array $filters)
{
return $query
->whereHas('petani', function ($petani) use ($filters) {
$this->applyPetaniFilters($petani, $filters);
})
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
}
private function applyTanamanTableFilters($query, array $filters)
{
return $query
->leftJoin('map_lahan as l', 'l.id', '=', 'map_tanaman.lahan_id')
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
$query
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'l.desa_kelurahan_id')
->where('d.kecamatan_id', $filters['kecamatan']);
})
->when($filters['desa'] !== 'all', fn ($query) => $query->where('l.desa_kelurahan_id', $filters['desa']))
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('map_tanaman.komoditas_id', $filters['komoditas']))
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
$query->where(function ($query) use ($filters) {
$query
->whereIn('map_tanaman.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']))
->orWhereIn('l.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
});
});
}
private function segmentasiPetaniIdsQuery(string $segmentasi)
{
return Petani::query()
->select('id')
->whereHas('skor', function ($skor) use ($segmentasi) {
$this->applySegmentasiRange($skor, $segmentasi);
});
}
private function applySegmentasiRange($query, string $segmentasi)
{
return match ($segmentasi) {
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)->where('skor_kesiapan_adopsi', '<', 4),
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)->where('skor_kesiapan_adopsi', '<', 3),
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)->where('skor_kesiapan_adopsi', '<', 2),
default => $query->where(function ($query) {
$query->whereNull('skor_kesiapan_adopsi')->orWhere('skor_kesiapan_adopsi', '<', 1);
}),
};
}
/**
* SQL CASE expression converting a jumlah/satuan pair to kilograms,
* so aggregation happens in the database instead of pulling every
* row of `produksi` into PHP to sum with toKilograms().
*/
private function kgCaseExpr(string $jumlahColumn, string $satuanColumn): string
{
return "CASE LOWER({$satuanColumn})
WHEN 'ton' THEN {$jumlahColumn} * 1000
WHEN 'kuintal' THEN {$jumlahColumn} * 100
WHEN 'kwintal' THEN {$jumlahColumn} * 100
ELSE {$jumlahColumn}
END";
}
private function shortEnumLabel(string $value): string
{
return str($value)
->replace('_', ' ')
->headline()
->toString();
}
private function enumLabel(string $enumClass, int|string $value): string
{
$case = $enumClass::tryFrom($value);
if (! $case) {
return $this->shortEnumLabel((string) $value);
}
return method_exists($case, 'label')
? $case->label()
: $this->shortEnumLabel((string) $case->value);
}
private function keparahanSeranganLabel(string $value): string
{
return match (KeparahanSerangan::tryFrom($value)) {
KeparahanSerangan::RINGAN => 'Ringan',
KeparahanSerangan::SEDANG => 'Sedang',
KeparahanSerangan::BERAT => 'Parah',
default => $this->shortEnumLabel($value),
};
}
private function pemberianPupukLabel(int $value): string
{
return match (PemberianPupuk::tryFrom($value)) {
PemberianPupuk::TIDAK_PERNAH => 'Tidak pernah',
PemberianPupuk::JENIS_DOSIS_WAKTU_TIDAK_MENENTU => 'Tidak menentu',
PemberianPupuk::JENIS_SESUAI_DOSIS_TIDAK_MENENTU => 'Jenis sesuai',
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT => 'Cukup tepat',
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_TEPAT => 'Tepat & rutin',
default => $this->shortEnumLabel((string) $value),
};
}
private function toKilograms(float $value, string $unit): float
{
return match (strtolower($unit)) {
'ton' => $value * 1000,
'kuintal', 'kwintal' => $value * 100,
default => $value,
};
}
private function withAllOption(string $label, array $options): array
{
return array_merge([
[
'value' => 'all',
'label' => $label,
],
], $options);
}
private function kabupatenLabel(KabupatenKota $kabupaten): string
{
return preg_replace('/^(kabupaten|kota)\s+/i', '', $kabupaten->nama);
}
private function segmentLabel(float $score): string
{
return match (true) {
$score >= 4.0 => 'Penggerak',
$score >= 3.0 => 'Perintis',
$score >= 2.0 => 'Pemerhati',
$score >= 1.0 => 'Pemula',
default => 'Belum tersegmentasi',
};
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\DB;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Tanaman;
use App\Models\Pertanian\SkorPetani;
class ProfilPetaniController extends Controller
{
public function index()
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$title = 'Dashboard Profil Petani';
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
$komoditasIds = $komoditas->pluck('id');
$petaniIds = fn () => Petani::query()->select('id');
$lahanIds = fn () => Lahan::query()
->whereIn('petani_id', $petaniIds())
->select('id');
$petaniSummary = Petani::select(
'komoditas_id',
DB::raw('COUNT(*) as total')
)
->groupBy('komoditas_id')
->get();
$lahanSummary = Lahan::select(
'komoditas_id',
DB::raw('COUNT(*) as total'),
DB::raw('SUM(luas_lahan) as luas_lahan')
)
->whereIn('petani_id', $petaniIds())
->groupBy('komoditas_id')
->get();
$tanamanSummary = Tanaman::select(
'komoditas_id',
DB::raw('COUNT(*) as total')
)
->whereIn('lahan_id', $lahanIds())
->groupBy('komoditas_id')
->get();
$case = "
CASE
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
ELSE 'Belum Lengkap'
END
";
$skorSummary = SkorPetani::selectRaw("$case AS segmen, COUNT(*) AS total")
->whereIn('petani_id', $petaniIds())
->groupByRaw($case)
->get();
$skorKomoditasSummary = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->selectRaw("
petani.komoditas_id,
$case AS segmen,
COUNT(petani.id) AS total
")
->whereIn('petani_skor.petani_id', $petaniIds())
->groupBy('petani.komoditas_id')
->groupByRaw($case)
->get();
$skorAverage = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->whereIn('petani_id', $petaniIds())
->whereIn('petani.komoditas_id', $komoditasIds)
->avg('skor_kesiapan_adopsi');
if ($skorAverage >= 4.0) {
$segmenAverage = 'Penggerak';
} elseif ($skorAverage >= 3.0) {
$segmenAverage = 'Perintis';
} elseif ($skorAverage >= 2.0) {
$segmenAverage = 'Pemerhati';
} elseif ($skorAverage >= 1.0) {
$segmenAverage = 'Pemula';
} else {
$segmenAverage = 'Belum Lengkap';
}
$skorDimensi = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->whereIn('petani_id', $petaniIds())
->whereIn('petani.komoditas_id', $komoditasIds)
->selectRaw("
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
->first();
$skorKomponen = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->whereIn('petani_id', $petaniIds())
->whereIn('petani.komoditas_id', $komoditasIds)
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
->first();
$skorMultiDimensi = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->whereIn('petani_id', $petaniIds())
->whereIn('petani.komoditas_id', $komoditasIds)
->selectRaw("$case AS segmen,
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
->groupByRaw($case)
->get();
$skorMultiKomponen = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->whereIn('petani_id', $petaniIds())
->whereIn('petani.komoditas_id', $komoditasIds)
->selectRaw("$case AS segmen,
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
->groupByRaw($case)
->get();
$petani = Petani::query()
->select('id', 'nama', 'jenis_kelamin', 'tanggal_lahir', 'komoditas_id')
->with([
'komoditas:id,nama',
'skor:id,petani_id,skor_kesiapan_adopsi,skor_d2_sistem_praktik_budidaya,skor_d4_sosial_ekonomi,skor_d6_rantai_nilai_akses_pasar,skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,skor_komp_2_d4_k2_ekonomi_rumah_tangga,skor_komp_3_d2_praktik_budidaya_gap,skor_komp_4_d4_k4_keterlibatan_kelembagaan,skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar',
])
->whereIn('komoditas_id', $komoditasIds)
->orderBy('nama')
->get();
return view('dashboard.petani', compact('title',
'komoditas',
'petaniSummary',
'lahanSummary',
'tanamanSummary',
'skorSummary',
'skorKomoditasSummary',
'segmenAverage',
'skorAverage',
'skorDimensi',
'skorKomponen',
'skorMultiDimensi',
'skorMultiKomponen',
'petani',
));
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,523 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Models\User;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Tanaman;
use App\Models\Pertanian\SurveyPelatihan;
use App\Models\Pertanian\SurveySertifikasi;
use App\Enums\AksesPasar\PraktikFermentasi;
use App\Enums\AksesPasar\SaluranTerdahulu;
use App\Enums\AksesPasar\SkorPengalaman;
use BackedEnum;
class SurveyPetaniController extends Controller
{
private const GAP_ENUM_CLASS = 'App\\Enums\\PendidikanPelatihan\\PelatihanGap';
public function index()
{
$colors = $this->colors();
// Progres per enumerator
$enumerators = User::query()
->where('role', 'fasilitator')
->withCount([
'petaniDibuat as done',
])
->orderByDesc('done')
->get()
->map(function ($enumerator) use ($colors) {
$color = $colors[array_rand($colors)];
return [
'name' => $enumerator->name,
'done' => (int) $enumerator->done,
'color' => $color['color'],
'bg' => $color['bg'],
'tx' => $color['tx'],
];
})
->values();
// Tabel dan nama model
$petaniTable = (new Petani())->getTable();
$komoditasTable = (new Komoditas())->getTable();
$lahanTable = (new Lahan())->getTable();
// Distribusi petani berdasarkan komoditas
$komoditasCollection = DB::table("{$petaniTable} as p")
->leftJoin("{$komoditasTable} as k", 'k.id', '=', 'p.komoditas_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as nama")
->selectRaw('COUNT(p.id) as petani_count')
->where('k.is_utama', true)
->groupBy('k.id', 'k.nama')
->orderByDesc('petani_count')
->get();
// $totalPetani = (int) $komoditasCollection->sum('petani_count');
$totalPetani = Petani::count();
$circumference = 2 * pi() * 46;
$komoditas = $komoditasCollection
->map(function ($item, $index) use (
$colors,
$totalPetani,
$circumference
) {
$color = $colors[$index % count($colors)];
$ratio = $totalPetani > 0
? (int) $item->petani_count / $totalPetani
: 0;
return [
'label' => $item->nama,
'total' => (int) $item->petani_count,
'pct' => round($ratio * 100, 1),
'arc' => $ratio * $circumference,
'color' => $color['color'],
];
})
->values();
// Progres pengisian data per petani
$progressPetani = Petani::query()
->select(['id', 'nama', 'komoditas_id'])
->with([
'komoditas:id,nama',
'lahans',
'praktikBudidaya',
'sosialEkonomi',
'aksesPasar',
])
->orderBy('nama')
->get()
->map(function ($petani) {
$status = [
'lahan' => $this->moduleStatus(
'Lahan',
$this->hasCompleteLahan($petani)
),
'praktik_budidaya' => $this->moduleStatus(
'Praktik Budidaya',
$this->hasCompletePraktikBudidaya($petani->praktikBudidaya)
),
'sosial_ekonomi' => $this->moduleStatus(
'Sosial Ekonomi',
$this->hasCompleteSosialEkonomi($petani->sosialEkonomi)
),
'akses_pasar' => $this->moduleStatus(
'Akses Pasar',
$this->hasCompleteAksesPasar($petani->aksesPasar)
),
];
$totalModul = count($status);
$modulSelesai = collect($status)
->where('done', true)
->count();
return [
'id' => $petani->id,
'name' => $petani->nama,
'komoditas' => $petani->komoditas?->nama ?? 'Belum ditentukan',
'status' => $status,
'done' => $modulSelesai,
'total' => $totalModul,
'percent' => $totalModul > 0
? (int) round(($modulSelesai / $totalModul) * 100)
: 0,
];
})
->values();
// Ringkasan lahan
// $totalLuasLahan = ((float) Lahan::query()
// ->sum('luas_lahan')) / 10000;
$totalLuasLahan = Lahan::has('petani')->sum('luas_lahan');
// Jumlah tanaman berdasarkan komoditas petani
$tanamanTable = (new Tanaman())->getTable();
$tanamanPerKomoditasCollection = DB::table("{$tanamanTable} as t")
->join("{$lahanTable} as l", 'l.id', '=', 't.lahan_id')
->join("{$petaniTable} as p", 'p.id', '=', 'l.petani_id')
->leftJoin("{$komoditasTable} as k", 'k.id', '=', 't.komoditas_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('COUNT(t.id) as total')
->where('k.is_utama', true)
->whereNull('t.deleted_at')
->whereNull('l.deleted_at')
->whereNull('p.deleted_at')
->groupBy('k.id', 'k.nama')
->orderByDesc('total')
->get();
$totalJumlahTanaman = (int) $tanamanPerKomoditasCollection->sum('total');
$tanamanPerKomoditas = $tanamanPerKomoditasCollection
->map(function ($item, $index) use ($colors, $totalJumlahTanaman) {
$color = $colors[$index % count($colors)];
$total = (int) $item->total;
return [
'label' => $item->label,
'total' => $total,
'pct' => $totalJumlahTanaman > 0
? round(($total / $totalJumlahTanaman) * 100, 1)
: 0,
'color' => $color['color'],
'bg' => $color['bg'],
'tx' => $color['tx'],
];
})
->values();
// Distribusi peserta pelatihan GAP
$gapOptions = collect($this->pelatihanGapOptions());
$knownGapOptions = $gapOptions->reject(function ($label, $value) {
return $this->normalizeText((string) $value) === 'lainnya'
|| $this->normalizeText((string) $label) === 'lainnya';
});
$gapLookup = [];
foreach ($knownGapOptions as $value => $label) {
$gapLookup[$this->normalizeText((string) $value)] = (string) $label;
$gapLookup[$this->normalizeText((string) $label)] = (string) $label;
}
$pelatihanPairs = SurveyPelatihan::query()
->whereNotNull('petani_id')
->whereNotNull('jenis_pelatihan')
->where('jenis_pelatihan', '!=', '')
->get([
'petani_id',
'jenis_pelatihan',
])
->map(function ($row) use ($gapLookup) {
$normalized = $this->normalizeText((string) $row->jenis_pelatihan);
return [
'petani_id' => $row->petani_id,
'label' => $gapLookup[$normalized] ?? 'Lainnya',
];
})
->unique(fn ($item) => $item['petani_id'] . '|' . $item['label'])
->values();
$pelatihanCounts = $pelatihanPairs->countBy('label');
$pelatihanLabels = $knownGapOptions
->values()
->unique()
->push('Lainnya')
->values();
$pelatihanGap = $pelatihanLabels
->map(function ($label, $index) use ($pelatihanCounts, $colors) {
$color = $colors[$index % count($colors)];
return [
'label' => $label,
'total' => (int) $pelatihanCounts->get($label, 0),
'color' => $color['color'],
'bg' => $color['bg'],
'tx' => $color['tx'],
];
})
->values();
$totalPetaniPelatihan = $pelatihanPairs
->pluck('petani_id')
->unique()
->count();
// Sertifikasi
$totalPetaniBersertifikat = SurveySertifikasi::query()
->whereNotNull('petani_id')
->distinct()
->count('petani_id');
$totalPetaniSertifikatAktif = SurveySertifikasi::query()
->whereNotNull('petani_id')
->where('is_aktif', true)
->distinct()
->count('petani_id');
return view('dashboard.survey-petani', [
'enumerators' => $enumerators,
'komoditas' => $komoditas,
'totalPetani' => $totalPetani,
'circumference' => $circumference,
'progressPetani' => $progressPetani,
'totalLuasLahan' => $totalLuasLahan,
'totalJumlahTanaman' => $totalJumlahTanaman,
'tanamanPerKomoditas' => $tanamanPerKomoditas,
'pelatihanGap' => $pelatihanGap,
'totalPetaniPelatihan' => $totalPetaniPelatihan,
'totalPetaniBersertifikat' => $totalPetaniBersertifikat,
'totalPetaniSertifikatAktif' => $totalPetaniSertifikatAktif,
]);
}
private function moduleStatus(string $label, bool $done): array
{
return [
'label' => $label,
'done' => $done,
];
}
private function hasCompleteLahan(Petani $petani): bool
{
return $petani->lahans->contains(function ($lahan) {
return $this->allFieldsFilled($lahan, [
'luas_lahan',
'komoditas_id',
'jenis_komoditas_id',
'umur_tanaman',
'kondisi_tanaman',
]);
});
}
private function hasCompletePraktikBudidaya(?Model $praktikBudidaya): bool
{
return $this->allFieldsFilled($praktikBudidaya, [
'frekuensi_pemangkasan_utama',
'pemberian_pupuk',
'metode_penanganan_opt',
'frekuensi_sanitasi_lahan',
'penanganan_gulma',
'kondisi_pengelolaan_air',
]);
}
private function hasCompleteSosialEkonomi(?Model $sosialEkonomi): bool
{
return $this->allFieldsFilled($sosialEkonomi, [
'skor_periode_paceklik',
'skor_kemampuan_menabung',
'skor_kecukupan_penghasilan',
'skor_kejelasan_status_lahan',
'kemauan_mencoba_praktik_baru',
'pemahaman_mutu_harga',
'panutan_petani_sukses',
'kemauan_ikut_program',
'orientasi_rencana_jangka_panjang',
'keaktifan_pertemuan_kelompok',
'peran_dalam_kelompok',
'manfaat_kelompok_tani',
'status_rekening_tabungan',
'pengalaman_kredit_kebun',
'kondisi_aset_produktif',
'keterlibatan_program_keuangan_formal',
'keterikatan_hutang_panen',
'praktik_ijon',
'kebebasan_memilih_pembeli',
]);
}
private function hasCompleteAksesPasar(?Model $aksesPasar): bool
{
if (! $this->allFieldsFilled($aksesPasar, [
'skor_pengalaman',
'praktik_fermentasi',
'skor_kesadaran_premium',
'tingkat_kepercayaan_pembeli_baru',
])) {
return false;
}
$skorPengalaman = (int) $aksesPasar->getAttribute('skor_pengalaman');
if (
$skorPengalaman !== SkorPengalaman::BELUM_PERNAH->value
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu'))
) {
return false;
}
if (
in_array($skorPengalaman, [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value,
], true)
&& ! $this->valueIsFilled($aksesPasar->getAttribute('alasan_berhenti'))
) {
return false;
}
if (
in_array(
SaluranTerdahulu::LAINNYA->value,
$aksesPasar->getAttribute('saluran_terdahulu') ?? [],
true
)
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu_lainnya'))
) {
return false;
}
if (
$aksesPasar->getAttribute('praktik_fermentasi') !== PraktikFermentasi::BELUM_PERNAH->value
&& ! $this->allFieldsFilled($aksesPasar, [
'proses_fermentasi',
'model_fermentasi_memungkinkan',
])
) {
return false;
}
return true;
}
private function allFieldsFilled(?Model $model, array $fields): bool
{
if (! $model) {
return false;
}
foreach ($fields as $field) {
if (! $this->valueIsFilled($model->getAttribute($field))) {
return false;
}
}
return true;
}
private function valueIsFilled(mixed $value): bool
{
if ($value instanceof BackedEnum) {
$value = $value->value;
}
if (is_array($value)) {
return collect($value)
->contains(fn ($item) => $this->valueIsFilled($item));
}
return ! is_null($value) && $value !== '';
}
private function pelatihanGapOptions(): array
{
$enumClass = self::GAP_ENUM_CLASS;
if (enum_exists($enumClass)) {
if (method_exists($enumClass, 'options')) {
return collect($enumClass::options())
->mapWithKeys(function ($option, $key) {
if (is_array($option)) {
$value = (string) ($option['value'] ?? $key);
$label = (string) (
$option['label']
?? $option['name']
?? $option['value']
?? $key
);
return [$value => $label];
}
return [(string) $key => (string) $option];
})
->all();
}
return collect($enumClass::cases())
->mapWithKeys(function ($case) {
$value = $case instanceof BackedEnum
? (string) $case->value
: (string) $case->name;
$label = method_exists($case, 'label')
? (string) $case->label()
: Str::headline($case->name);
return [$value => $label];
})
->all();
}
return [
'pemangkasan_perawatan_tanaman'
=> 'Pemangkasan/perawatan tanaman',
'pemupukan_nutrisi_tanah'
=> 'Pemupukan/nutrisi tanah',
'pengendalian_hama_penyakit'
=> 'Pengendalian hama & penyakit',
'praktik_berkebun_baik_gap'
=> 'Praktik Berkebun yang Baik (GAP)',
'pengolahan_pascapanen'
=> 'Pengolahan/pascapanen',
'penyerbukan_manual_vanili'
=> 'Penyerbukan manual (vanili)',
'lainnya'
=> 'Lainnya',
];
}
private function normalizeText(string $value): string
{
return Str::lower(Str::squish($value));
}
private function colors(): array
{
return [
[
'color' => '#6FD68F',
'bg' => 'rgba(111,214,143,.24)',
'tx' => '#6FD68F',
],
[
'color' => '#5FC7D6',
'bg' => 'rgba(95,199,214,.24)',
'tx' => '#5FC7D6',
],
[
'color' => '#FFB46F',
'bg' => 'rgba(255,180,111,.26)',
'tx' => '#FFB46F',
],
[
'color' => '#F47C86',
'bg' => 'rgba(244,124,134,.26)',
'tx' => '#F47C86',
],
[
'color' => '#9D8EF0',
'bg' => 'rgba(157,142,240,.26)',
'tx' => '#9D8EF0',
],
[
'color' => '#65D8BD',
'bg' => 'rgba(101,216,189,.26)',
'tx' => '#65D8BD',
],
];
}
}
@@ -55,19 +55,7 @@ class FormBasisDataController extends Controller
], 404);
}
$validated = $request->validate([
'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'],
'tipe' => ['required', Rule::enum(LayoutForm::class)],
'column_width' => [
Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value),
'nullable', 'integer', 'min:1', 'max:12'],
'kolom_id' => [
Rule::requiredIf($request->tipe === LayoutForm::VALUE->value),
'nullable', 'exists:ekosistem_basis_data_kolom,id'],
'section_label' => [
Rule::requiredIf($request->tipe === LayoutForm::SECTION->value),
'nullable', 'string'],
]);
$validated = $this->validateFormPayload($request, $tabelId);
FormBasisData::create([...$validated,
'tabel_id' => $tabelId,
@@ -115,19 +103,7 @@ class FormBasisDataController extends Controller
], 404);
}
$validated = $request->validate([
'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'],
'tipe' => ['required', Rule::enum(LayoutForm::class)],
'column_width' => [
Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value),
'nullable', 'integer', 'min:1', 'max:12'],
'kolom_id' => [
Rule::requiredIf($request->tipe === LayoutForm::VALUE->value),
'nullable', 'exists:ekosistem_basis_data_kolom,id'],
'section_label' => [
Rule::requiredIf($request->tipe === LayoutForm::SECTION->value),
'nullable', 'string'],
]);
$validated = $this->validateFormPayload($request, $tabelId, $form);
$form->update([...$validated,
'updated_by' => auth()->user()->id
@@ -293,4 +269,88 @@ class FormBasisDataController extends Controller
'message' => 'Formulir basis data berhasil diperbarui.'
], 200);
}
private function validateFormPayload(Request $request, string $tabelId, ?FormBasisData $form = null): array
{
$tipe = $request->input('tipe');
$needsParent = in_array($tipe, [LayoutForm::COLUMN->value, LayoutForm::VALUE->value], true);
$mustBeRoot = in_array($tipe, [LayoutForm::ROW->value, LayoutForm::SECTION->value, LayoutForm::HR_LINE->value], true);
$validator = Validator::make($request->all(), [
'parent_id' => [
Rule::requiredIf($needsParent),
Rule::prohibitedIf($mustBeRoot),
'nullable',
Rule::exists('ekosistem_basis_data_form', 'id')
->where(fn ($query) => $query
->where('tabel_id', $tabelId)
->whereNull('deleted_at')),
],
'tipe' => ['required', Rule::enum(LayoutForm::class)],
'column_width' => [
Rule::requiredIf($tipe === LayoutForm::COLUMN->value),
'nullable', 'integer', 'min:1', 'max:12'
],
'kolom_id' => [
Rule::requiredIf($tipe === LayoutForm::VALUE->value),
'nullable',
Rule::exists('ekosistem_basis_data_kolom', 'id')
->where(fn ($query) => $query
->where('tabel_id', $tabelId)
->whereNull('deleted_at')),
],
'section_label' => [
Rule::requiredIf($tipe === LayoutForm::SECTION->value),
'nullable', 'string'
],
]);
$validator->after(function ($validator) use ($request, $tabelId, $form) {
if ($validator->errors()->isNotEmpty() || !$request->filled('parent_id')) {
return;
}
if ($form && (int) $request->input('parent_id') === (int) $form->id) {
$validator->errors()->add('parent_id', 'Induk formulir tidak boleh menunjuk ke dirinya sendiri.');
return;
}
$layout = LayoutForm::from($request->input('tipe'));
$expectedParent = match ($layout) {
LayoutForm::COLUMN => LayoutForm::ROW,
LayoutForm::VALUE => LayoutForm::COLUMN,
default => null,
};
if (!$expectedParent) {
return;
}
$parent = FormBasisData::where('tabel_id', $tabelId)->find($request->input('parent_id'));
if (!$parent || $parent->tipe !== $expectedParent) {
$validator->errors()->add('parent_id', 'Induk formulir tidak sesuai dengan tipe formulir.');
}
});
$validated = $validator->validate();
$layout = LayoutForm::from($validated['tipe']);
if (in_array($layout, [LayoutForm::ROW, LayoutForm::SECTION, LayoutForm::HR_LINE], true)) {
$validated['parent_id'] = null;
}
if ($layout !== LayoutForm::COLUMN) {
$validated['column_width'] = null;
}
if ($layout !== LayoutForm::VALUE) {
$validated['kolom_id'] = null;
}
if ($layout !== LayoutForm::SECTION) {
$validated['section_label'] = null;
}
return $validated;
}
}
@@ -5,10 +5,11 @@ namespace App\Http\Controllers\Web\Konfigurasi;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use App\Models\Profile;
class AuthController extends Controller
{
@@ -26,23 +27,49 @@ class AuthController extends Controller
public function createAccount(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
$credentials = $request->validate([
'username' => 'required|string|min:6|max:255|unique:users,username',
'email' => 'required|string|email|max:255|unique:users,email',
'password' => 'required|string|min:8|confirmed',
], [
'username.unique' => 'Nama pengguna sudah digunakan.',
'username.min' => 'Nama pengguna minimal 6 karakter.',
'email.unique' => 'Email sudah digunakan.',
'password.confirmed' => 'Konfirmasi password tidak cocok.',
'password.min' => 'Password minimal 8 karakter.',
]);
User::create([
'name' => $request->name,
$user = User::create([
'uid' => (string) Str::uuid(),
'name' => $request->username,
'username' => $request->username,
'email' => $request->email,
'password' => Hash::make($request->password),
'password' => Hash::make($request->password),
'status' => 'pending',
'role' => null,
'email_verified_at' => null,
'created_by' => null,
'updated_by' => null,
'deleted_by' => null,
]);
$user->update([
'created_by' => $user->id,
'updated_by' => $user->id,
]);
return redirect()->route('auth.login')->with('success', 'Account created successfully. Please login.');
Profile::create([
'user_id' => $user->id,
'user_uid' => $user->uid,
'role' => $request->role,
'nama' => $request->username,
'email' => $request->email,
'telepon' => $request->telepon,
'alamat' => $request->alamat,
'kk' => $request->kk,
'ktp' => $request->ktp,
'desa_kelurahan_id' => $request->desa_kelurahan_id,
'created_by' => $user->id,
'updated_by' => $user->id,
]);
return redirect()->route('login')->with('success', 'Account created successfully. Please login.');
}
public function verify(User $user)
@@ -68,6 +95,9 @@ class AuthController extends Controller
$request->validate([
'email_username' => 'required|string',
'password' => 'required|string',
], [
'email_username.required' => 'Email atau username harus diisi.',
'password.required' => 'Password harus diisi.',
]);
$login = $request->input('email_username');
@@ -49,23 +49,42 @@ class UserController extends Controller
return view('konfigurasi.user.index', compact('users', 'totalUsers', 'activeUsers', 'pendingUsers', 'roles', 'tab'));
}
public function verify($id)
public function verify(Request $request, $id)
{
$request->validate([
'role' => ['required', 'string'],
]);
try {
$user = User::findOrFail($id);
$user->email_verified_at = now();
$user->status = 'verified';
$user->role = $request->role;
$user->save();
return response()->json([
'status' => 'success',
'message' => 'User berhasil diverifikasi dan diaktifkan.'
Profile::where('user_id', $user->id)->update([
'role' => $request->role
]);
if ($request->expectsJson()) {
return response()->json([
'status' => 'success',
'message' => 'User ' . $user->name . ' berhasil diverifikasi sebagai ' . $request->role . '.'
]);
}
return redirect()->back()->with('success', 'User ' . $user->name . ' berhasil diverifikasi sebagai ' . $request->role . '.');
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Gagal verifikasi user.'
], 500);
if ($request->expectsJson()) {
return response()->json([
'status' => 'error',
'message' => 'Gagal verifikasi user: ' . $e->getMessage()
], 500);
}
return back()->with('error', 'Gagal verifikasi user: ' . $e->getMessage());
}
}
@@ -80,18 +99,20 @@ class UserController extends Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'username' => ['required', 'string', 'max:255', 'unique:users,username'],
'username' => ['required', 'string', 'max:255', 'min:6', 'unique:users,username'],
'nama' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'role' => ['required', 'string'],
'telepon' => ['required', 'max:13'],
'desa_kelurahan_id' => ['required'],
'desa_kelurahan_id' => ['nullable', 'exists:desa_kelurahan,id'],
'ktp' => ['nullable', 'min:16', 'max:16'],
'kk' => ['nullable', 'min:16', 'max:16'],
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,pdf,png', 'max:2048'],
], [
'username.unique' => 'Nama pengguna sudah digunakan.',
'username.min' => 'Nama pengguna minimal 6 karakter.',
'password.confirmed' => 'Konfirmasi password tidak cocok!',
'password.min' => 'Password minimal 8 karakter!',
'email.unique' => 'Email sudah terdaftar.',
@@ -0,0 +1,177 @@
<?php
namespace App\Http\Controllers\Web\Koperasi;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Koperasi\PengurusKoperasi;
class PengurusKoperasiController extends Controller
{
public function index(Request $request)
{
$query = PengurusKoperasi::query();
if ($request->filled('search')) {
$search = $request->search;
$query->where('nama_lengkap', 'like', "%{$search}%")
->orWhere('nama_koperasi', 'like', "%{$search}%");
}
$perPage = $request->get('per_page', 10);
$pengurus = $query->latest()->paginate($perPage);
$totalPengurus = PengurusKoperasi::count();
$totalKoperasi = PengurusKoperasi::distinct('nama_koperasi')->count('nama_koperasi');
$totalJabatan = PengurusKoperasi::distinct('posisi_dalam_koperasi')->count('posisi_dalam_koperasi');
return view('koperasi.profil.index', compact(
'pengurus',
'totalPengurus',
'totalKoperasi',
'totalJabatan',
));
}
public function create()
{
return view('koperasi.profil.add');
}
public function edit($id)
{
$pengurus = PengurusKoperasi::findOrFail($id);
return view('koperasi.profil.edit', compact('pengurus'));
}
public function update(Request $request, $id)
{
$validatedData = $request->validate([
'nama_lengkap' => 'required|string|max:255',
'posisi_dalam_koperasi' => 'required|string|max:255',
'tahun_bergabung' => 'nullable|integer',
'no_hp' => 'nullable|string|max:20',
'alamat_tinggal' => 'nullable|string',
'nama_koperasi' => 'required|string|max:255',
'jenis_koperasi' => 'required|string|max:255',
'jenis_koperasi_lainnya'=> 'nullable|string|max:255',
'alamat_koperasi' => 'nullable|string',
'skala_operasi' => 'required|string|max:255',
'tahun_berdiri' => 'nullable|integer',
'jumlah_anggota_awal' => 'nullable|integer',
'jumlah_anggota_dewasa' => 'nullable|integer',
'jumlah_anggota_anak' => 'nullable|integer',
'jumlah_pengurus' => 'nullable|integer',
'shu_2025' => 'nullable|numeric',
'shu_2024' => 'nullable|numeric',
'shu_2023' => 'nullable|numeric',
]);
if ($validatedData['jenis_koperasi'] === 'Lainnya' && !empty($validatedData['jenis_koperasi_lainnya'])) {
$validatedData['jenis_koperasi'] = $validatedData['jenis_koperasi_lainnya'];
}
unset($validatedData['jenis_koperasi_lainnya']);
$pengurus = PengurusKoperasi::findOrFail($id);
$pengurus->update($validatedData);
return redirect()->route('koperasi.pengurus.index')
->with('success', 'Profil Koperasi berhasil diperbarui!');
}
public function survey(Request $request, $id)
{
$pengurus = PengurusKoperasi::findOrFail($id);
$step = $request->query('step', 1);
if ($step == 1) {
return view('koperasi.kelembagaan-aktor.index', compact('pengurus'));
} elseif ($step == 2) {
return view('koperasi.rantai-nilai-akses-pasar.index', compact('pengurus'));
} elseif ($step == 3) {
return view('koperasi.sosial-ekonomi.index', compact('pengurus'));
}
abort(404);
}
public function surveyStore(Request $request, $id)
{
$pengurus = PengurusKoperasi::findOrFail($id);
$step = $request->query('step', 1);
$data = $request->except(['_token', 'pengurus_koperasi_id']);
foreach ($data as $key => $value) {
if (is_array($value)) {
$data[$key] = json_encode($value);
}
}
$pengurus->update($data);
if ($step == 1) {
if ($request->has('agregasi_komoditas') && str_contains($data['agregasi_komoditas'], 'Tidak terlibat')) {
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 3])
->with('success', 'Data Kelembagaan tersimpan! (Melewati Rantai Nilai)');
}
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 2])
->with('success', 'Data Kelembagaan berhasil disimpan!');
}
if ($step == 2) {
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 3])
->with('success', 'Data Rantai Nilai berhasil disimpan!');
}
if ($step == 3) {
return redirect()->route('koperasi.pengurus.index')
->with('success', 'Seluruh data survei berhasil diselesaikan dan disimpan!');
}
return redirect()->back();
}
public function store(Request $request)
{
$validatedData = $request->validate([
'nama_lengkap' => 'required|string|max:255',
'posisi_dalam_koperasi' => 'required|string|max:255',
'tahun_bergabung' => 'nullable|integer',
'no_hp' => 'nullable|string|max:20',
'alamat_tinggal' => 'nullable|string',
'nama_koperasi' => 'required|string|max:255',
'jenis_koperasi' => 'required|string|max:255',
'jenis_koperasi_lainnya'=> 'nullable|string|max:255',
'alamat_koperasi' => 'nullable|string',
'skala_operasi' => 'required|string|max:255',
'tahun_berdiri' => 'nullable|integer',
'jumlah_anggota_awal' => 'nullable|integer',
'jumlah_anggota_dewasa' => 'nullable|integer',
'jumlah_anggota_anak' => 'nullable|integer',
'jumlah_pengurus' => 'nullable|integer',
'shu_2025' => 'nullable|numeric',
'shu_2024' => 'nullable|numeric',
'shu_2023' => 'nullable|numeric',
]);
if ($validatedData['jenis_koperasi'] === 'Lainnya' && !empty($validatedData['jenis_koperasi_lainnya'])) {
$validatedData['jenis_koperasi'] = $validatedData['jenis_koperasi_lainnya'];
}
unset($validatedData['jenis_koperasi_lainnya']);
$pengurus = PengurusKoperasi::create($validatedData);
return redirect()->route('koperasi.pengurus.index')
->with('success', 'Profil Koperasi berhasil ditambahkan! Silakan klik tombol hijau untuk mulai survei.');
}
public function destroy($id)
{
$pengurus = PengurusKoperasi::findOrFail($id);
$pengurus->delete();
return redirect()->route('koperasi.pengurus.index')
->with('success', 'Data pengurus koperasi berhasil dihapus!');
}
}
@@ -51,9 +51,13 @@ class DesaKelurahanController extends Controller
return response()->json($desa);
}
public function getByKecamatan($kecamatan_id)
public function getByKecamatan(Request $request, $kecamatan_id)
{
$desa = DesaKelurahan::where('kecamatan_id', $kecamatan_id)
$query = $request->boolean('all_wilayah')
? DesaKelurahan::withoutGlobalScope('program')
: DesaKelurahan::query();
$desa = $query->where('kecamatan_id', $kecamatan_id)
->orderBy('nama', 'asc')
->get();
@@ -51,9 +51,13 @@ class KabupatenKotaController extends Controller
return response()->json($kabupaten);
}
public function getByProvinsi($provinsi_id)
public function getByProvinsi(Request $request, $provinsi_id)
{
$kabupaten = KabupatenKota::where('provinsi_id', $provinsi_id)
$query = $request->boolean('all_wilayah')
? KabupatenKota::withoutGlobalScope('program')
: KabupatenKota::query();
$kabupaten = $query->where('provinsi_id', $provinsi_id)
->orderBy('nama', 'asc')
->get();
@@ -51,9 +51,13 @@ class KecamatanController extends Controller
return response()->json($kecamatan);
}
public function getByKabupaten($kabupaten_id)
public function getByKabupaten(Request $request, $kabupaten_id)
{
$kecamatan = Kecamatan::where('kabupaten_kota_id', $kabupaten_id)
$query = $request->boolean('all_wilayah')
? Kecamatan::withoutGlobalScope('program')
: Kecamatan::query();
$kecamatan = $query->where('kabupaten_kota_id', $kabupaten_id)
->orderBy('nama', 'asc')
->get();
@@ -28,9 +28,13 @@ class KomoditasController extends Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama'], // Tambah unique biar nggak ada data kembar
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama'],
'kode' => ['required', 'string', 'max:2', 'unique:master_komoditas,kode'],
'is_utama' => ['nullable', 'boolean'],
], [
'nama.unique' => 'Nama komoditas ini sudah ada di database.'
'nama.unique' => 'Nama komoditas ini sudah ada di database.',
'kode.unique' => 'Kode komoditas ini sudah ada di database.',
'kode.max' => 'Kode komoditas maksimal 2 Digit.'
]);
if ($validator->fails()) {
@@ -39,6 +43,8 @@ class KomoditasController extends Controller
Komoditas::create([
'nama' => $request->nama,
'kode' => $request->kode,
'is_utama' => $request->is_utama,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
@@ -55,9 +61,13 @@ class KomoditasController extends Controller
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama,' . $id],
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama,' . $id],
'kode' => ['required', 'string', 'max:2', 'unique:master_komoditas,kode,' . $id],
'is_utama' => ['nullable', 'boolean'],
], [
'nama.unique' => 'Nama komoditas ini sudah digunakan.'
'nama.unique' => 'Nama komoditas ini sudah ada di database.',
'kode.unique' => 'Kode komoditas ini sudah ada di database.',
'kode.max' => 'Kode komoditas maksimal 2 Digit.'
]);
if ($validator->fails()) {
@@ -66,8 +76,10 @@ class KomoditasController extends Controller
$komoditas = Komoditas::findOrFail($id);
$komoditas->update([
'nama' => $request->nama,
'updated_by' => auth()->id(),
'nama' => $request->nama,
'kode' => $request->kode,
'is_utama' => $request->is_utama,
'updated_by' => auth()->id(),
]);
return response()->json(['status' => 'success', 'message' => 'Komoditas berhasil diupdate.']);
@@ -7,51 +7,64 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\AksesPasar;
use App\Models\Pertanian\HargaJualPanen;
use App\Enums\AksesPasar\SkorPengalaman;
use App\Enums\AksesPasar\SaluranTerdahulu;
use App\Enums\AksesPasar\PraktikFermentasi;
use App\Enums\AksesPasar\SkorKesadaranPremium;
use App\Enums\AksesPasar\TingkatKepercayaanPembeli;
class AksesPasarController extends Controller
{
public function index($petaniId)
{
$petani = Petani::findOrFail($petaniId);
$petani = Petani::findOrFail($petaniId);
$aksesPasar = AksesPasar::where('petani_id', $petaniId)->first();
$hargaJualPanen = HargaJualPanen::where('petani_id', $petaniId)->get();
$hargaJualRows = collect([
'Buah segar hasil petik',
'Biji basah',
'Biji kering/Biji Kupas',
'Terfermentasi (Rp/kg)',
])->map(function (string $bentuk, int $index) use ($hargaJualPanen) {
$existingData = $hargaJualPanen->firstWhere('bentuk_produk', $bentuk);
return view('produktivitas.petani.akses-pasar.index', compact('petani', 'aksesPasar', 'hargaJualPanen'));
return [
'index' => $index,
'bentuk' => $bentuk,
'harga_panen_terakhir' => $this->currencyForDisplay($existingData?->harga_panen_terakhir),
'harga_tahun_lalu' => $this->currencyForDisplay($existingData?->harga_tahun_lalu),
'harga_dua_tahun_lalu' => $this->currencyForDisplay($existingData?->harga_dua_tahun_lalu),
];
});
return view('produktivitas.petani.akses-pasar.index', array_merge(
compact('petani', 'aksesPasar', 'hargaJualPanen', 'hargaJualRows'),
$this->aksesPasarOptions()
));
}
public function store(Request $request)
{
$request->validate([
'petani_id' => 'required|exists:petani,id',
]);
$validated = $this->validateData($request);
DB::beginTransaction();
try {
$aksesPasarData = $request->except(['_token', 'harga_jual']);
$aksesPasarData['uid'] = Str::uuid();
$aksesPasarData = $this->aksesPasarPayload($validated);
$aksesPasarData['uid'] = 'AKP-' . Str::uuid();
$aksesPasarData['created_by'] = Auth::id();
$aksesPasarData['updated_by'] = Auth::id();
AksesPasar::create($aksesPasarData);
$aksesPasar = AksesPasar::create($aksesPasarData);
if ($request->has('harga_jual')) {
foreach ($request->harga_jual as $harga) {
if (!empty($harga['bentuk_produk'])) {
HargaJualPanen::create([
'uid' => Str::uuid(),
'petani_id' => $request->petani_id,
'bentuk_produk' => $harga['bentuk_produk'],
'harga_panen_terakhir' => $harga['harga_panen_terakhir'],
'harga_tahun_lalu' => $harga['harga_tahun_lalu'],
'harga_dua_tahun_lalu' => $harga['harga_dua_tahun_lalu'],
'created_by' => Auth::id(),
]);
}
}
$this->syncHargaJual($request->petani_id, $validated['harga_jual'] ?? [], Auth::id());
}
DB::commit();
@@ -64,38 +77,22 @@ class AksesPasarController extends Controller
}
}
public function update(Request $request, $id)
public function update(Request $request, $petaniId, $id)
{
$request->validate([
'petani_id' => 'required|exists:petani,id',
]);
$validated = $this->validateData($request);
DB::beginTransaction();
try {
$aksesPasar = AksesPasar::findOrFail($id);
$updateData = $request->except(['_token', '_method', 'harga_jual']);
$updateData = $this->aksesPasarPayload($validated);
$updateData['updated_by'] = Auth::id();
$aksesPasar->update($updateData);
HargaJualPanen::where('petani_id', $request->petani_id)->delete();
if ($request->has('harga_jual')) {
foreach ($request->harga_jual as $harga) {
if (!empty($harga['bentuk_produk'])) {
HargaJualPanen::create([
'uid' => Str::uuid(),
'petani_id' => $request->petani_id,
'bentuk_produk' => $harga['bentuk_produk'],
'harga_panen_terakhir' => $harga['harga_panen_terakhir'],
'harga_tahun_lalu' => $harga['harga_tahun_lalu'],
'harga_dua_tahun_lalu' => $harga['harga_dua_tahun_lalu'],
'created_by' => $aksesPasar->created_by,
'updated_by' => Auth::id(),
]);
}
}
$this->syncHargaJual($request->petani_id, $validated['harga_jual'] ?? [], $aksesPasar->created_by, Auth::id());
}
DB::commit();
@@ -107,4 +104,207 @@ class AksesPasarController extends Controller
return redirect()->back()->withErrors(['error' => 'Gagal memperbarui data: ' . $e->getMessage()])->withInput();
}
}
private function aksesPasarOptions(): array
{
return [
'skorPengalamanOptions' => SkorPengalaman::options(),
'saluranTerdahuluOptions' => SaluranTerdahulu::options(),
'saluranLainnyaValue' => SaluranTerdahulu::LAINNYA->value,
'praktikFermentasiOptions' => PraktikFermentasi::options(),
'praktikBelumPernahValue' => PraktikFermentasi::BELUM_PERNAH->value,
'skorKesadaranPremiumOptions' => SkorKesadaranPremium::options(),
'tingkatKepercayaanOptions' => TingkatKepercayaanPembeli::options(),
'skorPengalamanBerhentiValues' => [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value
],
'sumberInfoLainnyaValue' => 'Lainnya',
];
}
private function validateData(Request $request): array
{
$this->normalizeCurrencyInput($request);
return $request->validate([
'petani_id' => ['required', 'exists:petani,id'],
'tujuan_penjualan' => ['nullable', 'array'],
'tujuan_penjualan.*' => ['string'],
'tujuan_penjualan_lainnya' => [
Rule::requiredIf(fn () => in_array('Lainnya', $request->input('tujuan_penjualan', []), true)),
'nullable', 'string', 'max:255'
],
'sifat_hubungan_bisnis' => ['nullable', 'string', 'max:255'],
'bentuk_penjualan' => ['nullable', 'string', 'max:255'],
'frekuensi_penjualan' => ['nullable', 'string', 'max:255'],
'skor_pengalaman' => ['nullable', 'integer', Rule::in(SkorPengalaman::values())],
'alasan_berhenti' => [
Rule::requiredIf(fn () => in_array((int)$request->input('skor_pengalaman'), [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value
], true)),
'nullable', 'string', 'max:255'
],
'saluran_terdahulu' => ['nullable', 'array'],
'saluran_terdahulu.*' => ['string', Rule::in(SaluranTerdahulu::values())],
'saluran_terdahulu_lainnya' => [
Rule::requiredIf(fn () => in_array(SaluranTerdahulu::LAINNYA->value, $request->input('saluran_terdahulu', []), true)),
'nullable', 'string', 'max:255'
],
'tahu_harga_sebelumnya' => ['nullable', 'string', 'max:255'],
'sumber_info_harga' => ['nullable', 'array'],
'sumber_info_harga.*' => ['string'],
'sumber_info_harga_lainnya' => [
Rule::requiredIf(fn () => in_array('Lainnya', $request->input('sumber_info_harga', []), true)),
'nullable', 'string', 'max:255'
],
'praktik_fermentasi' => ['nullable', 'string', Rule::in(PraktikFermentasi::values())],
'proses_fermentasi' => [
Rule::requiredIf(fn () => filled($request->input('praktik_fermentasi')) && $request->input('praktik_fermentasi') !== PraktikFermentasi::BELUM_PERNAH->value),
'nullable', 'string'
],
'model_fermentasi_memungkinkan' => [
Rule::requiredIf(fn () => filled($request->input('praktik_fermentasi')) && $request->input('praktik_fermentasi') !== PraktikFermentasi::BELUM_PERNAH->value),
'nullable', 'string'
],
'skor_kesadaran_premium' => ['nullable', 'integer', Rule::in(SkorKesadaranPremium::values())],
'sumber_info_premium' => ['nullable', 'string', 'max:255'],
'tingkat_kepercayaan_pembeli_baru' => ['nullable', 'integer', Rule::in(TingkatKepercayaanPembeli::values())],
'alasan_kepercayaan_pembeli_baru' => ['nullable', 'string'],
'harga_jual' => ['nullable', 'array'],
'harga_jual.*.bentuk_produk' => ['nullable', 'string'],
'harga_jual.*.harga_panen_terakhir' => ['nullable', 'numeric', 'min:0'],
'harga_jual.*.harga_tahun_lalu' => ['nullable', 'numeric', 'min:0'],
'harga_jual.*.harga_dua_tahun_lalu' => ['nullable', 'numeric', 'min:0'],
], [
'petani_id.required' => 'Petani harus dipilih.',
'petani_id.exists' => 'Petani tidak ditemukan.',
'tujuan_penjualan_lainnya.required' => 'Keterangan saluran penjualan lainnya harus diisi.',
'skor_pengalaman.integer' => 'Skor pengalaman harus berupa angka.',
'skor_pengalaman.in' => 'Skor pengalaman tidak valid.',
'alasan_berhenti.required' => 'Alasan berhenti harus diisi.',
'alasan_berhenti.string' => 'Alasan berhenti harus berupa string.',
'alasan_berhenti.max' => 'Alasan berhenti tidak boleh lebih dari 255 karakter.',
'saluran_terdahulu.array' => 'Saluran terdahulu harus berupa array.',
'saluran_terdahulu.*.string' => 'Saluran terdahulu harus berupa string.',
'saluran_terdahulu.*.in' => 'Saluran terdahulu tidak valid.',
'saluran_terdahulu_lainnya.required' => 'Saluran terdahulu lainnya harus diisi.',
'saluran_terdahulu_lainnya.string' => 'Saluran terdahulu lainnya harus berupa string.',
'saluran_terdahulu_lainnya.max' => 'Saluran terdahulu lainnya tidak boleh lebih dari 255 karakter.',
'praktik_fermentasi.string' => 'Praktik fermentasi harus berupa string.',
'praktik_fermentasi.in' => 'Praktik fermentasi tidak valid.',
'proses_fermentasi.required' => 'Proses fermentasi harus diisi.',
'proses_fermentasi.string' => 'Proses fermentasi harus berupa string.',
'model_fermentasi_memungkinkan.required' => 'Model fermentasi memungkinkan harus diisi.',
'model_fermentasi_memungkinkan.string' => 'Model fermentasi memungkinkan harus berupa string.',
'skor_kesadaran_premium.integer' => 'Skor kesadaran premium harus berupa angka.',
'skor_kesadaran_premium.in' => 'Skor kesadaran premium tidak valid.',
'sumber_info_premium.string' => 'Sumber info premium harus berupa string.',
'sumber_info_premium.max' => 'Sumber info premium tidak boleh lebih dari 255 karakter.',
'harga_jual.array' => 'Harga jual harus berupa array.',
'harga_jual.*.bentuk_produk.string' => 'Bentuk produk harus berupa string.',
'harga_jual.*.harga_panen_terakhir.numeric' => 'Harga panen terakhir harus berupa angka.',
'harga_jual.*.harga_tahun_lalu.numeric' => 'Harga tahun lalu harus berupa angka.',
'harga_jual.*.harga_dua_tahun_lalu.numeric' => 'Harga dua tahun lalu harus berupa angka.',
'harga_jual.*.harga_panen_terakhir.min' => 'Harga panen terakhir minimal 0.',
'harga_jual.*.harga_tahun_lalu.min' => 'Harga tahun lalu minimal 0.',
'harga_jual.*.harga_dua_tahun_lalu.min' => 'Harga dua tahun lalu minimal 0.',
]);
}
private function aksesPasarPayload(array $validated): array
{
$payload = collect($validated)->except(['harga_jual'])->all();
if (!in_array('Lainnya', $payload['tujuan_penjualan'] ?? [], true)) {
$payload['tujuan_penjualan_lainnya'] = null;
}
if (!in_array((int)($payload['skor_pengalaman'] ?? 0), [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value
], true)) {
$payload['alasan_berhenti'] = null;
}
if (!in_array(SaluranTerdahulu::LAINNYA->value, $payload['saluran_terdahulu'] ?? [], true)) {
$payload['saluran_terdahulu_lainnya'] = null;
}
if (!in_array('Lainnya', $payload['sumber_info_harga'] ?? [], true)) {
$payload['sumber_info_harga_lainnya'] = null;
}
if (($payload['praktik_fermentasi'] ?? null) === PraktikFermentasi::BELUM_PERNAH->value) {
$payload['proses_fermentasi'] = null;
$payload['model_fermentasi_memungkinkan'] = null;
}
return $payload;
}
private function syncHargaJual($petaniId, array $hargaJualData, $createdBy, $updatedBy = null): void
{
HargaJualPanen::where('petani_id', $petaniId)->delete();
foreach ($hargaJualData as $harga) {
if (!empty($harga['bentuk_produk'])) {
HargaJualPanen::create([
'uid' => 'HJP-' . Str::uuid(),
'petani_id' => $petaniId,
'bentuk_produk' => $harga['bentuk_produk'],
'harga_panen_terakhir' => $harga['harga_panen_terakhir'] ?? null,
'harga_tahun_lalu' => $harga['harga_tahun_lalu'] ?? null,
'harga_dua_tahun_lalu' => $harga['harga_dua_tahun_lalu'] ?? null,
'created_by' => $createdBy,
'updated_by' => $updatedBy ?? $createdBy,
]);
}
}
}
private function normalizeCurrencyInput(Request $request): void
{
$data = $request->all();
if (isset($data['harga_jual']) && is_array($data['harga_jual'])) {
foreach ($data['harga_jual'] as $index => $item) {
if (is_array($item)) {
if (array_key_exists('harga_panen_terakhir', $item)) {
$data['harga_jual'][$index]['harga_panen_terakhir'] = $this->currencyDigits($item['harga_panen_terakhir']);
}
if (array_key_exists('harga_tahun_lalu', $item)) {
$data['harga_jual'][$index]['harga_tahun_lalu'] = $this->currencyDigits($item['harga_tahun_lalu']);
}
if (array_key_exists('harga_dua_tahun_lalu', $item)) {
$data['harga_jual'][$index]['harga_dua_tahun_lalu'] = $this->currencyDigits($item['harga_dua_tahun_lalu']);
}
}
}
}
$request->merge($data);
}
private function currencyDigits(mixed $value): ?string
{
if (is_null($value) || $value === '') return null;
$digits = preg_replace('/\D/', '', (string) $value);
return $digits === '' ? null : $digits;
}
private function currencyForDisplay(mixed $value): string
{
$digits = $this->currencyDigits($value);
return is_null($digits) ? '' : number_format((int) $digits, 0, ',', '.');
}
}
@@ -0,0 +1,153 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use App\Models\Pertanian\KelompokTani;
use App\Models\Pertanian\AsetKelompokTani;
class AsetKelompokTaniController extends Controller
{
public const JENIS_ASET_BAKU = [
'Bibit/alat produksi',
'Pupuk (stok kelompok)',
'Pulper/alat kupas',
'Lantai jemur',
'Kotak/wadah fermentasi',
'Kas/simpanan kelompok',
'Gudang/tempat simpan',
'Timbangan',
];
private function authorizeAccess(): void
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
}
private function redirectToSection(string $url, string $section, string $message)
{
return redirect($url . '#section-' . $section)->with([
'success' => $message,
'success_section' => $section,
]);
}
public function index(KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$kelompokTani->load('aset');
return view('produktivitas.survey-kelompok-tani.aset-keuangan', compact('kelompokTani'));
}
public function update(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$validated = $request->validate([
'aset' => ['required', 'array'],
'aset.*.jenis_aset' => ['required', Rule::in(self::JENIS_ASET_BAKU)],
'aset.*.status_aset' => ['nullable', 'boolean'],
'aset.*.status_pengelolaan_aset' => ['nullable', 'string', 'max:100'],
'aset.*.kondisi_aset' => ['nullable', 'string', 'max:100'],
]);
foreach ($validated['aset'] as $item) {
$jenisAset = $item['jenis_aset'];
$ada = (bool) ($item['status_aset'] ?? false);
$existing = $kelompokTani->aset()->where('jenis_aset', $jenisAset)->first()
?? $kelompokTani->aset()->onlyTrashed()->where('jenis_aset', $jenisAset)->latest('id')->first();
if (!$ada) {
if ($existing && !$existing->trashed()) {
$existing->update(['deleted_by' => auth()->id()]);
$existing->delete();
}
continue;
}
if ($existing) {
if ($existing->trashed()) {
$existing->restore();
}
$existing->update([
'status_aset' => true,
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? $existing->status_pengelolaan_aset,
'kondisi_aset' => $item['kondisi_aset'] ?? $existing->kondisi_aset,
'deleted_by' => null,
'updated_by' => auth()->id(),
]);
continue;
}
$kelompokTani->aset()->create([
'uid' => 'AKT-' . strtoupper(Str::random(10)),
'jenis_aset' => $jenisAset,
'status_aset' => true,
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? '-',
'kondisi_aset' => $item['kondisi_aset'] ?? '-',
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-baku', 'Data aset produktif berhasil disimpan.');
}
public function store(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$validated = $request->validate([
'aset_lainnya' => ['nullable', 'array'],
'aset_lainnya.*.jenis_aset' => ['nullable', 'string', 'max:255', Rule::notIn(self::JENIS_ASET_BAKU)],
'aset_lainnya.*.status_pengelolaan_aset' => ['nullable', 'string', 'max:100'],
'aset_lainnya.*.kondisi_aset' => ['nullable', 'string', 'max:100'],
], [
'aset_lainnya.*.jenis_aset.not_in' => 'Jenis aset ini sudah ada di daftar baku di atas.',
]);
foreach ($validated['aset_lainnya'] ?? [] as $item) {
$jenisAset = trim($item['jenis_aset'] ?? '');
if ($jenisAset === '') {
continue;
}
$kelompokTani->aset()->create([
'uid' => 'AKT-' . strtoupper(Str::random(10)),
'jenis_aset' => $jenisAset,
'status_aset' => true,
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? '-',
'kondisi_aset' => $item['kondisi_aset'] ?? '-',
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-lainnya', 'Aset lainnya berhasil ditambahkan.');
}
public function destroy(KelompokTani $kelompokTani, AsetKelompokTani $aset)
{
$this->authorizeAccess();
abort_unless($aset->survey_kelompok_tani_id === $kelompokTani->id, 404);
abort_if(in_array($aset->jenis_aset, self::JENIS_ASET_BAKU), 422, 'Aset baku diatur lewat checklist, bukan dihapus langsung.');
$aset->update(['deleted_by' => auth()->id()]);
$aset->delete();
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-lainnya', 'Aset lainnya berhasil dihapus.');
}
}
@@ -0,0 +1,97 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use App\Models\Pertanian\KelompokTani;
use App\Models\Pertanian\BantuanKelompokTani;
class BantuanKelompokTaniController extends Controller
{
private function authorizeAccess(): void
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
}
private function redirectToSection(string $url, string $section, string $message)
{
return redirect($url . '#section-' . $section)->with([
'success' => $message,
'success_section' => $section,
]);
}
private function redirectToSectionWithError(string $url, string $section, string $message)
{
return redirect($url . '#section-' . $section)->with([
'error' => $message,
'error_section' => $section,
]);
}
public function index(KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$kelompokTani->load('bantuan');
return view('produktivitas.survey-kelompok-tani.bantuan-penilaian', compact('kelompokTani'));
}
public function store(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
if (!$kelompokTani->pernah_mendapatkan_bantuan) {
return $this->redirectToSectionWithError(
route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id),
'status-bantuan',
'Set status jadi "Pernah" dulu sebelum menambahkan daftar bantuan/program.'
);
}
$validated = $request->validate([
'bantuan' => ['nullable', 'array'],
'bantuan.*.nama_program' => ['nullable', 'string', 'max:255'],
'bantuan.*.penyelenggara' => ['nullable', 'string', 'max:255'],
'bantuan.*.bentuk_bantuan' => ['nullable', 'string', 'max:255'],
'bantuan.*.tahun' => ['nullable', 'integer', 'min:1900', 'max:' . date('Y')],
]);
foreach ($validated['bantuan'] ?? [] as $item) {
if (empty($item['nama_program']) && empty($item['penyelenggara']) && empty($item['bentuk_bantuan'])) {
continue;
}
$kelompokTani->bantuan()->create([
'uid' => 'BKT-' . strtoupper(Str::random(10)),
'nama_program' => $item['nama_program'] ?? null,
'penyelenggara' => $item['penyelenggara'] ?? null,
'bentuk_bantuan' => $item['bentuk_bantuan'] ?? null,
'tahun' => $item['tahun'] ?? null,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'bantuan', 'Data bantuan berhasil ditambahkan.');
}
public function destroy(KelompokTani $kelompokTani, BantuanKelompokTani $bantuan)
{
$this->authorizeAccess();
abort_unless($bantuan->survey_kelompok_tani_id === $kelompokTani->id, 404);
$bantuan->update(['deleted_by' => auth()->id()]);
$bantuan->delete();
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'bantuan', 'Data bantuan berhasil dihapus.');
}
}
@@ -5,20 +5,186 @@ namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\SurveyPelatihan;
use App\Models\Pertanian\SurveySertifikasi;
class PendidikanPelatihanController extends Controller
{
public function index($petaniId, Request $request)
private function authorizeAccess(): void
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
}
public function index(Petani $petani)
{
$this->authorizeAccess();
$title = 'Petani - Pendidikan dan Pelatihan';
$petani = Petani::findOrFail($petaniId);
return view('produktivitas.petani.pendidikan-pelatihan.add', compact('title', 'petani'));
$petani->load([
'surveyPelatihan',
'surveySertifikasi',
]);
return view('produktivitas.petani.pendidikan-pelatihan.index', compact(
'title',
'petani'
));
}
public function storePelatihan(Request $request, Petani $petani)
{
$this->authorizeAccess();
$validatedData = $request->validate([
'riwayat_pelatihan' => ['nullable', 'array'],
'riwayat_pelatihan.*.jenis_pelatihan' => ['nullable', 'string', 'max:255'],
'riwayat_pelatihan.*.penyelenggara' => ['nullable', 'string', 'max:255'],
'riwayat_pelatihan.*.tahun' => ['nullable', 'integer', 'min:1900', 'max:' . date('Y')],
], [
'riwayat_pelatihan.*.tahun.integer' => 'Tahun pelatihan harus berupa angka.',
'riwayat_pelatihan.*.tahun.min' => 'Tahun pelatihan tidak valid.',
'riwayat_pelatihan.*.tahun.max' => 'Tahun pelatihan tidak boleh melebihi tahun saat ini.',
], [
'riwayat_pelatihan.*.jenis_pelatihan' => 'jenis pelatihan',
'riwayat_pelatihan.*.penyelenggara' => 'penyelenggara',
'riwayat_pelatihan.*.tahun' => 'tahun pelatihan',
]);
foreach ($validatedData['riwayat_pelatihan'] ?? [] as $item) {
if (
empty($item['jenis_pelatihan']) &&
empty($item['penyelenggara']) &&
empty($item['tahun'])
) {
continue;
}
$petani->surveyPelatihan()->create([
'uid' => 'SPL-' . strtoupper(Str::random(10)),
'petani_id' => $petani->id,
'jenis_pelatihan' => $item['jenis_pelatihan'] ?? null,
'penyelenggara' => $item['penyelenggara'] ?? null,
'tahun_pelaksanaan' => $item['tahun'] ?? null,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectBackToFragment($request, 'section-sertifikasi')
->with('success', 'Riwayat pelatihan berhasil disimpan.');
}
public function destroyPelatihan(Request $request, Petani $petani, SurveyPelatihan $surveyPelatihan)
{
$this->authorizeAccess();
abort_unless($surveyPelatihan->petani_id === $petani->id, 404);
$surveyPelatihan->update([
'deleted_by' => auth()->id(),
]);
$surveyPelatihan->delete();
return $this->redirectBackToFragment($request, 'section-pelatihan')
->with('success', 'Riwayat pelatihan berhasil dihapus.');
}
public function storeSertifikasi(Request $request, Petani $petani)
{
$this->authorizeAccess();
$sertifikasi = $request->input('sertifikasi', []);
foreach ($sertifikasi as $index => $item) {
$status = $item['status_berlaku_sertifikat'] ?? 0;
if (is_array($status)) {
$status = in_array('1', $status, true) ? 1 : 0;
} else {
$status = $status == 1 ? 1 : 0;
}
$sertifikasi[$index]['status_berlaku_sertifikat'] = $status;
}
$request->merge([
'sertifikasi' => $sertifikasi,
]);
$validatedData = $request->validate([
'sertifikasi' => ['nullable', 'array'],
'sertifikasi.*.jenis_sertifikasi' => ['nullable', 'string', 'max:255'],
'sertifikasi.*.lembaga_penerbit' => ['nullable', 'string', 'max:255'],
'sertifikasi.*.tahun' => ['nullable', 'integer', 'min:1900', 'max:' . date('Y')],
'sertifikasi.*.status_berlaku_sertifikat' => ['nullable', 'boolean'],
], [
'sertifikasi.*.tahun.integer' => 'Tahun sertifikasi harus berupa angka.',
'sertifikasi.*.tahun.min' => 'Tahun sertifikasi tidak valid.',
'sertifikasi.*.tahun.max' => 'Tahun sertifikasi tidak boleh melebihi tahun saat ini.',
], [
'sertifikasi.*.jenis_sertifikasi' => 'jenis sertifikasi',
'sertifikasi.*.lembaga_penerbit' => 'lembaga penerbit',
'sertifikasi.*.tahun' => 'tahun sertifikasi',
'sertifikasi.*.status_berlaku_sertifikat' => 'status sertifikat',
]);
foreach ($validatedData['sertifikasi'] ?? [] as $item) {
if (
empty($item['jenis_sertifikasi']) &&
empty($item['lembaga_penerbit']) &&
empty($item['tahun'])
) {
continue;
}
$petani->surveySertifikasi()->create([
'uid' => 'SPS-' . strtoupper(Str::random(10)),
'petani_id' => $petani->id,
'jenis_sertifikasi' => $item['jenis_sertifikasi'] ?? null,
'lembaga_penerbit' => $item['lembaga_penerbit'] ?? null,
'tahun_pelaksanaan' => $item['tahun'] ?? null,
'is_aktif' => $item['status_berlaku_sertifikat'] ?? 0,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectBackToFragment($request, 'section-sertifikasi')
->with('success', 'Sertifikasi berhasil disimpan.');
}
public function destroySertifikasi(Request $request, Petani $petani, SurveySertifikasi $surveySertifikasi)
{
$this->authorizeAccess();
abort_unless($surveySertifikasi->petani_id === $petani->id, 404);
$surveySertifikasi->update([
'deleted_by' => auth()->id(),
]);
$surveySertifikasi->delete();
return $this->redirectBackToFragment($request, 'section-sertifikasi')
->with('success', 'Sertifikasi berhasil dihapus.');
}
private function redirectBackToFragment(Request $request, string $defaultFragment)
{
$fragment = $request->input('redirect_fragment', $defaultFragment);
if (!is_string($fragment) || !preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
$fragment = $defaultFragment;
}
return redirect()->to(Str::before(url()->previous(), '#') . '#' . $fragment);
}
}
@@ -0,0 +1,81 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use App\Models\Pertanian\KelompokTani;
use App\Models\Pertanian\PengurusKelompokTani;
class PengurusKelompokTaniController extends Controller
{
private function authorizeAccess(): void
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
}
private function redirectToSection(string $url, string $section, string $message)
{
return redirect($url . '#section-' . $section)->with([
'success' => $message,
'success_section' => $section,
]);
}
public function index(KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$kelompokTani->load('pengurus');
return view('produktivitas.survey-kelompok-tani.pengurus-anggota', compact('kelompokTani'));
}
public function store(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$validated = $request->validate([
'pengurus' => ['nullable', 'array'],
'pengurus.*.nama_pengurus' => ['nullable', 'string', 'max:255'],
'pengurus.*.jabatan' => ['nullable', 'string', 'max:255'],
'pengurus.*.kontak' => ['nullable', 'string', 'max:50'],
'pengurus.*.status' => ['nullable', 'boolean'],
]);
foreach ($validated['pengurus'] ?? [] as $item) {
if (empty($item['nama_pengurus']) && empty($item['jabatan']) && empty($item['kontak'])) {
continue;
}
$kelompokTani->pengurus()->create([
'uid' => 'PKT-' . strtoupper(Str::random(10)),
'nama_pengurus' => $item['nama_pengurus'] ?? null,
'jabatan' => $item['jabatan'] ?? null,
'kontak' => $item['kontak'] ?? null,
'status' => $item['status'] ?? false,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'pengurus', 'Data pengurus berhasil ditambahkan.');
}
public function destroy(KelompokTani $kelompokTani, PengurusKelompokTani $pengurus)
{
$this->authorizeAccess();
abort_unless($pengurus->survey_kelompok_tani_id === $kelompokTani->id, 404);
$pengurus->update(['deleted_by' => auth()->id()]);
$pengurus->delete();
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'pengurus', 'Data pengurus berhasil dihapus.');
}
}
@@ -12,8 +12,14 @@ use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use App\Models\Wilayah\Provinsi;
use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan;
use App\Models\Pertanian\Petani;
use App\Models\Wilayah\DesaKelurahan;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Tanaman;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\SkorPetani;
use App\Enums\Pendidikan;
use App\Enums\Pekerjaan;
@@ -26,21 +32,186 @@ class PetaniController extends Controller
*/
public function index(Request $request)
{
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$query = Petani::with('desakelurahan')
->orderBy('created_at', 'desc');
$provinsi = Provinsi::select('id', 'kode', 'nama')->orderBy('kode', 'asc')->get();
$userRole = auth()->user()->role->value;
$showFasilitatorColumn = in_array($userRole, ['super_admin', 'admin'], true);
$defaultKabupaten = KabupatenKota::select('id', 'nama')
->whereRaw('lower(nama) like ?', ['%ngada%'])
->orderBy('nama')
->first();
$selectedKabupatenId = $request->query('kabupaten_kota_id', $defaultKabupaten?->id);
$allowedSorts = ['nama_asc', 'nama_desc', 'luas_lahan_desc', 'luas_lahan_asc'];
$requestedSort = $request->query('sort_by')
?? match ($request->query('sort_nama')) {
'asc' => 'nama_asc',
'desc' => 'nama_desc',
default => null,
};
if ($request->search) {
$query->where(function ($query) use ($request) {
$query->where('nama', 'like', '%' . $request->search . '%')
->orWhere('ktp', 'like', '%' . $request->search . '%');
$filters = [
'search' => $request->query('search'),
'komoditas_id' => $request->query('komoditas_id'),
'kabupaten_kota_id' => $selectedKabupatenId,
'kecamatan_id' => $request->query('kecamatan_id'),
'desa_kelurahan_id' => $request->query('desa_kelurahan_id'),
'sort_by' => in_array($requestedSort, $allowedSorts, true)
? $requestedSort
: null,
];
$query = Petani::with([
'desaKelurahan:id,nama,kecamatan_id',
'desaKelurahan.kecamatan:id,nama,kabupaten_kota_id',
'desaKelurahan.kecamatan.kabupatenKota:id,nama,provinsi_id',
'desaKelurahan.kecamatan.kabupatenKota.provinsi:id,nama',
'komoditas:id,nama',
'skor:id,petani_id,skor_kesiapan_adopsi',
'creator:id,name,role',
])
->withSum('lahans as total_luas_lahan', 'luas_lahan');
$search = $request->filled('search') ? Str::lower($request->search) : null;
if ($search) {
$query->where(function ($query) use ($search) {
$query->whereRaw('lower(nama) like ?', ["%{$search}%"])
->orWhereRaw('lower(ktp) like ?', ["%{$search}%"]);
});
}
if ($request->filled('komoditas_id')) {
$query->where('komoditas_id', $request->komoditas_id);
}
if ($filters['kabupaten_kota_id']) {
$query->whereHas('desaKelurahan.kecamatan', function ($query) use ($filters) {
$query->where('kabupaten_kota_id', $filters['kabupaten_kota_id']);
});
}
if ($request->filled('kecamatan_id')) {
$query->whereHas('desaKelurahan', function ($query) use ($request) {
$query->where('kecamatan_id', $request->kecamatan_id);
});
}
if ($request->filled('desa_kelurahan_id')) {
$query->where('desa_kelurahan_id', $request->desa_kelurahan_id);
}
if ($userRole === 'fasilitator') {
$query->where('created_by', auth()->user()->id);
}
$totalPetani = (clone $query)->count();
$totalPetaniLakiLaki = (clone $query)->where('jenis_kelamin', 1)->count();
$totalPetaniPerempuan = (clone $query)->where('jenis_kelamin', 0)->count();
match ($filters['sort_by']) {
'nama_asc' => $query->orderBy('nama', 'asc')->orderBy('created_at', 'desc'),
'nama_desc' => $query->orderBy('nama', 'desc')->orderBy('created_at', 'desc'),
'luas_lahan_desc' => $query->orderByDesc(
Lahan::selectRaw('COALESCE(SUM(luas_lahan), 0)')
->whereColumn('map_lahan.petani_id', 'petani.id')
->whereNull('map_lahan.deleted_at')
)->orderBy('created_at', 'desc'),
'luas_lahan_asc' => $query->orderBy(
Lahan::selectRaw('COALESCE(SUM(luas_lahan), 0)')
->whereColumn('map_lahan.petani_id', 'petani.id')
->whereNull('map_lahan.deleted_at')
)->orderBy('created_at', 'desc'),
default => $query->orderBy('created_at', 'desc'),
};
$summaryPetaniIds = (clone $query)->pluck('id');
$petani = $query->paginate($request->per_page ?? 10);
return view('produktivitas.petani.index', compact('petani', 'provinsi'));
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
$kabupatenOptions = KabupatenKota::select('id', 'nama')->orderBy('nama')->get();
$selectedKabupaten = $filters['kabupaten_kota_id']
? KabupatenKota::select('id', 'nama')->find($filters['kabupaten_kota_id'])
: null;
$kecamatanOptions = $filters['kabupaten_kota_id']
? Kecamatan::select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get()
: collect();
$desaOptions = $request->filled('kecamatan_id')
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $request->kecamatan_id)->orderBy('nama')->get()
: collect();
$lahanSummaryQuery = Lahan::query()
->whereHas('petani')
->when($request->filled('komoditas_id'), function ($query) use ($request) {
$query->where('komoditas_id', $request->komoditas_id);
})
->when($filters['kabupaten_kota_id'], function ($query) use ($filters) {
$query->whereHas('desaKelurahan.kecamatan', function ($kecamatan) use ($filters) {
$kecamatan->where('kabupaten_kota_id', $filters['kabupaten_kota_id']);
});
})
->when($request->filled('kecamatan_id'), function ($query) use ($request) {
$query->whereHas('desaKelurahan', function ($desa) use ($request) {
$desa->where('kecamatan_id', $request->kecamatan_id);
});
})
->when($request->filled('desa_kelurahan_id'), function ($query) use ($request) {
$query->where('desa_kelurahan_id', $request->desa_kelurahan_id);
})
->when($request->filled('search'), function ($query) use ($search) {
$query->whereHas('petani', function ($petani) use ($search) {
$petani->whereRaw('lower(nama) like ?', ["%{$search}%"])
->orWhereRaw('lower(ktp) like ?', ["%{$search}%"]);
});
})
->when($userRole === 'fasilitator', function ($query) {
$query->whereHas('petani', function ($petani) {
$petani->where('created_by', auth()->id());
});
});
$totalLahan = (clone $lahanSummaryQuery)->count();
$totalLuasLahan = (clone $lahanSummaryQuery)->sum('luas_lahan');
$totalTanaman = Tanaman::whereIn('lahan_id', (clone $lahanSummaryQuery)->select('id'))
->when($request->filled('komoditas_id'), function ($query) use ($request) {
$query->where('komoditas_id', $request->komoditas_id);
})
->count();
$case = "
CASE
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
ELSE 'Belum Lengkap'
END
";
$skor = SkorPetani::selectRaw("$case AS segmen, COUNT(*) AS total")
->whereIn('petani_id', $summaryPetaniIds)
->groupByRaw($case)
->get();
return view('produktivitas.petani.index', compact(
'petani',
'provinsi',
'komoditas',
'komoditasOptions',
'kabupatenOptions',
'selectedKabupaten',
'kecamatanOptions',
'desaOptions',
'filters',
'skor',
'totalLahan',
'totalLuasLahan',
'totalTanaman',
'totalPetani',
'totalPetaniLakiLaki',
'totalPetaniPerempuan',
'showFasilitatorColumn'
));
}
public function create()
@@ -53,19 +224,17 @@ class PetaniController extends Controller
$pekerjaan = Pekerjaan::options();
$pernikahan = Pernikahan::options();
$komoditas = Komoditas::where('is_utama', true)->get();
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
return view('produktivitas.petani.add', compact('pendidikan', 'pekerjaan',
'pernikahan', 'provinsi'));
'pernikahan', 'provinsi', 'komoditas'));
}
public function store(Request $request)
private function validatePetani(Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$validatedData = $request->validate([
return $request->validate([
'nama' => ['required','string','max:255'],
'jenis_kelamin' => ['required','boolean'],
'tempat_lahir' => ['required','string','max:255'],
@@ -73,7 +242,7 @@ class PetaniController extends Controller
'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)],
'alamat' => ['required','string','max:500'],
'desa_kelurahan_id' => ['required','exists:master_desa_kelurahan,id'],
//'komoditas_id' => ['required','exists:master_komoditas,id'],
'komoditas_id' => ['required','exists:master_komoditas,id'],
'status_perkawinan' => ['required', Rule::enum(Pernikahan::class)],
'ktp' => ['nullable', 'min:16', 'max:16'],
'file_ktp' => ['nullable', 'image', 'mimes:jpeg,jpg,png', 'max:2048'],
@@ -82,8 +251,48 @@ class PetaniController extends Controller
'foto' => ['nullable', 'image', 'mimes:jpeg,jpg,png', 'max:2048'],
'nomor_telepon' => ['nullable', 'digits_between:11,13'],
], [
'nama.required' => 'Nama harus diisi.',
'nama.string' => 'Nama harus berupa string.',
'nama.max' => 'Nama tidak boleh lebih dari 255 karakter.',
'jenis_kelamin.required' => 'Jenis kelamin harus dipilih.',
'tempat_lahir.required' => 'Tempat lahir harus diisi.',
'tempat_lahir.string' => 'Tempat lahir harus berupa string.',
'tempat_lahir.max' => 'Tempat lahir tidak boleh lebih dari 255 karakter.',
'tanggal_lahir.required' => 'Tanggal lahir harus diisi.',
'tanggal_lahir.date' => 'Tanggal lahir harus berupa tanggal.',
'pendidikan_terakhir.required' => 'Pendidikan terakhir harus dipilih.',
'alamat.required' => 'Alamat harus diisi.',
'alamat.string' => 'Alamat harus berupa string.',
'alamat.max' => 'Alamat tidak boleh lebih dari 500 karakter.',
'desa_kelurahan_id.required' => 'Desa kelurahan harus dipilih.',
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
'komoditas_id.required' => 'Komoditas harus dipilih.',
'komoditas_id.exists' => 'Komoditas tidak ditemukan.',
'status_perkawinan.required' => 'Status perkawinan harus dipilih.',
'ktp.min' => 'KTP harus berupa 16 digit.',
'ktp.max' => 'KTP harus berupa 16 digit.',
'kk.min' => 'KK harus berupa 16 digit.',
'kk.max' => 'KK harus berupa 16 digit.',
'file_ktp.image' => 'File KTP harus berupa gambar.',
'file_ktp.mimes' => 'File KTP harus berupa jpeg, jpg, atau png.',
'file_ktp.max' => 'File KTP tidak boleh lebih dari 2MB.',
'file_kk.image' => 'File KK harus berupa gambar.',
'file_kk.mimes' => 'File KK harus berupa jpeg, jpg, atau png.',
'file_kk.max' => 'File KK tidak boleh lebih dari 2MB.',
'foto.image' => 'Foto harus berupa gambar.',
'foto.mimes' => 'Foto harus berupa jpeg, jpg, atau png.',
'foto.max' => 'Foto tidak boleh lebih dari 2MB.',
'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.',
]);
}
public function store(Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$validatedData = $this->validatePetani($request);
if ($request->hasFile('file_ktp')) {
$file_ktp = $request->file('file_ktp');
@@ -100,12 +309,13 @@ class PetaniController extends Controller
$validatedData['foto'] = $file_foto->store('petani/foto');
}
Petani::create([...$validatedData,
'uid' => 'PTN-'. strtoupper(Str::random(10)),
$petaniUID = strtoupper(Str::random(10));
$petani = Petani::create([...$validatedData,
'uid' => 'PTN-'. $petaniUID,
'created_by' => auth()->id(),
'updated_by' => auth()->id()]);
return redirect()->route('petani.index')->with('success', 'Data petani berhasil ditambahkan.');
return redirect()->route('petani.edit', $petani->id)->with('success', 'Data petani berhasil ditambahkan.');
}
public function show(string $id)
@@ -134,10 +344,10 @@ class PetaniController extends Controller
$provinsi = Provinsi::all();
$section = $request->section ?? 'budidaya';
$komoditas = Komoditas::where('is_utama', true)->get();
return view('produktivitas.petani.edit', compact('petani', 'pendidikan', 'pekerjaan',
'pernikahan', 'provinsi', 'section'));
'pernikahan', 'provinsi', 'komoditas'));
}
@@ -149,25 +359,14 @@ class PetaniController extends Controller
$petani = Petani::findOrFail($id);
$validatedData = $request->validate([
'nama' => ['required','string','max:255'],
'jenis_kelamin' => ['required','boolean'],
'tempat_lahir' => ['required','string','max:255'],
'tanggal_lahir' => ['required','date'],
'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)],
'alamat' => ['required','string','max:500'],
'desa_kelurahan_id' => ['required','exists:master_desa_kelurahan,id'],
//'komoditas_id' => ['required','exists:master_komoditas,id'],
'status_perkawinan' => ['required', Rule::enum(Pernikahan::class)],
'ktp' => ['nullable', 'min:16', 'max:16'],
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
'kk' => ['nullable', 'min:16', 'max:16'],
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
'foto' => ['nullable', 'file', 'mimes:jpeg,jpg,png', 'max:2048'],
'nomor_telepon' => ['nullable', 'digits_between:11,13'],
], [
'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.',
]);
$validatedData = $this->validatePetani($request);
if ($request->input('hapus_foto') == '1') {
if ($petani->foto && Storage::exists($petani->foto)) {
Storage::delete($petani->foto);
}
$validatedData['foto'] = null;
}
if ($request->hasFile('file_ktp')) {
$file_ktp = $request->file('file_ktp');
@@ -180,6 +379,10 @@ class PetaniController extends Controller
$validatedData['file_kk'] = $file_kk->getClientOriginalName();
}
if ($request->hasFile('foto')) {
if ($petani->foto && Storage::exists($petani->foto)) {
Storage::delete($petani->foto);
}
$file_foto = $request->file('foto');
$validatedData['foto'] = $file_foto->store('petani/foto');
}
@@ -187,15 +390,41 @@ class PetaniController extends Controller
$petani->update([...$validatedData,
'updated_by' => auth()->id()]);
return redirect()->route('petani.index')->with('success', 'Data petani berhasil diupdate.');
return redirect()->route('petani.edit', $petani->id)->with('success', 'Data petani berhasil diupdate.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
return response()->json(['message' => 'Anda tidak memiliki akses.'], 403);
}
try {
$petani = Petani::findOrFail($id);
if (auth()->user()->role->value === 'fasilitator' && $petani->created_by !== auth()->id()) {
return response()->json(['message' => 'Anda tidak memiliki akses.'], 403);
}
DB::transaction(function () use ($petani) {
$petani->update([
'deleted_by' => auth()->id(),
]);
$petani->delete();
});
return response()->json([
'status' => 'success',
'message' => 'Data petani berhasil dihapus.'
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Gagal menghapus data: ' . $e->getMessage()
], 500);
}
}
public function showImage(string $id, string $field)
@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Enum;
@@ -17,7 +18,9 @@ use App\Models\Pertanian\Lahan;
use App\Models\Wilayah\DesaKelurahan;
use App\Models\Wilayah\Provinsi;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\Tanaman;
use App\Models\Master\JenisTanaman;
use App\Enums\StatusLahan;
use App\Enums\KondisiTanaman;
@@ -62,8 +65,131 @@ class PetaniLahanController extends Controller
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
$komoditas = Komoditas::all();
$tanamanPenaung = JenisTanaman::all();
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
$jenisHamaOtomatis = $autoPestDiseaseData['jenis_hama'];
$jenisPenyakitOtomatis = $autoPestDiseaseData['jenis_penyakit'];
return view('produktivitas.petani.lahan.add', compact('petani', 'lahan', 'provinsi', 'komoditas'));
return view('produktivitas.petani.lahan.add', compact('petani', 'lahan', 'provinsi', 'komoditas', 'tanamanPenaung', 'jenisHamaOtomatis', 'jenisPenyakitOtomatis'));
}
private function autoPestDiseaseData(string|int $petaniId): array
{
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petaniId)->latest()->first();
return [
'jenis_hama' => $this->normalizeArrayValue($praktikBudidaya?->jenis_hama),
'jenis_penyakit' => $this->normalizeArrayValue($praktikBudidaya?->jenis_penyakit),
];
}
private function normalizeArrayValue(mixed $value): array
{
if (is_array($value)) {
return array_values(array_filter($value, fn ($item) => filled($item)));
}
if (is_string($value) && $value !== '') {
$decoded = json_decode($value, true);
if (is_array($decoded)) {
return array_values(array_filter($decoded, fn ($item) => filled($item)));
}
return [$value];
}
return [];
}
private function hasPestDiseaseColumns(string $table): bool
{
return Schema::hasColumn($table, 'jenis_hama')
&& Schema::hasColumn($table, 'jenis_penyakit');
}
private function validateLahan(Request $request)
{
return $request->validate([
'nama' => ['nullable', 'string', 'max:255'],
'status_kepemilikan' => ['nullable', 'string', 'max:255'],
'profile_id' => ['nullable'],
'petani_id' => ['nullable'],
'keluarga_pengelola_id' => ['nullable'],
'adat_pemilik_id' => ['nullable'],
'desa_kelurahan_id' => ['nullable'],
'luas_lahan' => ['required', 'numeric', 'min:0.01'],
'path' => ['nullable', 'string'],
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
'jenis_komoditas_id' => ['required', 'exists:jenis_komoditas,id'],
'varietas_id' => ['nullable', 'exists:master_varietas,id'],
'jumlah_tanaman' => ['nullable', 'numeric', 'min:0'],
'umur_lahan' => ['nullable', 'numeric', 'min:1'],
'umur_tanaman' => ['required', 'numeric', 'min:1'],
'kondisi_tanaman' => ['required', 'string', new Enum(KondisiTanaman::class)],
'serangan_hama' => ['nullable', 'string', new Enum(SeranganHama::class)],
'penyakit_tanaman' => ['nullable', 'string', new Enum(PenyakitTanaman::class)],
'keparahan_serangan' => ['nullable', 'string', new Enum(KeparahanSerangan::class)],
'komoditas_lain' => ['nullable', 'array'],
'komoditas_lain.*' => ['integer', 'exists:master_komoditas,id'],
'tanaman_penaung' => ['nullable', 'array'],
'tanaman_penaung.*' => ['string'],
'foto' => ['nullable', 'array'],
'foto.*' => ['image', 'mimes:jpeg,jpg,png', 'max:2048'],
], [
'nama.string' => 'Nama lahan diisi dengan huruf.',
'nama.max' => 'Nama lahan maksimal 255 karakter.',
'status_kepemilikan.string' => 'Status kepemilikan diisi dengan huruf.',
'status_kepemilikan.max' => 'Status kepemilikan maksimal 255 karakter.',
'luas_lahan.required' => 'Luas lahan wajib diisi.',
'luas_lahan.numeric' => 'Luas lahan diisi dengan angka.',
'luas_lahan.min' => 'Luas lahan minimal 0.01.',
//'jumlah_tanaman.required' => 'Jumlah tanaman wajib diisi.',
'jumlah_tanaman.numeric' => 'Jumlah tanaman diisi dengan angka.',
'jumlah_tanaman.min' => 'Jumlah tanaman minimal 0.',
//'umur_lahan.required' => 'Umur lahan wajib diisi.',
'umur_lahan.numeric' => 'Umur lahan diisi dengan angka.',
'umur_lahan.min' => 'Umur lahan minimal 1.',
'umur_tanaman.required' => 'Umur tanaman wajib diisi.',
'umur_tanaman.numeric' => 'Umur tanaman diisi dengan angka.',
'umur_tanaman.min' => 'Umur tanaman minimal 1.',
'kondisi_tanaman.required' => 'Kondisi tanaman wajib dipilih.',
'kondisi_tanaman.string' => 'Kondisi tanaman diisi dengan huruf.',
'serangan_hama.string' => 'Serangan hama diisi dengan huruf.',
'penyakit_tanaman.string' => 'Penyakit tanaman diisi dengan huruf.',
'keparahan_serangan.string' => 'Keparahan serangan diisi dengan huruf.',
'komoditas_lain.array' => 'Komoditas lain diisi dengan array.',
'komoditas_lain.*.exists' => 'Komoditas lain yang dipilih tidak valid.',
'tanaman_penaung.array' => 'Tanaman penaung diisi dengan array.',
//'tanaman_penaung.*.exists' => 'Tanaman penaung yang dipilih tidak valid.',
'foto.array' => 'Foto diisi dengan array.',
'foto.*.image' => 'Foto harus berupa gambar.',
'foto.*.mimes' => 'Foto harus berupa gambar dengan ekstensi jpeg, jpg, atau png.',
'foto.*.max' => 'Foto maksimal 2MB.',
]);
}
private function syncTanamanPenaung(Request $request)
{
$existing = JenisTanaman::pluck('nama')->toArray();
$tanamanPenaung = collect($request->tanaman_penaung)
->unique()
->reject(fn ($nama) => in_array($nama, $existing))
->map(fn ($nama) => [
'nama' => Str::title($nama),
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
'created_at' => now(),
'updated_at' => now(),
])
->values()
->toArray();
if (!empty($tanamanPenaung)) {
JenisTanaman::insert($tanamanPenaung);
}
}
public function store($petaniId, Request $request)
@@ -74,34 +200,42 @@ class PetaniLahanController extends Controller
$petani = Petani::findOrFail($petaniId);
$validated = $request->validate([
'nama' => ['nullable', 'string', 'max:255'],
'status_kepemilikan' => ['nullable', 'string', 'max:255'],
'profile_id' => ['nullable'],
'petani_id' => ['nullable'],
'keluarga_pengelola_id' => ['nullable'],
'adat_pemilik_id' => ['nullable'],
'desa_kelurahan_id' => ['nullable'],
'luas_lahan' => ['required', 'numeric'],
'path' => ['nullable', 'string'],
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
'jenis_komoditas_id' => ['required', 'exists:jenis_komoditas,id'],
'varietas_id' => ['nullable', 'exists:master_varietas,id'],
'jumlah_tanaman' => ['required', 'numeric'],
'umur_lahan' => ['required', 'numeric'],
'umur_tanaman' => ['required', 'numeric'],
'kondisi_tanaman' => ['required', 'string', new Enum(KondisiTanaman::class)],
'serangan_hama' => ['nullable', 'string', new Enum(SeranganHama::class)],
'penyakit_tanaman' => ['nullable', 'string', new Enum(PenyakitTanaman::class)],
'keparahan_serangan' => ['nullable', 'string', new Enum(KeparahanSerangan::class)],
]);
$request->merge([
'tanaman_penaung' => collect($request->tanaman_penaung)
->map(fn ($nama) => Str::title($nama))
->toArray()
]);
$validated = $this->validateLahan($request);
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
$lahanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Lahan)->getTable());
$tanamanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Tanaman)->getTable());
if ($lahanHasPestDiseaseColumns) {
$validated = array_merge($validated, $autoPestDiseaseData);
}
$komoditas = Komoditas::where('id', $request->komoditas_id)->first();
$lahanCount = Lahan::where('petani_id', $petaniId)
->count();
$lahanUid = strtoupper(Str::random(10));
$foto = null;
if ($request->hasFile('foto')) {
$foto = [];
foreach ($request->file('foto') as $file) {
$path = $file->store('lahan', 'public');
$foto[] = [
'path' => $path,
'file' => $file->getClientOriginalName(),
];
}
}
$this->syncTanamanPenaung($request);
$lahan = Lahan::create([...$validated,
'foto' => $foto && count($foto) > 0 ? json_encode($foto) : null,
'nama' => 'Lahan ' . $komoditas->nama . ' #' . ($lahanCount + 1),
'uid' => 'LHN-' . $petani->uid . '-' . $lahanUid,
'petani_id' => $petaniId,
@@ -109,9 +243,9 @@ class PetaniLahanController extends Controller
'updated_by' => auth()->id()]
);
if ($lahan) {
$data = collect(range(1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid) {
return [
if ($lahan && $lahan->jumlah_tanaman > 0) {
$data = collect(range(1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid, $autoPestDiseaseData, $tanamanHasPestDiseaseColumns) {
$row = [
'lahan_id' => $lahan->id,
'uid' => 'TNM-' . $lahanUid . '-' . strtoupper(Str::random(10)),
'komoditas_id' => $lahan->komoditas_id,
@@ -129,14 +263,31 @@ class PetaniLahanController extends Controller
'created_at' => now(),
'updated_at' => now(),
];
if ($tanamanHasPestDiseaseColumns) {
$row['jenis_hama'] = json_encode($autoPestDiseaseData['jenis_hama'] ?? []);
$row['jenis_penyakit'] = json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []);
}
return $row;
});
$data->chunk(500)->each(function ($chunk) {
Tanaman::insert($chunk->toArray());
});
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil ditambahkan.');
if ($request->ajax()) {
return response()->json([
'success' => true,
'redirect' => route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id]),
'message' => 'Lahan berhasil ditambahkan.'
]);
}
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil ditambahkan.');
}
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil ditambahkan.');
}
public function show(string $petaniId, string $id)
@@ -167,14 +318,19 @@ class PetaniLahanController extends Controller
$lahan = Lahan::where('petani_id', $petaniId)
->orderBy('id', 'asc')
->get();
$lahanSelected = Lahan::where('petani_id', $petaniId)->findOrFail($id);
$komoditas = Komoditas::all();
$tanamanPenaung = JenisTanaman::all();
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
$jenisHamaOtomatis = $autoPestDiseaseData['jenis_hama'];
$jenisPenyakitOtomatis = $autoPestDiseaseData['jenis_penyakit'];
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
return view('produktivitas.petani.lahan.edit', compact('lahan', 'lahanSelected', 'petani', 'komoditas', 'provinsi'));
return view('produktivitas.petani.lahan.edit', compact('lahan', 'lahanSelected', 'petani', 'komoditas', 'provinsi', 'tanamanPenaung', 'jenisHamaOtomatis', 'jenisPenyakitOtomatis'));
}
public function update(Request $request, string $petaniId, string $id)
@@ -186,36 +342,62 @@ class PetaniLahanController extends Controller
$petani = Petani::findOrFail($petaniId);
$lahan = Lahan::where('petani_id', $petaniId)->findOrFail($id);
$validated = $request->validate([
'nama' => ['nullable', 'string', 'max:255'],
'status_kepemilikan' => ['nullable', 'string', 'max:255'],
'profile_id' => ['nullable'],
'petani_id' => ['nullable'],
'keluarga_pengelola_id' => ['nullable'],
'adat_pemilik_id' => ['nullable'],
'desa_kelurahan_id' => ['nullable'],
'luas_lahan' => ['required', 'numeric'],
'path' => ['nullable', 'string'],
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
'jenis_komoditas_id' => ['required', 'exists:jenis_komoditas,id'],
'varietas_id' => ['nullable', 'exists:master_varietas,id'],
'jumlah_tanaman' => ['required', 'numeric'],
'umur_lahan' => ['required', 'numeric'],
'umur_tanaman' => ['required', 'numeric'],
'kondisi_tanaman' => ['required', 'string', new Enum(KondisiTanaman::class)],
'serangan_hama' => ['nullable', 'string', new Enum(SeranganHama::class)],
'penyakit_tanaman' => ['nullable', 'string', new Enum(PenyakitTanaman::class)],
'keparahan_serangan' => ['nullable', 'string', new Enum(KeparahanSerangan::class)],
]);
$request->merge([
'tanaman_penaung' => collect($request->tanaman_penaung)
->map(fn ($nama) => Str::title($nama))
->toArray()
]);
$validated = $this->validateLahan($request);
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
$lahanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Lahan)->getTable());
$tanamanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Tanaman)->getTable());
if ($lahanHasPestDiseaseColumns) {
$validated = array_merge($validated, $autoPestDiseaseData);
}
$komoditas = Komoditas::where('id', $request->komoditas_id)->first();
$tanamanCount = Tanaman::where('lahan_id', $lahan->id)->count();
$lahanCount = Lahan::where('petani_id', $petaniId)->count();
$lahanCount = Lahan::where('petani_id', $petaniId)
->count();
$fotoLama = $lahan->foto ?? [];
if (is_string($fotoLama)) {
$fotoLama = json_decode($fotoLama, true);
}
if (!is_array($fotoLama)) {
$fotoLama = [];
}
if ($request->has('deleted_photos') && is_array($request->deleted_photos)) {
foreach ($request->deleted_photos as $deletedPath) {
if (Storage::disk('public')->exists($deletedPath)) {
Storage::disk('public')->delete($deletedPath);
}
$fotoLama = array_filter($fotoLama, function($item) use ($deletedPath) {
$currentPath = is_array($item) ? ($item['path'] ?? '') : $item;
return $currentPath !== $deletedPath;
});
}
}
$fotoLama = array_values($fotoLama);
if ($request->hasFile('foto')) {
foreach ($request->file('foto') as $file) {
$path = $file->store('lahan', 'public');
$fotoLama[] = [
'path' => $path,
'file' => $file->getClientOriginalName(),
];
}
}
unset($validated['foto']);
$lahan->update([...$validated,
'nama' => 'Lahan ' . $komoditas->nama . ' #' . ($lahanCount + 1),
'petani_id' => $petaniId,
'foto' => count($fotoLama) > 0 ? json_encode($fotoLama) : null,
'updated_by' => auth()->id()]
);
@@ -223,9 +405,11 @@ class PetaniLahanController extends Controller
$lahanUidParts = explode('LHN-', $lahanUid);
$lahanUid = end($lahanUidParts);
if ($lahan) {
$data = collect(range($lahanCount, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid) {
return [
$this->syncTanamanPenaung($request);
if ($lahan && $lahan->jumlah_tanaman > $tanamanCount) {
$data = collect(range($tanamanCount + 1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid, $autoPestDiseaseData, $tanamanHasPestDiseaseColumns) {
$row = [
'lahan_id' => $lahan->id,
'komoditas_id' => $lahan->komoditas_id,
'jenis_komoditas_id' => $lahan->jenis_komoditas_id,
@@ -243,19 +427,56 @@ class PetaniLahanController extends Controller
'created_at' => now(),
'updated_at' => now(),
];
if ($tanamanHasPestDiseaseColumns) {
$row['jenis_hama'] = json_encode($autoPestDiseaseData['jenis_hama'] ?? []);
$row['jenis_penyakit'] = json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []);
}
return $row;
});
$data->chunk(500)->each(function ($chunk) {
Tanaman::insert($chunk->toArray());
});
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil ditambahkan.');
}
if ($tanamanHasPestDiseaseColumns) {
$lahan->tanaman()->update([
'jenis_hama' => json_encode($autoPestDiseaseData['jenis_hama'] ?? []),
'jenis_penyakit' => json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []),
'updated_by' => auth()->id(),
]);
}
if ($request->ajax()) {
return response()->json([
'success' => true,
'redirect' => route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id]),
'message' => 'Lahan berhasil diupdate.'
]);
}
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil diperbarui.');
}
public function destroy(string $id)
public function destroy(string $petaniId, string $id)
{
//
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$lahan = Lahan::findOrFail($id);
$lahan->tanaman()->update([
'deleted_by' => auth()->id()
]);
$lahan->tanaman()->delete();
$lahan->update([
'deleted_by' => auth()->id()
]);
$lahan->delete();
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil dihapus.');
}
public function summary(string $petaniId)
@@ -11,6 +11,9 @@ use Illuminate\Validation\Rules\Enum;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\SurveyPanen;
use App\Models\Pertanian\SurveyProduktivitas;
use App\Models\Pertanian\Komoditas;
use App\Enums\PraktikBudidaya\TenagaKerja;
use App\Enums\PraktikBudidaya\FrekuensiPemangkasanUtama;
@@ -18,6 +21,22 @@ use App\Enums\PraktikBudidaya\FrekuensiPemangkasanPendamping;
use App\Enums\PraktikBudidaya\PemberianPupuk;
use App\Enums\PraktikBudidaya\JenisPupuk;
use App\Enums\PraktikBudidaya\PenyerbukanManual;
use App\Enums\PraktikBudidaya\MetodePenangananHamaPenyakit;
use App\Enums\PraktikBudidaya\IntensitasSeranganHamaPenyakit;
use App\Enums\PraktikBudidaya\WaktuKemunculanTerparahHamaPenyakit;
use App\Enums\PraktikBudidaya\FrekuensiSanitasiLahan;
use App\Enums\PraktikBudidaya\PenangananSisaBuahRanting;
use App\Enums\PraktikBudidaya\PenangananGulma;
use App\Enums\PraktikBudidaya\KondisiPengelolaanAir;
use App\Enums\PraktikBudidaya\KebutuhanAirPerMusim;
use App\Enums\PraktikBudidaya\PemahamanManfaatKonservasi;
use App\Enums\PraktikBudidaya\DasarPenentuanPanen;
use App\Enums\PraktikBudidaya\Bulan;
use App\Enums\PraktikBudidaya\PraktikSortasi;
use App\Enums\PraktikBudidaya\CaraSortasi;
use App\Enums\PraktikBudidaya\TingkatPanenReject;
use App\Enums\PraktikBudidaya\TindakanPanenReject;
use App\Enums\PraktikBudidaya\EstimasiKehilanganHasil;
class PraktikBudidayaController extends Controller
{
@@ -77,6 +96,11 @@ class PraktikBudidayaController extends Controller
$title = 'Petani - Praktik Budidaya';
$petani = Petani::findOrFail($petaniId);
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petaniId)->latest()->first();
$surveyPanen = SurveyPanen:: where('petani_id', $petaniId)->latest()->first();
$komoditas = Komoditas::all();
$surveyProduktivitas = SurveyProduktivitas::where('petani_id', $petaniId)->get();
$teknikPemangkasan = $praktikBudidaya->teknik_pemangkasan ?? [];
$teknikPemangkasanLainnya = collect($teknikPemangkasan)
@@ -86,7 +110,7 @@ class PraktikBudidayaController extends Controller
$alasanTidakPenyerbukanLainnya = collect(explode(', ', $alasanTidakPenyerbukan))
->first(fn ($item) => !in_array($item, $defaultAlasanTidakPenyerbukan));
$frekuensiPemupukan = $praktikBudidaya->frekuensi_pemupukan ?? '';
$frekuensiPemupukan = $praktikBudidaya->frekuensi_pemberian_pupuk ?? '';
$frekuensiPemupukanLainnya = collect(explode(', ', $frekuensiPemupukan))
->first(fn ($item) => !in_array($item, $defaultFrekuensiPemupukan));
@@ -98,8 +122,8 @@ class PraktikBudidayaController extends Controller
$caraPemberianPupukLainnya = collect(explode(', ', $caraPemberianPupuk))
->first(fn ($item) => !in_array($item, $defaultCaraPemberianPupuk));
$sumberAir = $praktikBudidaya->sumber_air ?? '';
$sumberAirLainnya = collect(explode(', ', $sumberAir))
$sumberAir = $praktikBudidaya->sumber_air ?? [];
$sumberAirLainnya = collect($sumberAir)
->first(fn ($item) => !in_array($item, $defaultSumberAir));
$sistemIrigasi = $praktikBudidaya->sistem_irigasi ?? '';
@@ -107,7 +131,7 @@ class PraktikBudidayaController extends Controller
->first(fn ($item) => !in_array($item, $defaultSistemIrigasi));
if (!empty($praktikBudidaya)) {
return view('produktivitas.petani.praktik-budidaya.edit', compact('title', 'petani', 'praktikBudidaya', 'teknikPemangkasanLainnya', 'alasanTidakPenyerbukanLainnya', 'frekuensiPemupukanLainnya', 'waktuPemberianPupukLainnya', 'caraPemberianPupukLainnya', 'sumberAirLainnya', 'sistemIrigasiLainnya'));
return view('produktivitas.petani.praktik-budidaya.edit', compact('title', 'petani', 'praktikBudidaya', 'surveyPanen', 'komoditas', 'surveyProduktivitas', 'teknikPemangkasanLainnya', 'alasanTidakPenyerbukanLainnya', 'frekuensiPemupukanLainnya', 'waktuPemberianPupukLainnya', 'caraPemberianPupukLainnya', 'sumberAirLainnya', 'sistemIrigasiLainnya'));
}
return view('produktivitas.petani.praktik-budidaya.add', compact('title', 'petani', 'teknikPemangkasanLainnya', 'alasanTidakPenyerbukanLainnya', 'frekuensiPemupukanLainnya', 'waktuPemberianPupukLainnya', 'caraPemberianPupukLainnya'));
@@ -154,9 +178,15 @@ class PraktikBudidayaController extends Controller
$teknik_pemangkasan = array_values($teknik_pemangkasan);
if ((int) ($validatedData['frekuensi_pemangkasan_komoditas_utama'] ?? 0) === FrekuensiPemangkasanUtama::TIDAK_PERNAH->value) {
$teknik_pemangkasan = [];
}
$petani = Petani::findOrFail($petaniId);
$praktikBudidaya = $petani->praktik_budidaya()->create([
$pemupukanTidakPernah = (int) ($validatedData['pemberian_pupuk'] ?? 0) === PemberianPupuk::TIDAK_PERNAH->value;
$praktikBudidaya = $petani->praktikBudidaya()->create([
'petani_id' => $petani->id,
'uid' => 'PB-' . strtoupper(Str::random(10)),
'tenaga_kerja' => $validatedData['tenaga_kerja'],
@@ -169,12 +199,40 @@ class PraktikBudidayaController extends Controller
'deskripsi_penyerbukan_manual' => $validatedData['alasan_tidak_penyerbukan'] ?? null,
'tingkat_keberhasilan_penyerbukan' => $validatedData['tingkat_keberhasilan_penyerbukan'] ?? null,
'pemberian_pupuk' => $validatedData['pemberian_pupuk'] ?? null,
'jenis_pupuk' => $validatedData['jenis_pupuk'] ?? null,
'frekuensi_pemberian_pupuk' => ($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null),
'estimasi_dosis_pupuk_organik' => $validatedData['estimasi_dosis_pupuk_organik'] ?? null,
'estimasi_dosis_pupuk_anorganik' => $validatedData['estimasi_dosis_pupuk_anorganik'] ?? null,
'waktu_pemberian_pupuk' => ($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null),
'cara_pemberian_pupuk' => ($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null),
'jenis_pupuk' => $pemupukanTidakPernah ? null : ($validatedData['jenis_pupuk'] ?? null),
'frekuensi_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null)),
'estimasi_dosis_pupuk_organik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_organik'] ?? null),
'estimasi_dosis_pupuk_anorganik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_anorganik'] ?? null),
'waktu_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null)),
'cara_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null)),
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
], [
'string' => ':attribute harus berupa teks.',
'integer' => ':attribute harus berupa angka.',
'array' => ':attribute harus berupa pilihan.',
'enum' => 'Pilihan :attribute tidak valid.',
], [
'tenaga_kerja' => 'tenaga kerja',
'frekuensi_pemangkasan_komoditas_utama' => 'frekuensi pemangkasan komoditas utama',
'teknik_pemangkasan' => 'teknik pemangkasan',
'teknik_pemangkasan_lainnya' => 'teknik pemangkasan lainnya',
'frekuensi_pemangkasan_pohon_penaung' => 'frekuensi pemangkasan pohon penaung',
'penyerbukan_manual' => 'penyerbukan manual',
'alasan_tidak_penyerbukan' => 'alasan tidak melakukan penyerbukan manual',
'alasan_tidak_penyerbukan_lainnya' => 'alasan lainnya tidak melakukan penyerbukan manual',
'tingkat_keberhasilan_penyerbukan' => 'tingkat keberhasilan penyerbukan',
'pemberian_pupuk' => 'pemberian pupuk',
'jenis_pupuk' => 'jenis pupuk',
'frekuensi_pemupukan' => 'frekuensi pemupukan',
'frekuensi_pemupukan_lainnya' => 'frekuensi pemupukan lainnya',
'estimasi_dosis_pupuk_organik' => 'estimasi dosis pupuk organik',
'estimasi_dosis_pupuk_anorganik' => 'estimasi dosis pupuk anorganik',
'waktu_pemberian_pupuk' => 'waktu pemberian pupuk',
'waktu_pemberian_pupuk_lainnya' => 'waktu pemberian pupuk lainnya',
'cara_pemberian_pupuk' => 'cara pemberian pupuk',
'cara_pemberian_pupuk_lainnya' => 'cara pemberian pupuk lainnya',
]);
return redirect()->route('petani.praktik-budidaya.edit', [$petani->id , $praktikBudidaya->id])
@@ -237,6 +295,11 @@ class PraktikBudidayaController extends Controller
$title = 'Petani - Praktik Budidaya';
$petani = Petani::findOrFail($petaniId);
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petaniId)->where('id', $praktikBudidayaId)->latest()->first();
$surveyPanen = SurveyPanen:: where('petani_id', $petaniId)->latest()->first();
$komoditas = Komoditas::all();
$surveyProduktivitas = SurveyProduktivitas::where('petani_id', $petaniId)->get();
$teknikPemangkasan = $praktikBudidaya->teknik_pemangkasan ?? [];
$teknikPemangkasanLainnya = collect($teknikPemangkasan)
@@ -246,7 +309,7 @@ class PraktikBudidayaController extends Controller
$alasanTidakPenyerbukanLainnya = collect(explode(', ', $alasanTidakPenyerbukan))
->first(fn ($item) => !in_array($item, $defaultAlasanTidakPenyerbukan));
$frekuensiPemupukan = $praktikBudidaya->frekuensi_pemupukan ?? '';
$frekuensiPemupukan = $praktikBudidaya->frekuensi_pemberian_pupuk ?? '';
$frekuensiPemupukanLainnya = collect(explode(', ', $frekuensiPemupukan))
->first(fn ($item) => !in_array($item, $defaultFrekuensiPemupukan));
@@ -259,13 +322,382 @@ class PraktikBudidayaController extends Controller
->first(fn ($item) => !in_array($item, $defaultCaraPemberianPupuk));
$sumberAir = $praktikBudidaya->sumber_air ?? '';
$sumberAirLainnya = collect(explode(', ', $sumberAir))
$sumberAirLainnya = collect($sumberAir)
->first(fn ($item) => !in_array($item, $defaultSumberAir));
$sistemIrigasi = $praktikBudidaya->sistem_irigasi ?? '';
$sistemIrigasiLainnya = collect(explode(', ', $sistemIrigasi))
->first(fn ($item) => !in_array($item, $defaultSistemIrigasi));
return view('produktivitas.petani.praktik-budidaya.edit', compact('title', 'petani', 'praktikBudidaya', 'teknikPemangkasanLainnya', 'alasanTidakPenyerbukanLainnya', 'frekuensiPemupukanLainnya', 'waktuPemberianPupukLainnya', 'caraPemberianPupukLainnya', 'sumberAirLainnya', 'sistemIrigasiLainnya'));
return view('produktivitas.petani.praktik-budidaya.edit', compact('title', 'petani', 'praktikBudidaya', 'surveyPanen', 'komoditas', 'surveyProduktivitas', 'teknikPemangkasanLainnya', 'alasanTidakPenyerbukanLainnya', 'frekuensiPemupukanLainnya', 'waktuPemberianPupukLainnya', 'caraPemberianPupukLainnya', 'sumberAirLainnya', 'sistemIrigasiLainnya'));
}
public function update($petaniId, $praktikBudidayaId, Request $request)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petaniId)
->where('id', $praktikBudidayaId)
->firstOrFail();
$validatedData = $request->validate([
// 2.2 Persiapan Lahan & Penanaman
'tenaga_kerja' => ['nullable', 'string', new Enum(TenagaKerja::class)],
// 2.3 Praktik Pemangkasan & Penyerbukan
'frekuensi_pemangkasan_komoditas_utama' => ['nullable', 'integer', new Enum(FrekuensiPemangkasanUtama::class)],
'teknik_pemangkasan' => ['nullable', 'array'],
'teknik_pemangkasan.*' => ['string'],
'teknik_pemangkasan_lainnya' => ['nullable', 'string'],
'frekuensi_pemangkasan_pohon_penaung' => ['nullable', 'string', new Enum(FrekuensiPemangkasanPendamping::class)],
'penyerbukan_manual' => ['nullable', 'string', new Enum(PenyerbukanManual::class)],
'alasan_tidak_penyerbukan' => ['nullable', 'string'],
'alasan_tidak_penyerbukan_lainnya' => ['nullable', 'string'],
'tingkat_keberhasilan_penyerbukan' => ['nullable', 'string'],
// 2.4 Praktik Pemupukan
'pemberian_pupuk' => ['nullable', 'integer', new Enum(PemberianPupuk::class)],
'jenis_pupuk' => ['nullable', 'string', new Enum(JenisPupuk::class)],
'frekuensi_pemupukan' => ['nullable', 'string'],
'frekuensi_pemupukan_lainnya' => ['nullable', 'string'],
'estimasi_dosis_pupuk_organik' => ['nullable', 'numeric'],
'estimasi_dosis_pupuk_anorganik' => ['nullable', 'numeric'],
'waktu_pemberian_pupuk' => ['nullable', 'string'],
'waktu_pemberian_pupuk_lainnya' => ['nullable', 'string'],
'cara_pemberian_pupuk' => ['nullable', 'string'],
'cara_pemberian_pupuk_lainnya' => ['nullable', 'string'],
// 2.5 Pengendalian Organisme Pengganggu Tumbuhan (OPT)
'metode_penanganan_hama_penyakit' => ['nullable', 'integer', new Enum(MetodePenangananHamaPenyakit::class)],
'jenis_hama' => ['nullable', 'array'],
'jenis_penyakit' => ['nullable', 'array'],
'intensitas_serangan' => ['nullable', 'string', new Enum(IntensitasSeranganHamaPenyakit::class)],
'waktu_kemunculan_terparah' => ['nullable', 'string', new Enum(WaktuKemunculanTerparahHamaPenyakit::class)],
'metode_paling_sering_dilakukan' => ['nullable', 'string'],
'volume_obat_obatan_padat' => ['nullable', 'numeric'],
'volume_obat_obatan_cair' => ['nullable', 'numeric'],
// 2.6 Sanitasi Kebun
'frekuensi_sanitasi_lahan' => ['nullable', 'integer', new Enum(FrekuensiSanitasiLahan::class)],
'penanganan_sisa_buah_ranting' => ['nullable', 'string', new Enum(PenangananSisaBuahRanting::class)],
'penanganan_gulma' => ['nullable', 'integer', new Enum(PenangananGulma::class)],
// 2.7 Pengelolaan Air & Irigasi
'kondisi_pengelolaan_air_irigasi' => ['nullable', 'integer', new Enum(KondisiPengelolaanAir::class)],
'sumber_air' => ['nullable', 'array'],
'sumber_air_lainnya' => ['nullable', 'string'],
'sistem_irigasi' => ['nullable', 'string'],
'sistem_irigasi_lainnya' => ['nullable', 'string'],
'kebutuhan_air_permusim' => ['nullable', 'string', new Enum(KebutuhanAirPerMusim::class)],
'kebiasaan_konservasi_tanah' => ['nullable', 'array'],
'kebiasaan_konservasi_tanah.*' => ['string'],
'pemahaman_manfaat_konservasi' => ['nullable', 'string', new Enum(PemahamanManfaatKonservasi::class)],
'upah_tenaga_kerja' => ['nullable', 'numeric'],
'biaya_bibit' => ['nullable', 'numeric'],
'biaya_pupuk' => ['nullable', 'numeric'],
'biaya_pestisida' => ['nullable', 'numeric'],
'biaya_sewa_alat' => ['nullable', 'numeric'],
'biaya_penyediaan_air' => ['nullable', 'numeric'],
'biaya_lainnya' => ['nullable', 'numeric'],
'total_estimasi_biaya_produksi' => ['nullable', 'numeric'],
'dasar_penentuan_panen' => ['nullable', 'integer', new Enum(DasarPenentuanPanen::class)],
'frekuensi_panen_pertahun' => ['nullable', 'integer'],
'bulan_panen_utama' => ['nullable', 'string', new Enum(Bulan::class)],
'hasil_komoditas_utama_permusim' => ['nullable', 'numeric'],
'hasil_komoditas_lainnya_permusim' => ['nullable', 'numeric'],
'disimpan_untuk_sendiri' => ['nullable', 'boolean'],
'hasil_panen_disimpan' => ['nullable', 'numeric'],
'tujuan_disimpan' => ['nullable', 'string'],
'praktik_sortasi' => ['nullable', 'integer', new Enum(PraktikSortasi::class)],
'bentuk_sortasi' => ['nullable', 'string', new Enum(CaraSortasi::class)],
'tingkat_reject' => ['nullable', 'integer', new Enum(TingkatPanenReject::class)],
'hasil_panen_reject' => ['nullable', 'numeric'],
'tindakan_hasil_reject' => ['nullable', 'string', new Enum(TindakanPanenReject::class)],
'upah_tenaga_panen' => ['nullable', 'numeric'],
'biaya_pengangkutan' => ['nullable', 'numeric'],
'biaya_wadah_kemasan' => ['nullable', 'numeric'],
'biaya_sewa_alat_panen' => ['nullable', 'numeric'],
'biaya_panen_lainnya' => ['nullable', 'numeric'],
'total_estimasi_biaya_panen' => ['nullable', 'numeric'],
'estimasi_kehilangan_akibat_opt' => ['nullable', 'string', new Enum(EstimasiKehilanganHasil::class)],
'estimasi_kehilangan_akibat_hama' => ['nullable', 'string', new Enum(EstimasiKehilanganHasil::class)],
], [
'string' => ':attribute harus berupa teks.',
'integer' => ':attribute harus berupa angka.',
'numeric' => ':attribute harus berupa angka.',
'array' => ':attribute harus berupa daftar pilihan.',
'boolean' => ':attribute harus bernilai ya atau tidak.',
'enum' => 'Pilihan :attribute tidak valid.',
'teknik_pemangkasan.*.string' => 'Setiap pilihan teknik pemangkasan harus berupa teks.',
'kebiasaan_konservasi_tanah.*.string' => 'Setiap pilihan kebiasaan konservasi tanah harus berupa teks.',
], [
// 2.2 Persiapan Lahan & Penanaman
'tenaga_kerja' => 'tenaga kerja',
// 2.3 Praktik Pemangkasan & Penyerbukan
'frekuensi_pemangkasan_komoditas_utama' => 'frekuensi pemangkasan komoditas utama',
'teknik_pemangkasan' => 'teknik pemangkasan',
'teknik_pemangkasan.*' => 'pilihan teknik pemangkasan',
'teknik_pemangkasan_lainnya' => 'teknik pemangkasan lainnya',
'frekuensi_pemangkasan_pohon_penaung' => 'frekuensi pemangkasan pohon penaung',
'penyerbukan_manual' => 'penyerbukan manual',
'alasan_tidak_penyerbukan' => 'alasan tidak melakukan penyerbukan manual',
'alasan_tidak_penyerbukan_lainnya' => 'alasan lainnya tidak melakukan penyerbukan manual',
'tingkat_keberhasilan_penyerbukan' => 'tingkat keberhasilan penyerbukan',
// 2.4 Praktik Pemupukan
'pemberian_pupuk' => 'pemberian pupuk',
'jenis_pupuk' => 'jenis pupuk',
'frekuensi_pemupukan' => 'frekuensi pemupukan',
'frekuensi_pemupukan_lainnya' => 'frekuensi pemupukan lainnya',
'estimasi_dosis_pupuk_organik' => 'estimasi dosis pupuk organik',
'estimasi_dosis_pupuk_anorganik' => 'estimasi dosis pupuk anorganik',
'waktu_pemberian_pupuk' => 'waktu pemberian pupuk',
'waktu_pemberian_pupuk_lainnya' => 'waktu pemberian pupuk lainnya',
'cara_pemberian_pupuk' => 'cara pemberian pupuk',
'cara_pemberian_pupuk_lainnya' => 'cara pemberian pupuk lainnya',
// 2.5 Pengendalian OPT
'metode_penanganan_hama_penyakit' => 'metode penanganan hama dan penyakit',
'jenis_hama' => 'jenis hama',
'jenis_penyakit' => 'jenis penyakit',
'intensitas_serangan' => 'intensitas serangan',
'waktu_kemunculan_terparah' => 'waktu kemunculan terparah',
'metode_paling_sering_dilakukan' => 'metode yang paling sering dilakukan',
'volume_obat_obatan_padat' => 'volume obat-obatan padat per musim',
'volume_obat_obatan_cair' => 'volume obat-obatan cair per musim',
// 2.6 Sanitasi Kebun
'frekuensi_sanitasi_lahan' => 'frekuensi sanitasi lahan',
'penanganan_sisa_buah_ranting' => 'penanganan sisa buah dan ranting',
'penanganan_gulma' => 'penanganan gulma',
// 2.7 Pengelolaan Air & Irigasi
'kondisi_pengelolaan_air_irigasi' => 'kondisi pengelolaan air dan irigasi',
'sumber_air' => 'sumber air',
'sumber_air_lainnya' => 'sumber air lainnya',
'sistem_irigasi' => 'sistem irigasi',
'sistem_irigasi_lainnya' => 'sistem irigasi lainnya',
'kebutuhan_air_permusim' => 'kebutuhan air per musim',
'kebiasaan_konservasi_tanah' => 'kebiasaan konservasi tanah',
'kebiasaan_konservasi_tanah.*' => 'pilihan kebiasaan konservasi tanah',
'pemahaman_manfaat_konservasi' => 'pemahaman manfaat konservasi',
// Biaya Produksi
'upah_tenaga_kerja' => 'upah tenaga kerja',
'biaya_bibit' => 'biaya bibit',
'biaya_pupuk' => 'biaya pupuk',
'biaya_pestisida' => 'biaya pestisida',
'biaya_sewa_alat' => 'biaya sewa alat',
'biaya_penyediaan_air' => 'biaya penyediaan air',
'biaya_lainnya' => 'biaya lainnya',
'total_estimasi_biaya_produksi' => 'total estimasi biaya produksi',
// Panen
'dasar_penentuan_panen' => 'dasar penentuan panen',
'frekuensi_panen_pertahun' => 'frekuensi panen per tahun',
'bulan_panen_utama' => 'bulan panen utama',
'hasil_komoditas_utama_permusim' => 'hasil komoditas utama per musim',
'hasil_komoditas_lainnya_permusim' => 'hasil komoditas lainnya per musim',
'disimpan_untuk_sendiri' => 'disimpan untuk sendiri',
'hasil_panen_disimpan' => 'hasil panen yang disimpan',
'tujuan_disimpan' => 'tujuan disimpan',
// Sortasi & Reject
'praktik_sortasi' => 'praktik sortasi',
'bentuk_sortasi' => 'bentuk sortasi',
'tingkat_reject' => 'tingkat reject',
'hasil_panen_reject' => 'hasil panen reject',
'tindakan_hasil_reject' => 'tindakan terhadap hasil reject',
// Biaya Panen
'upah_tenaga_panen' => 'upah tenaga panen',
'biaya_pengangkutan' => 'biaya pengangkutan',
'biaya_wadah_kemasan' => 'biaya wadah kemasan',
'biaya_sewa_alat_panen' => 'biaya sewa alat panen',
'biaya_panen_lainnya' => 'biaya panen lainnya',
'total_estimasi_biaya_panen' => 'total estimasi biaya panen',
// Kehilangan Hasil
'estimasi_kehilangan_akibat_opt' => 'estimasi kehilangan akibat OPT',
'estimasi_kehilangan_akibat_hama' => 'estimasi kehilangan akibat hama',
]);
$dataPraktikBudidaya = [];
$dataSurveyPanen = [];
$petani = Petani::findOrFail($petaniId);
if ($request->has('tenaga_kerja')) {
$teknik_pemangkasan = $request->teknik_pemangkasan ?? [];
if (($key = array_search('Lainnya', $teknik_pemangkasan)) !== false) {
unset($teknik_pemangkasan[$key]);
if (!empty($request->teknik_pemangkasan_lainnya)) {
$teknik_pemangkasan[] = $request->teknik_pemangkasan_lainnya;
}
}
$teknikPemangkasan = array_values($teknik_pemangkasan);
if ((int) ($validatedData['frekuensi_pemangkasan_komoditas_utama'] ?? 0) === FrekuensiPemangkasanUtama::TIDAK_PERNAH->value) {
$teknikPemangkasan = [];
}
$pemupukanTidakPernah = (int) ($validatedData['pemberian_pupuk'] ?? 0) === PemberianPupuk::TIDAK_PERNAH->value;
$dataPraktikBudidaya = [
'tenaga_kerja' => $validatedData['tenaga_kerja'],
'frekuensi_pemangkasan_utama' => $validatedData['frekuensi_pemangkasan_komoditas_utama'],
'teknik_pemangkasan' => $teknikPemangkasan,
'frekuensi_pemangkasan_penaung' => $validatedData['frekuensi_pemangkasan_pohon_penaung'],
'penyerbukan_manual' => $validatedData['penyerbukan_manual'] ?? null,
'deskripsi_penyerbukan_manual' => $validatedData['alasan_tidak_penyerbukan'] ?? null,
'tingkat_keberhasilan_penyerbukan' => $validatedData['tingkat_keberhasilan_penyerbukan'] ?? null,
'pemberian_pupuk' => $validatedData['pemberian_pupuk'] ?? null,
'jenis_pupuk' => $pemupukanTidakPernah ? null : ($validatedData['jenis_pupuk'] ?? null),
'frekuensi_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null)),
'estimasi_dosis_pupuk_organik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_organik'] ?? null),
'estimasi_dosis_pupuk_anorganik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_anorganik'] ?? null),
'waktu_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null)),
'cara_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null)),
];
}
if ($request->has('metode_penanganan_hama_penyakit')) {
$sumber_air = $request->sumber_air ?? [];
if (($key = array_search('Lainnya', $sumber_air)) !== false) {
unset($sumber_air[$key]);
if (!empty($request->sumber_air_lainnya)) {
$sumber_air[] = $request->sumber_air_lainnya;
}
}
$sumberAir = array_values($sumber_air);
$dataPraktikBudidaya = [
'metode_penanganan_opt' => $validatedData['metode_penanganan_hama_penyakit'] ?? null,
'jenis_hama' => $validatedData['jenis_hama'] ?? [],
'jenis_penyakit' => $validatedData['jenis_penyakit'] ?? [],
'intensitas_serangan' => $validatedData['intensitas_serangan'] ?? null,
'waktu_kemunculan_terparah' => $validatedData['waktu_kemunculan_terparah'] ?? null,
'metode_paling_sering_dilakukan' => $validatedData['metode_paling_sering_dilakukan'] ?? null,
'volume_obat_obatan_padat_permusim' => $validatedData['volume_obat_obatan_padat'] ?? null,
'volume_obat_obatan_cair_permusim' => $validatedData['volume_obat_obatan_cair'] ?? null,
'frekuensi_sanitasi_lahan' => $validatedData['frekuensi_sanitasi_lahan'] ?? null,
'penanganan_sisa_buah_ranting' => $validatedData['penanganan_sisa_buah_ranting'] ?? null,
'penanganan_gulma' => $validatedData['penanganan_gulma'] ?? null,
'kondisi_pengelolaan_air' => $validatedData['kondisi_pengelolaan_air_irigasi'] ?? null,
'sumber_air' => $sumberAir,
'sistem_irigasi' => ($validatedData['sistem_irigasi'] ?? null) === 'Lainnya' ? ($validatedData['sistem_irigasi_lainnya'] ?? null) : ($validatedData['sistem_irigasi'] ?? null),
'kebutuhan_air_permusim' => $validatedData['kebutuhan_air_permusim'] ?? null,
'kebiasaan_konservasi_tanah' => $validatedData['kebiasaan_konservasi_tanah'] ?? [],
'pemahaman_manfaat_konservasi_tanah' => $validatedData['pemahaman_manfaat_konservasi'] ?? null,
];
}
if ($request->has('upah_tenaga_kerja')) {
$dataPraktikBudidaya = [
'upah_tenaga_kerja' => $validatedData['upah_tenaga_kerja'] ?? null,
'biaya_bibit' => $validatedData['biaya_bibit'] ?? null,
'biaya_pupuk' => $validatedData['biaya_pupuk'] ?? null,
'biaya_obat_obatan' => $validatedData['biaya_pestisida'] ?? null,
'biaya_sewa_alat_produksi' => $validatedData['biaya_sewa_alat'] ?? null,
'biaya_penyediaan_air' => $validatedData['biaya_penyediaan_air'] ?? null,
'biaya_produksi_lainnya' => $validatedData['biaya_lainnya'] ?? null,
'total_estimasi_biaya_produksi' => $validatedData['total_estimasi_biaya_produksi'] ?? null,
];
}
if ($request->has('dasar_penentuan_panen')) {
$dataSurveyPanen = [
'dasar_penentuan_panen' => $validatedData['dasar_penentuan_panen'] ?? null,
'frekuensi_panen_pertahun' => $validatedData['frekuensi_panen_pertahun'] ?? null,
'bulan_panen_utama' => $validatedData['bulan_panen_utama'] ?? null,
'hasil_komoditas_utama_permusim' => $validatedData['hasil_komoditas_utama_permusim'] ?? null,
'hasil_komoditas_lainnya_permusim' => $validatedData['hasil_komoditas_lainnya_permusim'] ?? null,
'disimpan_untuk_sendiri' => $validatedData['disimpan_untuk_sendiri'] ?? false,
'hasil_panen_disimpan' => $validatedData['hasil_panen_disimpan'] ?? null,
'tujuan_disimpan' => $validatedData['tujuan_disimpan'] ?? null,
];
}
if ($request->has('praktik_sortasi')) {
$dataSurveyPanen = [
'praktik_sortasi' => $validatedData['praktik_sortasi'] ?? null,
'bentuk_sortasi' => $validatedData['bentuk_sortasi'] ?? null,
'hasil_panen_reject' => $validatedData['tingkat_reject'] ?? null,
'jumlah_panen_reject' => $validatedData['hasil_panen_reject'] ?? null,
'tindakan_panen_reject' => $validatedData['tindakan_hasil_reject'] ?? null,
'upah_tenaga_panen' => $validatedData['upah_tenaga_panen'] ?? null,
'biaya_pengangkutan_panen' => $validatedData['biaya_pengangkutan'] ?? null,
'biaya_wadah_panen' => $validatedData['biaya_wadah_kemasan'] ?? null,
'biaya_sewa_alat_panen' => $validatedData['biaya_sewa_alat_panen'] ?? null,
'biaya_panen_lainnya' => $validatedData['biaya_panen_lainnya'] ?? null,
'total_estimasi_biaya_panen' => $validatedData['total_estimasi_biaya_panen'] ?? null,
];
}
if ($request->has('estimasi_kehilangan_akibat_opt')) {
$dataSurveyPanen = [
'estimasi_kehilangan_akibat_opt' => $validatedData['estimasi_kehilangan_akibat_opt'] ?? null,
'estimasi_kehilangan_akibat_hama_penyakit' => $validatedData['estimasi_kehilangan_akibat_hama'] ?? null,
];
}
if (!empty($dataPraktikBudidaya)) {
$dataPraktikBudidaya['updated_by'] = auth()->id();
$praktikBudidaya->update($dataPraktikBudidaya);
}
if (!empty($dataSurveyPanen)) {
$surveyPanen = SurveyPanen::where('petani_id', $petani->id)->first();
if ($surveyPanen) {
$dataSurveyPanen['updated_by'] = auth()->id();
$surveyPanen->update($dataSurveyPanen);
} else {
SurveyPanen::create(array_merge([
'uid' => 'SP-' . strtoupper(Str::random(10)),
'petani_id' => $petani->id,
'created_by' => auth()->id(),
'updated_by_by' => auth()->id()
], $dataSurveyPanen));
}
}
return $this->redirectBackToFragment($request)->with('success', 'Data berhasil diperbarui.');
}
private function redirectBackToFragment(Request $request)
{
$fragment = $request->input('redirect_fragment');
if (!is_string($fragment) || !preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
return back();
}
return redirect()->to(Str::before(url()->previous(), '#') . '#' . $fragment);
}
}
@@ -0,0 +1,978 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\SkorPetani;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Tanaman;
use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\SurveyPanen;
use App\Models\Pertanian\SosialEkonomi;
use App\Models\Pertanian\SurveyPelatihan;
use App\Models\Pertanian\SurveySertifikasi;
use App\Models\Pertanian\AksesPasar;
use App\Models\Pertanian\HargaJualPanen;
use App\Models\Pertanian\PendapatanKomoditas;
use App\Models\Wilayah\DesaKelurahan;
use App\Models\Profile;
use App\Enums\PraktikBudidaya\FrekuensiPemangkasanUtama;
use App\Enums\PraktikBudidaya\PemberianPupuk;
use App\Enums\PraktikBudidaya\MetodePenangananHamaPenyakit;
use App\Enums\PraktikBudidaya\FrekuensiSanitasiLahan;
use App\Enums\PraktikBudidaya\PenangananGulma;
use App\Enums\PraktikBudidaya\KondisiPengelolaanAir;
use App\Enums\SosialEkonomi\SkorKecukupanPenghasilan;
use App\Enums\SosialEkonomi\SkorKemampuanMenabung;
use App\Enums\SosialEkonomi\TerdaftarKelompokTani;
use App\Enums\SosialEkonomi\PeranDalamKelompok;
use App\Enums\SosialEkonomi\StatusRekeningTabungan;
use App\Enums\SosialEkonomi\PengalamanKreditKebun;
use App\Enums\SosialEkonomi\KeterikatanHutangPanen;
use App\Enums\SosialEkonomi\PraktikIjon;
use App\Enums\SosialEkonomi\KebebasanMemilihPembeli;
class ProfilPetaniController extends Controller
{
private const DIMENSION_LOW_MAX = 2.49;
private const DIMENSION_MEDIUM_MAX = 3.49;
public function index(int $id)
{
$petani = Petani::findOrFail($id);
$skorPetani = SkorPetani::where('petani_id', $id)->first();
$enumerator = Profile::where('user_id', $petani->created_by)->first();
$komoditasUtama = Komoditas::where('id', $petani->komoditas_id)->first();
$lahan = Lahan::query()
->select(['id', 'petani_id', 'nama', 'path', 'luas_lahan', 'komoditas_id', 'desa_kelurahan_id'])
->with([
'komoditas:id,nama',
'desaKelurahan:id,nama,path,kecamatan_id',
'desaKelurahan.kecamatan:id,nama,path',
])
->where('petani_id', $id)
->get();
$desaKelurahan = DesaKelurahan::with('kecamatan')
->where('id', $petani->desa_kelurahan_id)
->first();
$ktpMasked = $petani->ktp ? Str::mask($petani->ktp, '*', 4, -4) : '-';
$assessmentFields = [
'skor_kesiapan_adopsi',
'skor_d2_sistem_praktik_budidaya',
'skor_d4_sosial_ekonomi',
'skor_d6_rantai_nilai_akses_pasar',
'skor_komp_1_d4_k3_motivasi_kapasitas_adopsi',
'skor_komp_2_d4_k2_ekonomi_rumah_tangga',
'skor_komp_3_d2_praktik_budidaya_gap',
'skor_komp_4_d4_k4_keterlibatan_kelembagaan',
'skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan',
'skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar',
];
$hasAssessment = $skorPetani !== null && collect($assessmentFields)->every(
fn (string $field) => $skorPetani->{$field} !== null
);
$profiling = $hasAssessment ? $this->buildProfiling($skorPetani) : null;
$tanaman = Tanaman::where('petani_id', $petani->id)
->where('komoditas_id', $petani->komoditas_id)
->first();
if ($tanaman?->updated_at && $tanaman->umur_tanaman !== null) {
$tahunInputTanaman = $tanaman->updated_at->year;
$umurTanaman = (int) $tanaman->umur_tanaman;
$tahunSekarang = now()->year;
$tahunTanam = $tahunInputTanaman - $umurTanaman;
$umurTanaman = $tahunSekarang - $tahunTanam;
} else {
$tahunTanam = null;
$umurTanaman = null;
}
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petani->id)->first();
$surveyPanen = SurveyPanen::where('petani_id', $petani->id)->first();
$sosialEkonomi = SosialEkonomi::where('petani_id', $id)->first();
$indikatorOnFarm = collect();
if ($praktikBudidaya) {
$indikatorOnFarm = collect([
[
'key' => 'pemangkasan',
'nama' => 'Pemangkasan',
'skor' => $praktikBudidaya->frekuensi_pemangkasan_utama,
'deskripsi' => FrekuensiPemangkasanUtama::tryFrom(
(int) $praktikBudidaya->frekuensi_pemangkasan_utama
)?->label(),
],
[
'key' => 'pemupukan',
'nama' => 'Pemupukan',
'skor' => $praktikBudidaya->pemberian_pupuk,
'deskripsi' => PemberianPupuk::tryFrom(
(int) $praktikBudidaya->pemberian_pupuk
)?->label(),
],
[
'key' => 'opt',
'nama' => 'Hama / Penyakit',
'skor' => $praktikBudidaya->metode_penanganan_opt,
'deskripsi' => MetodePenangananHamaPenyakit::tryFrom(
(int) $praktikBudidaya->metode_penanganan_opt
)?->label(),
],
[
'key' => 'sanitasi',
'nama' => 'Sanitasi Kebun',
'skor' => $praktikBudidaya->frekuensi_sanitasi_lahan,
'deskripsi' => FrekuensiSanitasiLahan::tryFrom(
(int) $praktikBudidaya->frekuensi_sanitasi_lahan
)?->label(),
],
[
'key' => 'gulma',
'nama' => 'Penanganan Gulma',
'skor' => $praktikBudidaya->penanganan_gulma,
'deskripsi' => PenangananGulma::tryFrom(
(int) $praktikBudidaya->penanganan_gulma
)?->label(),
],
[
'key' => 'air',
'nama' => 'Pengelolaan Air',
'skor' => $praktikBudidaya->kondisi_pengelolaan_air,
'deskripsi' => KondisiPengelolaanAir::tryFrom(
(int) $praktikBudidaya->kondisi_pengelolaan_air
)?->label(),
],
])
->map(function ($item) {
$skor = $item['skor'] !== null
? (int) $item['skor']
: null;
return array_merge($item, [
'skor' => $skor,
'persentase' => $skor !== null
? ($skor / 5) * 100
: 0,
'color' => $this->getScoreColor($skor),
]);
});
}
$detailOnFarm = [
'jenis_hama' => $praktikBudidaya?->jenis_hama ?? [],
'jenis_penyakit' => $praktikBudidaya?->jenis_penyakit ?? [],
'konservasi_tanah' => $praktikBudidaya?->kebiasaan_konservasi_tanah ?? [],
];
$d4SosialEkonomi = null;
$terdaftarKelompok = $sosialEkonomi?->terdaftar_kelompok_tani;
$peranKelompok = $sosialEkonomi?->peran_dalam_kelompok;
if ($sosialEkonomi) {
$kecukupanPenghasilan = $sosialEkonomi->skor_kecukupan_penghasilan;
$kemampuanMenabung = $sosialEkonomi->skor_kemampuan_menabung;
$rekening = $sosialEkonomi->status_rekening_tabungan;
$kredit = $sosialEkonomi->pengalaman_kredit_kebun;
$hutangPanen = $sosialEkonomi->keterikatan_hutang_panen;
$ijon = $sosialEkonomi->praktik_ijon;
$programKeuanganRaw = $sosialEkonomi->status_program_keuangan_formal ?? [];
$namaProgram = [
'pkh' => 'PKH',
'blt' => 'BLT',
'kur' => 'KUR',
'kredit_koperasi_resmi' => 'Kredit Koperasi',
'credit_union_cu' => 'Credit Union (CU)',
'pinjaman_bumdes' => 'Pinjaman BUMDes',
'modal_hibah_dinas_pertanian' => 'Hibah Dinas Pertanian',
];
$programBantuan = collect($programKeuanganRaw)->map(function ($status, $key) use ($namaProgram) {
$statusMeta = match ($status) {
'sedang_menerima' => [
'label' => 'Sedang Menerima',
'short' => 'Aktif',
'color' => 'success',
'icon' => 'tabler-circle-check',
],
'pernah_tapi_tidak_lagi' => [
'label' => 'Pernah, Tidak Lagi',
'short' => 'Pernah',
'color' => 'info',
'icon' => 'tabler-history',
],
'belum_pernah' => [
'label' => 'Belum Pernah',
'short' => 'Belum Pernah',
'color' => 'secondary',
'icon' => 'tabler-minus',
],
default => [
'label' => filled($status) ? Str::headline($status) : 'Belum diisi',
'short' => filled($status) ? Str::headline($status) : 'Belum diisi',
'color' => 'secondary',
'icon' => 'tabler-help-circle',
],
};
return [
'key' => $key,
'nama' => $namaProgram[$key] ?? Str::headline($key),
'status' => $status,
'label' => $statusMeta['label'],
'short' => $statusMeta['short'],
'color' => $statusMeta['color'],
'icon' => $statusMeta['icon'],
];
})
->values();
$d4SosialEkonomi = [
'jumlah_tanggungan' => $sosialEkonomi->jumlah_tanggungan,
'kecukupan_penghasilan' => [
'skor' => $kecukupanPenghasilan?->value,
'label' => $kecukupanPenghasilan?->label(),
'color' => $this->getScoreColor(
$kecukupanPenghasilan?->value
),
],
'kemampuan_menabung' => [
'skor' => $kemampuanMenabung?->value,
'label' => $kemampuanMenabung?->label(),
'color' => $this->getScoreColor(
$kemampuanMenabung?->value
),
],
'status_kepemilikan_lahan' => $sosialEkonomi->status_kepemilikan_lahan,
'kelembagaan' => [
'terdaftar' =>
$terdaftarKelompok === TerdaftarKelompokTani::YA,
'status' =>
$terdaftarKelompok?->label(),
'peran' =>
$peranKelompok?->label(),
],
'kondisi_keuangan' => [
'rekening' => [
'label' => $rekening?->label(),
'value' => $rekening?->value,
'color' => $this->getFinancialColor(
$rekening?->value
),
'icon' => 'tabler-building-bank',
],
'kredit' => [
'label' => $kredit?->label(),
'value' => $kredit?->value,
'color' => $this->getFinancialColor(
$kredit?->value
),
'icon' => 'tabler-credit-card',
],
'hutang_panen' => [
'label' => $hutangPanen?->label(),
'value' => $hutangPanen?->value,
'color' => $this->getFinancialColor(
$hutangPanen?->value
),
'icon' => 'tabler-coins',
],
'ijon' => [
'label' => $ijon?->label(),
'value' => $ijon?->value,
'color' => $this->getFinancialColor(
$ijon?->value
),
'icon' => 'tabler-link',
],
],
'program_bantuan' => $programBantuan,
];
}
$surveyPelatihan = SurveyPelatihan::where('petani_id', $id)
->orderByDesc('tahun_pelaksanaan')
->get();
$surveySertifikasi = SurveySertifikasi::where('petani_id', $id)
->orderByDesc('tahun_pelaksanaan')
->get();
$aksesPasar = AksesPasar::where('petani_id', $id)->first();
$saluranTerdahulu = collect($aksesPasar?->saluran_terdahulu ?? [] );
$d6SaluranPasar = [
[
'key' => 'tengkulak',
'label' => 'Tengkulak',
'icon' => 'tabler-user-dollar',
'aktif' => $saluranTerdahulu->contains(
fn ($item) => Str::contains(
Str::lower($item),
['tengkulak', 'pedagang lokal']
)
),
],
[
'key' => 'koperasi',
'label' => 'Koperasi',
'icon' => 'tabler-users-group',
'aktif' => $saluranTerdahulu->contains(
fn ($item) => Str::contains(
Str::lower($item),
'koperasi'
)
),
],
[
'key' => 'bumdes',
'label' => 'BUMDes',
'icon' => 'tabler-building-community',
'aktif' => $saluranTerdahulu->contains(
fn ($item) => Str::contains(
Str::lower($item),
'bumdes'
)
),
],
[
'key' => 'eksportir',
'label' => 'Eksportir',
'icon' => 'tabler-plane-departure',
'aktif' => $saluranTerdahulu->contains(
fn ($item) => Str::contains(
Str::lower($item),
'eksportir'
)
),
],
];
$hargaJualPanen = HargaJualPanen::where('petani_id', $id)->get();
$d6HargaJual = $hargaJualPanen->map(function ($item) {
$hargaSekarang = $item->harga_panen_terakhir !== null
? (float) $item->harga_panen_terakhir
: null;
$hargaTahunLalu = $item->harga_tahun_lalu !== null
? (float) $item->harga_tahun_lalu
: null;
$perubahan = null;
if (
$hargaSekarang !== null &&
$hargaTahunLalu !== null &&
$hargaTahunLalu > 0
) {
$perubahan = (
($hargaSekarang - $hargaTahunLalu)
/ $hargaTahunLalu
) * 100;
}
return [
'bentuk_produk' => $item->bentuk_produk,
'harga_sekarang' => $hargaSekarang,
'harga_tahun_lalu' => $hargaTahunLalu,
'harga_dua_tahun_lalu' =>
$item->harga_dua_tahun_lalu !== null
? (float) $item->harga_dua_tahun_lalu
: null,
'perubahan' => $perubahan,
'trend' => match (true) {
$perubahan === null => 'neutral',
$perubahan > 0 => 'up',
$perubahan < 0 => 'down',
default => 'stable',
},
];
});
$kebebasanPembeli = $sosialEkonomi?->kebebasan_memilih_pembeli;
$d6RantaiNilai = [
'saluran_pasar' => $d6SaluranPasar,
'harga_jual' => $d6HargaJual,
'kebebasan_memilih_pembeli' => [
'skor' => $kebebasanPembeli?->value,
'label' => $kebebasanPembeli?->label(),
'persentase' => $kebebasanPembeli
? ($kebebasanPembeli->value / 5) * 100
: 0,
'color' => $this->getScoreColor(
$kebebasanPembeli?->value
),
],
];
// Report missing profile data without treating an unanswered field as zero.
$missingSections = [];
$identityFields = [
'nama' => 'Nama', 'ktp' => 'NIK', 'tanggal_lahir' => 'Tanggal lahir',
'jenis_kelamin' => 'Jenis kelamin', 'pendidikan_terakhir' => 'Pendidikan terakhir',
'status_perkawinan' => 'Status perkawinan',
];
foreach ($identityFields as $field => $label) {
if (blank($petani->{$field})) {
$missingSections[] = $label;
}
}
if (!$desaKelurahan?->nama || !$desaKelurahan?->kecamatan?->nama) {
$missingSections[] = 'Wilayah desa/kecamatan';
}
if (!$komoditasUtama?->nama) {
$missingSections[] = 'Komoditas utama';
}
if ($lahan->isEmpty()) {
$missingSections[] = 'Data lahan';
}
if ($tahunTanam === null) {
$missingSections[] = 'Umur tanaman';
}
if (!$praktikBudidaya || $indikatorOnFarm->contains(fn ($item) => $item['skor'] === null)) {
$missingSections[] = 'Praktik budidaya';
}
if ($surveyPanen?->hasil_komoditas_utama_permusim === null) {
$missingSections[] = 'Survei panen';
}
$socialFields = [
'jumlah_tanggungan', 'skor_kecukupan_penghasilan', 'skor_kemampuan_menabung',
'status_kepemilikan_lahan', 'terdaftar_kelompok_tani', 'status_rekening_tabungan',
'pengalaman_kredit_kebun', 'keterikatan_hutang_panen', 'praktik_ijon',
'kebebasan_memilih_pembeli',
];
if (!$sosialEkonomi || collect($socialFields)->contains(fn ($field) => blank($sosialEkonomi->{$field}))) {
$missingSections[] = 'Sosial ekonomi';
}
if (!$aksesPasar || blank($aksesPasar->saluran_terdahulu)) {
$missingSections[] = 'Akses pasar';
}
if (!$hasAssessment) {
$missingSections[] = 'Penilaian kesiapan adopsi belum lengkap';
}
$data = [
'missingSections' => $missingSections,
'hasAssessment' => $hasAssessment,
'petani' => $petani,
'skorPetani' => $skorPetani,
'enumerator' => $enumerator,
'komoditasUtama' => $komoditasUtama,
'lahan' => $lahan,
'farmerLandMap' => $this->farmerLandMapData($lahan),
'desaKelurahan' => $desaKelurahan,
'ktpMasked' => $ktpMasked,
'profiling' => $profiling,
'tahunTanam' => $tahunTanam,
'umurTanaman' => $umurTanaman,
'surveyPanen' => $surveyPanen,
'praktikBudidaya' => $praktikBudidaya,
'indikatorOnFarm' => $indikatorOnFarm,
'detailOnFarm' => $detailOnFarm,
'sosialEkonomi' => $sosialEkonomi,
'd4SosialEkonomi' => $d4SosialEkonomi,
'surveyPelatihan' => $surveyPelatihan,
'surveySertifikasi' => $surveySertifikasi,
'aksesPasar' => $aksesPasar,
'd6SaluranPasar' => $d6SaluranPasar,
'hargaJualPanen' => $hargaJualPanen,
'd6HargaJual' => $d6HargaJual,
'kebebasanPembeli' => $kebebasanPembeli,
'd6RantaiNilai' => $d6RantaiNilai
];
return view('produktivitas.profil-petani.index', $data);
}
private function farmerLandMapData(\Illuminate\Support\Collection $lands): array
{
$regions = ['desa' => [], 'kecamatan' => []];
$plots = [];
foreach ($lands as $land) {
$plot = [
'id' => $land->id,
'nama' => $land->nama,
'path' => $land->path,
'luas' => $land->luas_lahan,
'komoditas' => $land->komoditas?->nama,
];
foreach (['desa' => $land->desaKelurahan, 'kecamatan' => $land->desaKelurahan?->kecamatan] as $level => $region) {
$plot[$level . '_id'] = $region?->id;
if ($region && !isset($regions[$level][$region->id])) {
$regions[$level][$region->id] = [
'id' => $region->id,
'nama' => $region->nama,
'path' => $region->path,
];
}
}
$plots[] = $plot;
}
return ['lands' => $plots, 'regions' => $regions];
}
private function getScoreColor(?int $score): string
{
if ($score === null) {
return 'secondary';
}
return match (true) {
$score >= 5 => 'success',
$score >= 4 => 'info',
$score >= 3 => 'primary',
$score >= 2 => 'warning',
default => 'danger',
};
}
private function getFinancialColor(?int $score): string
{
if ($score === null || $score === 0) {
return 'secondary';
}
return match (true) {
$score >= 5 => 'success',
$score >= 4 => 'info',
$score >= 2 => 'warning',
default => 'danger',
};
}
private function buildProfiling(SkorPetani $skorPetani): array
{
$skorAkhir = $this->normalizeScore($skorPetani->skor_kesiapan_adopsi);
$segmen = $this->getSegmen($skorAkhir);
$dimensions = [
'd2' => [
'kode' => 'D2',
'nama' => 'Sistem & Praktik Budidaya',
'skor' => $this->normalizeScore(
$skorPetani->skor_d2_sistem_praktik_budidaya
),
],
'd4' => [
'kode' => 'D4',
'nama' => 'Sosial-Ekonomi Rumah Tangga',
'skor' => $this->normalizeScore(
$skorPetani->skor_d4_sosial_ekonomi
),
],
'd6' => [
'kode' => 'D6',
'nama' => 'Rantai Nilai & Akses Pasar',
'skor' => $this->normalizeScore(
$skorPetani->skor_d6_rantai_nilai_akses_pasar
),
],
];
foreach ($dimensions as $key => $dimension) {
$dimensions[$key]['level'] = $this->getDimensionLevel($dimension['skor']);
}
$components = collect([
[
'key' => 'motivasi',
'nama' => 'Motivasi & Kapasitas Adopsi',
'short' => 'Motivasi',
'dimensi' => 'D4',
'bobot' => 25,
'icon' => 'tabler-bulb',
'skor' => $this->normalizeScore($skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi),
],
[
'key' => 'ekonomi',
'nama' => 'Kondisi Ekonomi Rumah Tangga',
'short' => 'Ekonomi',
'dimensi' => 'D4',
'bobot' => 20,
'icon' => 'tabler-home-dollar',
'skor' => $this->normalizeScore($skorPetani->skor_komp_2_d4_k2_ekonomi_rumah_tangga),
],
[
'key' => 'gap',
'nama' => 'Praktik Budidaya & GAP',
'short' => 'Budidaya / GAP',
'dimensi' => 'D2',
'bobot' => 20,
'icon' => 'tabler-plant-2',
'skor' => $this->normalizeScore($skorPetani->skor_komp_3_d2_praktik_budidaya_gap),
],
[
'key' => 'kelembagaan',
'nama' => 'Keterlibatan Kelembagaan',
'short' => 'Kelembagaan',
'dimensi' => 'D4',
'bobot' => 15,
'icon' => 'tabler-users-group',
'skor' => $this->normalizeScore($skorPetani->skor_komp_4_d4_k4_keterlibatan_kelembagaan),
],
[
'key' => 'keuangan',
'nama' => 'Akses & Kebebasan Keuangan',
'short' => 'Keuangan',
'dimensi' => 'D4',
'bobot' => 10,
'icon' => 'tabler-building-bank',
'skor' => $this->normalizeScore($skorPetani->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan),
],
[
'key' => 'pasar',
'nama' => 'Rantai Nilai & Akses Pasar',
'short' => 'Akses Pasar',
'dimensi' => 'D6',
'bobot' => 10,
'icon' => 'tabler-building-store',
'skor' => $this->normalizeScore($skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar),
],
]);
$availableComponents = $components
->filter(fn ($item) => $item['skor'] !== null)
->values();
$strengths = $availableComponents
->sortByDesc('skor')
->take(2)
->values();
$barriers = $availableComponents
->filter(fn ($item) => $item['skor'] < 3)
->sortBy('skor')
->values();
$specialPattern = $this->detectSpecialPattern(
$segmen,
$dimensions,
$skorPetani
);
$title = $this->buildProfileTitle(
$segmen,
$specialPattern,
$barriers
);
$narrative = $this->buildNarrative(
$segmen,
$specialPattern,
$strengths,
$barriers
);
$recommendations = $this->buildRecommendations(
$specialPattern,
$barriers
);
return [
'skor_akhir' => $skorAkhir,
'segmen' => $segmen,
'title' => $title,
'dimensions' => $dimensions,
'components' => $components,
'strengths' => $strengths,
'barriers' => $barriers,
'special_pattern' => $specialPattern,
'narrative' => $narrative,
'recommendations' => $recommendations,
];
}
private function normalizeScore($score): ?float
{
if ($score === null || $score === '') {
return null;
}
$score = (float) $score;
if ($score <= 0) {
return null;
}
return $score;
}
private function getSegmen(?float $score): string
{
if ($score === null) {
return 'Belum Dinilai';
}
return match (true) {
$score >= 4.0 => 'Penggerak',
$score >= 3.0 => 'Perintis',
$score >= 2.0 => 'Pemerhati',
default => 'Pemula',
};
}
private function getDimensionLevel(?float $score): string
{
if ($score === null) {
return 'belum_dinilai';
}
if ($score <= self::DIMENSION_LOW_MAX) {
return 'rendah';
}
if ($score <= self::DIMENSION_MEDIUM_MAX) {
return 'sedang';
}
return 'tinggi';
}
private function detectSpecialPattern(
string $segmen,
array $dimensions,
SkorPetani $skorPetani
): ?array {
$d2 = $dimensions['d2']['level'];
$d4 = $dimensions['d4']['level'];
$d6 = $dimensions['d6']['level'];
if (
$segmen === 'Perintis' &&
$d4 === 'tinggi' &&
$d2 === 'rendah' &&
$d6 === 'sedang'
) {
return [
'kode' => 'perintis_teknis',
'nama' => 'Perintis dengan Hambatan Teknis',
'prioritas' => 'gap',
];
}
if (
$segmen === 'Perintis' &&
$d4 === 'tinggi' &&
in_array($d2, ['sedang', 'tinggi']) && $d6 === 'rendah'
) {
return [
'kode' => 'perintis_pasar',
'nama' => 'Perintis dengan Hambatan Pasar',
'prioritas' => 'pasar',
];
}
if (
$segmen === 'Pemerhati' &&
$d4 === 'sedang' &&
$d2 === 'rendah' &&
$d6 === 'sedang'
) {
return [
'kode' => 'pemerhati_teknis',
'nama' => 'Pemerhati Teknis',
'prioritas' => 'gap',
];
}
if (
$segmen === 'Pemula' &&
$d4 === 'rendah' &&
$d2 === 'rendah' &&
$d6 === 'rendah'
) {
return [
'kode' => 'pemula_struktural',
'nama' => 'Pemula Struktural',
'prioritas' => 'struktural',
];
}
return null;
}
private function buildProfileTitle(
string $segmen,
?array $specialPattern,
$barriers
): string {
if ($specialPattern) {
return $specialPattern['nama'];
}
if ($barriers->isNotEmpty()) {
$mainBarrier = $barriers->first();
return $segmen . ' dengan Prioritas ' . $mainBarrier['short'];
}
return $segmen . ' dengan Profil Relatif Seimbang';
}
private function buildNarrative(
string $segmen,
?array $specialPattern,
$strengths,
$barriers
): string {
if ($specialPattern) {
return match ($specialPattern['kode']) {
'perintis_teknis' =>
'Petani memiliki fondasi sosial-ekonomi yang relatif siap '
. 'untuk berkembang, tetapi penerapannya belum sepenuhnya '
. 'tercermin dalam praktik budidaya. Hambatan utama berada '
. 'pada aspek teknis di lapangan.',
'perintis_pasar' =>
'Petani memiliki fondasi sosial-ekonomi dan kesiapan '
. 'budidaya yang relatif baik, tetapi manfaat ekonominya '
. 'masih dibatasi oleh rantai nilai dan akses pasar.',
'pemerhati_teknis' =>
'Petani telah memiliki sebagian fondasi untuk berkembang, '
. 'namun praktik budidaya masih menjadi hambatan utama. '
. 'Pendekatan berbasis demonstrasi hasil nyata akan lebih '
. 'relevan sebelum intervensi teknis yang lebih lanjut.',
'pemula_struktural' =>
'Petani menghadapi hambatan pada beberapa dimensi sekaligus. '
. 'Kondisi sosial-ekonomi, praktik budidaya, serta akses '
. 'pasar perlu dibaca bersama untuk menentukan hambatan '
. 'mendasar yang paling mendesak.',
default =>
$this->buildGenericNarrative($segmen, $strengths, $barriers),
};
}
return $this->buildGenericNarrative($segmen, $strengths, $barriers);
}
private function buildGenericNarrative(
string $segmen,
$strengths,
$barriers
): string {
$text = 'Petani berada pada segmen ' . $segmen . '. ';
if ($strengths->isNotEmpty()) {
$strengthNames = $strengths->pluck('nama')->implode(' dan ');
$text .= 'Kekuatan relatif terlihat pada ' . $strengthNames . '. ';
}
if ($barriers->isNotEmpty()) {
$barrierNames = $barriers->take(3)->pluck('nama')->implode(', ');
$text .=
'Area yang masih memerlukan perhatian terutama pada ' . $barrierNames . '. Prioritas intervensi sebaiknya mengikuti ' . 'hambatan dengan skor paling rendah.';
} else {
$text .= 'Tidak terdapat komponen dengan skor rendah yang ' . 'menonjol. Pendampingan dapat difokuskan pada ' . 'pemeliharaan dan penguatan capaian yang sudah ada.';
}
return $text;
}
private function buildRecommendations(
?array $specialPattern,
$barriers
): array {
if ($specialPattern) {
$specialRecommendation = match ($specialPattern['kode']) {
'perintis_teknis' => [
'Prioritaskan pendampingan GAP langsung di lapangan.',
'Perkuat koneksi petani terhadap sumber input dan pelatihan teknis.',
],
'perintis_pasar' => [
'Perkuat koneksi ke saluran pasar yang lebih baik.',
'Fasilitasi akses pembeli, negosiasi, atau penguatan koperasi.',
],
'pemerhati_teknis' => [
'Gunakan demonstrasi lapangan untuk menunjukkan hasil nyata.',
'Lakukan pendampingan teknis secara bertahap sebelum pelatihan formal.',
],
'pemula_struktural' => [
'Identifikasi hambatan dasar seperti lahan, hutang, atau logistik.',
'Tangani hambatan struktural sebelum mendorong adopsi praktik baru.',
],
default => [],
};
if (!empty($specialRecommendation)) {
return $specialRecommendation;
}
}
$recommendationMap = [
'motivasi' => 'Gunakan demonstrasi hasil nyata, role model, dan pendekatan perubahan secara bertahap.',
'ekonomi' => 'Perkuat ruang gerak ekonomi rumah tangga sebelum mendorong investasi atau perubahan praktik yang berisiko.',
'gap' => 'Prioritaskan pendampingan praktik budidaya dan GAP langsung di kebun.',
'kelembagaan' => 'Perkuat keterlibatan petani dalam kelompok tani sebagai ruang belajar dan penguatan posisi tawar.',
'keuangan' => 'Tinjau akses keuangan formal serta kemungkinan ikatan hutang panen atau ijon yang membatasi ruang gerak petani.',
'pasar' => 'Perkuat koneksi dengan pembeli dan saluran pasar alternatif serta kemampuan negosiasi petani.',
];
return $barriers->take(3)->map(
fn ($item) =>
$recommendationMap[$item['key']] ?? null
)->filter()->values()->all();
}
}
@@ -64,12 +64,26 @@ class ProfileController extends Controller
$validator = Validator::make($request->all(), [
'nama' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255'],
'telepon' => ['required', 'max:13'],
'desa_kelurahan_id' => ['required'],
'telepon' => ['nullable', 'max:13'],
'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'],
'ktp' => ['nullable', 'min:16', 'max:16'],
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
'kk' => ['nullable', 'min:16', 'max:16'],
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
], [
'nama.required' => 'Nama wajib diisi.',
'email.required' => 'Email wajib diisi.',
'email.email' => 'Format email tidak sesuai.',
'telepon.max' => 'Nomor telepon tidak sesuai.',
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
'ktp.min' => 'KTP harus terdiri dari 16 digit.',
'ktp.max' => 'KTP harus terdiri dari 16 digit.',
'kk.min' => 'KK harus terdiri dari 16 digit.',
'kk.max' => 'KK harus terdiri dari 16 digit.',
'file_ktp.mimes' => 'Format file harus dalam bentuk JPEG, JPG, PNG, atau PDF.',
'file_kk.mimes' => 'Format file harus dalam bentuk JPEG, JPG, PNG, atau PDF.',
'file_ktp.max' => 'Ukuran file maksimal 2MB.',
'file_kk.max' => 'Ukuran file maksimal 2MB.',
]);
if ($validator->fails()) {
@@ -143,12 +157,31 @@ class ProfileController extends Controller
$validator = Validator::make($request->all(), [
'nama' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255'],
'telepon' => ['required', 'max:13'],
'desa_kelurahan_id' => ['required'],
'telepon' => ['nullable', 'max:13'],
'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'],
'ktp' => ['nullable', 'min:16', 'max:16'],
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
'kk' => ['nullable', 'min:16', 'max:16'],
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
], [
'nama.required' => 'Nama harus diisi.',
'nama.string' => 'Nama harus berupa string.',
'nama.max' => 'Nama tidak boleh lebih dari 255 karakter.',
'email.required' => 'Email harus diisi.',
'email.email' => 'Email harus berupa alamat email yang valid.',
'email.max' => 'Email tidak boleh lebih dari 255 karakter.',
'telepon.max' => 'Nomor telepon tidak boleh lebih dari 13 karakter.',
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
'ktp.min' => 'Nomor KTP harus terdiri dari 16 karakter.',
'ktp.max' => 'Nomor KTP harus terdiri dari 16 karakter.',
'kk.min' => 'Nomor KK harus terdiri dari 16 karakter.',
'kk.max' => 'Nomor KK harus terdiri dari 16 karakter.',
'file_ktp.file' => 'File harus berupa file.',
'file_ktp.mimes' => 'File harus berupa file dengan ekstensi jpeg, jpg, png, atau pdf.',
'file_ktp.max' => 'Ukuran file tidak boleh lebih dari 2MB.',
'file_kk.file' => 'File harus berupa file.',
'file_kk.mimes' => 'File harus berupa file dengan ekstensi jpeg, jpg, png, atau pdf.',
'file_kk.max' => 'Ukuran file tidak boleh lebih dari 2MB.',
]);
if ($validator->fails()) {
@@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Models\Pertanian\SkorPetani;
use App\Models\Pertanian\Petani;
class SkorPetaniController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(string $petaniId)
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
$petani = Petani::findOrFail($petaniId);
$skor = $petani->skor;
return view('produktivitas.petani.skor.index', compact('petani', 'skor'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $petaniId, string $id)
{
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
}
@@ -3,15 +3,44 @@
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use App\Enums\KategoriPendapatanKomoditas;
use App\Enums\PeriodePendapatan;
use App\Enums\PihakMembantuKonflik;
use App\Enums\SkorKecukupanPenghasilan;
use App\Enums\SkorKejelasanStatusLahan;
use App\Enums\SkorKemampuanMenabung;
use App\Enums\SkorPeriodePaceklik;
use App\Enums\StatusKepemilikanLahanSosialEkonomi;
use App\Enums\StatusKonflik;
use App\Enums\SosialEkonomi\AlasanKreditDitolak;
use App\Enums\SosialEkonomi\BisaBeliPupukBersubsidi;
use App\Enums\SosialEkonomi\JenisAsetProduktif;
use App\Enums\SosialEkonomi\KategoriPendapatanKomoditas;
use App\Enums\SosialEkonomi\KemauanIkutProgram;
use App\Enums\SosialEkonomi\KemauanMencobaPraktikBaru;
use App\Enums\SosialEkonomi\KeaktifanPertemuanKelompok;
use App\Enums\SosialEkonomi\KedekatanSesamaAnggota;
use App\Enums\SosialEkonomi\KebebasanMemilihPembeli;
use App\Enums\SosialEkonomi\KeterikatanHutangPanen;
use App\Enums\SosialEkonomi\KeterlibatanProgramKeuanganFormal;
use App\Enums\SosialEkonomi\KondisiAsetProduktif;
use App\Enums\SosialEkonomi\ManfaatKelompokTani;
use App\Enums\SosialEkonomi\MinatPembeliHargaStabil;
use App\Enums\SosialEkonomi\OrientasiRencanaJangkaPanjang;
use App\Enums\SosialEkonomi\PanutanPetaniSukses;
use App\Enums\SosialEkonomi\PemahamanMutuHarga;
use App\Enums\SosialEkonomi\PengetahuanHargaHasilOlah;
use App\Enums\SosialEkonomi\PengalamanKreditKebun;
use App\Enums\SosialEkonomi\PeriodePendapatan;
use App\Enums\SosialEkonomi\PeranDalamKelompok;
use App\Enums\SosialEkonomi\PernahBantuanNgoPerusahaan;
use App\Enums\SosialEkonomi\PernahJualHargaLebihTinggi;
use App\Enums\SosialEkonomi\PinjamanTokohAdatPanen;
use App\Enums\SosialEkonomi\PihakMembantuKonflik;
use App\Enums\SosialEkonomi\PraktikIjon;
use App\Enums\SosialEkonomi\SkorKecukupanPenghasilan;
use App\Enums\SosialEkonomi\SkorKejelasanStatusLahan;
use App\Enums\SosialEkonomi\SkorKemampuanMenabung;
use App\Enums\SosialEkonomi\SkorPeriodePaceklik;
use App\Enums\SosialEkonomi\StatusKartuTani;
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
use App\Enums\SosialEkonomi\StatusKonflik;
use App\Enums\SosialEkonomi\StatusProgramKeuanganFormal;
use App\Enums\SosialEkonomi\StatusRekeningTabungan;
use App\Enums\SosialEkonomi\TekananJualSegera;
use App\Enums\SosialEkonomi\TerdaftarKelompokTani;
use App\Enums\SosialEkonomi\UsahaMutuTambahanHarga;
use App\Models\Pertanian\Komoditas;
use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Petani;
@@ -35,20 +64,14 @@ class SosialEkonomiController extends Controller
->where('petani_id', $petaniId)
->first();
$pendapatanKomoditas = $sosialEkonomi?->pendapatanKomoditas ?? collect();
$pendapatanKomoditasUtama = $pendapatanKomoditas->where('kategori', 'utama')->values();
$pendapatanKomoditasLainnya = $pendapatanKomoditas->where('kategori', 'lainnya')->values();
$pendapatanLuarPertanian = $sosialEkonomi?->pendapatanLuarPertanian?->values() ?? collect();
$komoditasPetani = $this->komoditasPetani($petani);
return view('produktivitas.petani.sosial-ekonomi.index', array_merge(compact(
'petani',
'sosialEkonomi',
'pendapatanKomoditasUtama',
'pendapatanKomoditasLainnya',
'pendapatanLuarPertanian',
'komoditasPetani'
), $this->sosialEkonomiOptions()));
return $this->formView(
request(),
$petani,
$sosialEkonomi,
$sosialEkonomi
? 'produktivitas.petani.sosial-ekonomi.edit'
: 'produktivitas.petani.sosial-ekonomi.index'
);
}
public function index(Request $request, Petani $petani)
@@ -131,8 +154,7 @@ class SosialEkonomiController extends Controller
});
if (!$request->expectsJson()) {
return redirect()
->route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id])
return $this->redirectToEditSection($sosialEkonomi, $request)
->with('success', 'Data sosial ekonomi berhasil disimpan.');
}
@@ -163,7 +185,7 @@ class SosialEkonomiController extends Controller
$validated = $this->validateData($request, $sosialEkonomi->id);
$sosialEkonomi = DB::transaction(function () use ($request, $validated, $sosialEkonomi) {
$sosialEkonomi->update($this->sosialEkonomiPayload($validated) + [
$sosialEkonomi->update($this->sosialEkonomiPayload($validated, $sosialEkonomi) + [
'updated_by' => auth()->id(),
]);
@@ -183,8 +205,7 @@ class SosialEkonomiController extends Controller
});
if (!$request->expectsJson()) {
return redirect()
->route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id])
return $this->redirectToEditSection($sosialEkonomi, $request)
->with('success', 'Data sosial ekonomi berhasil diperbarui.');
}
@@ -195,6 +216,18 @@ class SosialEkonomiController extends Controller
]);
}
private function redirectToEditSection(SosialEkonomi $sosialEkonomi, Request $request): RedirectResponse
{
$url = route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id]);
$fragment = $request->input('redirect_fragment');
if (is_string($fragment) && preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
$url .= '#' . $fragment;
}
return redirect()->to($url);
}
public function destroy(string $id): JsonResponse
{
$sosialEkonomi = SosialEkonomi::findOrFail($id);
@@ -212,14 +245,19 @@ class SosialEkonomiController extends Controller
private function validateData(Request $request, ?int $id = null): array
{
return $request->validate([
$this->normalizeCurrencyInput($request);
$section = $request->input('form_section');
$rules = [
'form_section' => ['required', 'string', Rule::in(['utama', 'sosial_budaya', 'keanggotaan_kelompok', 'akses_keuangan', 'keterikatan_hutang'])],
'uid' => [
'nullable',
'string',
'max:255',
Rule::unique('sosial_ekonomi', 'uid')->ignore($id),
],
'petani_id' => ['nullable', 'exists:petani,id'],
'petani_id' => ['required', 'exists:petani,id'],
'jumlah_tanggungan' => ['nullable', 'integer', 'min:0'],
'anak_masih_sekolah' => ['nullable', 'integer', 'min:0'],
'yang_ikut_bekerja' => ['nullable', 'integer', 'min:0'],
@@ -240,6 +278,67 @@ class SosialEkonomiController extends Controller
'pihak_membantu_konflik_nama' => ['nullable', 'string', 'max:255'],
'tahun_bantuan_konflik' => ['nullable', 'integer', 'digits:4'],
'status_konflik' => ['nullable', 'string', Rule::in(StatusKonflik::values())],
'kemauan_mencoba_praktik_baru' => ['nullable', 'integer', Rule::in(KemauanMencobaPraktikBaru::values())],
'alasan_mencoba_praktik_baru' => ['nullable', 'string'],
'hambatan_mengubah_cara_bertani' => ['nullable', 'string'],
'pemahaman_mutu_harga' => ['nullable', 'integer', Rule::in(PemahamanMutuHarga::values())],
'contoh_pemahaman_mutu_harga' => ['nullable', 'string'],
'pengetahuan_harga_hasil_olah' => ['nullable', 'string', Rule::in(PengetahuanHargaHasilOlah::values())],
'pernah_jual_harga_lebih_tinggi' => ['nullable', 'string', Rule::in(PernahJualHargaLebihTinggi::values())],
'penyebab_harga_lebih_tinggi' => ['nullable', 'string'],
'usaha_mutu_tambahan_harga' => ['nullable', 'string', Rule::in(UsahaMutuTambahanHarga::values())],
'alasan_usaha_mutu_tambahan_harga' => ['nullable', 'string'],
'panutan_petani_sukses' => ['nullable', 'integer', Rule::in(PanutanPetaniSukses::values())],
'nama_lokasi_panutan' => ['nullable', 'string'],
'kemauan_ikut_program' => ['nullable', 'integer', Rule::in(KemauanIkutProgram::values())],
'harapan_program' => ['nullable', 'string'],
'orientasi_rencana_jangka_panjang' => ['nullable', 'integer', Rule::in(OrientasiRencanaJangkaPanjang::values())],
'rencana_jangka_panjang' => ['nullable', 'string'],
'terdaftar_kelompok_tani' => ['nullable', 'string', Rule::in(TerdaftarKelompokTani::values())],
'nama_kelompok_tani' => ['nullable', 'string', 'max:255'],
'lama_bergabung_kelompok_tahun' => ['nullable', 'integer', 'min:0'],
'kedekatan_sesama_anggota' => ['nullable', 'string', Rule::in(KedekatanSesamaAnggota::values())],
'keaktifan_pertemuan_kelompok' => ['nullable', 'integer', Rule::in(KeaktifanPertemuanKelompok::values())],
'topik_biasa_dibahas' => ['nullable', 'string'],
'peran_dalam_kelompok' => ['nullable', 'integer', Rule::in(PeranDalamKelompok::values())],
'manfaat_kelompok_tani' => ['nullable', 'integer', Rule::in(ManfaatKelompokTani::values())],
'status_kartu_tani' => ['nullable', 'string', Rule::in(StatusKartuTani::values())],
'bisa_beli_pupuk_bersubsidi' => ['nullable', 'string', Rule::in(BisaBeliPupukBersubsidi::values())],
'manfaat_lain_kartu_tani' => ['nullable', 'string'],
'status_rekening_tabungan' => ['nullable', 'integer', Rule::in(StatusRekeningTabungan::values())],
'nama_bank' => ['nullable', 'string', 'max:255'],
'pengalaman_kredit_kebun' => ['nullable', 'integer', Rule::in(PengalamanKreditKebun::values())],
'alasan_kredit_ditolak' => ['nullable', 'array'],
'alasan_kredit_ditolak.*' => ['string', Rule::in(AlasanKreditDitolak::values())],
'alasan_kredit_ditolak_lainnya' => ['nullable', 'string', 'max:255'],
'kondisi_aset_produktif' => ['nullable', 'integer', Rule::in(KondisiAsetProduktif::values())],
'jenis_aset_produktif' => ['nullable', 'array'],
'jenis_aset_produktif.*' => ['string', Rule::in(JenisAsetProduktif::values())],
'jenis_aset_produktif_lainnya' => ['nullable', 'string', 'max:255'],
'keterangan_kondisi_aset' => ['nullable', 'string'],
'keterlibatan_program_keuangan_formal' => ['nullable', 'integer', Rule::in(KeterlibatanProgramKeuanganFormal::values())],
'status_program_keuangan_formal' => ['nullable', 'array'],
'status_program_keuangan_formal.*' => ['nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'program_keuangan_formal_lainnya_nama' => ['nullable', 'string', 'max:255'],
'pernah_bantuan_ngo_perusahaan' => ['nullable', 'string', Rule::in(PernahBantuanNgoPerusahaan::values())],
'detail_bantuan_ngo_perusahaan' => ['nullable', 'array'],
'detail_bantuan_ngo_perusahaan.*.nama_lembaga' => ['nullable', 'string', 'max:255'],
'detail_bantuan_ngo_perusahaan.*.bentuk_bantuan' => ['nullable', 'string', 'max:255'],
'detail_bantuan_ngo_perusahaan.*.frekuensi' => ['nullable', 'integer', 'min:0'],
'detail_bantuan_ngo_perusahaan.*.tahun_terakhir' => ['nullable', 'integer', 'digits:4'],
'keterikatan_hutang_panen' => ['nullable', 'integer', Rule::in(KeterikatanHutangPanen::values())],
'pinjaman_tokoh_adat_panen' => ['nullable', 'string', Rule::in(PinjamanTokohAdatPanen::values())],
'keterangan_pinjaman_tokoh_adat' => ['nullable', 'string'],
'total_hutang_rupiah' => ['nullable', 'integer', 'min:0'],
'total_hutang_setara_kg_panen' => ['nullable', 'numeric', 'min:0'],
'total_hutang_tidak_bersedia' => ['nullable', 'boolean'],
'praktik_ijon' => ['nullable', 'integer', Rule::in(PraktikIjon::values())],
'tekanan_jual_segera' => ['nullable', 'string', Rule::in(TekananJualSegera::values())],
'penyebab_tekanan_jual_segera' => ['nullable', 'string'],
'kebebasan_memilih_pembeli' => ['nullable', 'integer', Rule::in(KebebasanMemilihPembeli::values())],
'alasan_tidak_bebas_memilih_pembeli' => ['nullable', 'string'],
'minat_pembeli_harga_stabil' => ['nullable', 'string', Rule::in(MinatPembeliHargaStabil::values())],
'alasan_minat_pembeli_harga_stabil' => ['nullable', 'string'],
'pendapatan_komoditas' => ['nullable', 'array'],
'pendapatan_komoditas.*.kategori' => ['required_with:pendapatan_komoditas', 'string', Rule::in(KategoriPendapatanKomoditas::values())],
@@ -250,28 +349,510 @@ class SosialEkonomiController extends Controller
'pendapatan_luar_pertanian' => ['nullable', 'array'],
'pendapatan_luar_pertanian.*.jenis_pekerjaan' => ['nullable', 'string', 'max:255'],
'pendapatan_luar_pertanian.*.estimasi_per_bulan' => ['nullable', 'integer', 'min:0'],
]);
];
if ($section === 'utama') {
$rules = array_merge($rules, [
'jumlah_tanggungan' => ['required', 'integer', 'min:0'],
'anak_masih_sekolah' => ['required', 'integer', 'min:0'],
'yang_ikut_bekerja' => ['required', 'integer', 'min:0'],
'skor_periode_paceklik' => ['required', 'integer', Rule::in(SkorPeriodePaceklik::values())],
'bulan_paceklik' => ['required', 'string', 'max:255'],
'sumber_penghasilan_paceklik' => ['required', 'string'],
'total_estimasi_biaya_kebun_per_musim' => ['required', 'integer', 'min:0'],
'skor_kemampuan_menabung' => ['required', 'integer', Rule::in(SkorKemampuanMenabung::values())],
'estimasi_tabungan_per_bulan' => ['required', 'integer', 'min:0'],
'skor_kecukupan_penghasilan' => ['required', 'integer', Rule::in(SkorKecukupanPenghasilan::values())],
'alasan_kecukupan_penghasilan' => ['required', 'string'],
'status_kepemilikan_lahan' => ['required', 'string', Rule::in(StatusKepemilikanLahanSosialEkonomi::values())],
'skor_kejelasan_status_lahan' => ['required', 'integer', Rule::in(SkorKejelasanStatusLahan::values())],
'ada_sertifikat_dokumen' => ['required', 'boolean'],
'ada_sengketa' => ['required', 'boolean'],
'keterangan_sengketa' => ['required_if:ada_sengketa,1', 'nullable', 'string'],
'pihak_membantu_konflik' => ['required_if:ada_sengketa,1', 'nullable', 'string', Rule::in(PihakMembantuKonflik::values())],
'pihak_membantu_konflik_nama' => ['required_if:pihak_membantu_konflik,' . PihakMembantuKonflik::ADA->value, 'nullable', 'string', 'max:255'],
'tahun_bantuan_konflik' => ['required_if:pihak_membantu_konflik,' . PihakMembantuKonflik::ADA->value, 'nullable', 'integer', 'digits:4'],
'status_konflik' => ['required_if:ada_sengketa,1', 'nullable', 'string', Rule::in(StatusKonflik::values())],
]);
}
if ($section === 'sosial_budaya') {
$rules = array_merge($rules, [
'kemauan_mencoba_praktik_baru' => ['required', 'integer', Rule::in(KemauanMencobaPraktikBaru::values())],
'alasan_mencoba_praktik_baru' => ['required', 'string'],
'hambatan_mengubah_cara_bertani' => ['required', 'string'],
'pemahaman_mutu_harga' => ['required', 'integer', Rule::in(PemahamanMutuHarga::values())],
'contoh_pemahaman_mutu_harga' => ['required', 'string'],
'pengetahuan_harga_hasil_olah' => ['required', 'string', Rule::in(PengetahuanHargaHasilOlah::values())],
'pernah_jual_harga_lebih_tinggi' => ['required', 'string', Rule::in(PernahJualHargaLebihTinggi::values())],
'penyebab_harga_lebih_tinggi' => ['required_if:pernah_jual_harga_lebih_tinggi,' . PernahJualHargaLebihTinggi::PERNAH->value, 'nullable', 'string'],
'usaha_mutu_tambahan_harga' => ['required', 'string', Rule::in(UsahaMutuTambahanHarga::values())],
'alasan_usaha_mutu_tambahan_harga' => ['required_if:usaha_mutu_tambahan_harga,' . UsahaMutuTambahanHarga::TIDAK_SEBANDING_TERLALU_BANYAK_USAHA->value, 'nullable', 'string'],
'panutan_petani_sukses' => ['required', 'integer', Rule::in(PanutanPetaniSukses::values())],
'nama_lokasi_panutan' => ['required_unless:panutan_petani_sukses,' . PanutanPetaniSukses::TIDAK_ADA->value, 'nullable', 'string'],
'kemauan_ikut_program' => ['required', 'integer', Rule::in(KemauanIkutProgram::values())],
'harapan_program' => ['required', 'string'],
'orientasi_rencana_jangka_panjang' => ['required', 'integer', Rule::in(OrientasiRencanaJangkaPanjang::values())],
'rencana_jangka_panjang' => ['required', 'string'],
]);
}
if ($section === 'keanggotaan_kelompok') {
$terdaftarKelompokTani = $request->input('terdaftar_kelompok_tani') === TerdaftarKelompokTani::YA->value;
$rules = array_merge($rules, [
'terdaftar_kelompok_tani' => ['nullable', 'string', Rule::in(TerdaftarKelompokTani::values())],
'nama_kelompok_tani' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'string', 'max:255'],
'lama_bergabung_kelompok_tahun' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'integer', 'min:0'],
'kedekatan_sesama_anggota' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'string', Rule::in(KedekatanSesamaAnggota::values())],
'keaktifan_pertemuan_kelompok' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(KeaktifanPertemuanKelompok::values())],
'topik_biasa_dibahas' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'string'],
'peran_dalam_kelompok' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(PeranDalamKelompok::values())],
'manfaat_kelompok_tani' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(ManfaatKelompokTani::values())],
'status_kartu_tani' => ['nullable', 'string', Rule::in(StatusKartuTani::values())],
'bisa_beli_pupuk_bersubsidi' => ['nullable', 'string', Rule::in(BisaBeliPupukBersubsidi::values())],
'manfaat_lain_kartu_tani' => ['nullable', 'string'],
]);
}
if ($section === 'akses_keuangan') {
$pengalamanKredit = (int) $request->input('pengalaman_kredit_kebun');
$kondisiAset = (int) $request->input('kondisi_aset_produktif');
$tidakPernahMenerimaProgram = (int) $request->input('keterlibatan_program_keuangan_formal') === KeterlibatanProgramKeuanganFormal::TIDAK_PERNAH_MENERIMA->value;
$rules = array_merge($rules, [
'status_rekening_tabungan' => ['required', 'integer', Rule::in(StatusRekeningTabungan::values())],
'nama_bank' => [
Rule::requiredIf(fn () => (int) $request->input('status_rekening_tabungan') > StatusRekeningTabungan::TIDAK_PUNYA_REKENING->value),
'nullable',
'string',
'max:255',
],
'pengalaman_kredit_kebun' => ['required', 'integer', Rule::in(PengalamanKreditKebun::values())],
'alasan_kredit_ditolak' => [
Rule::requiredIf(fn () => in_array($pengalamanKredit, [
PengalamanKreditKebun::TIDAK_PERNAH_MENCOBA->value,
PengalamanKreditKebun::PERNAH_COBA_DITOLAK->value,
], true)),
'nullable',
'array',
],
'alasan_kredit_ditolak.*' => ['string', Rule::in(AlasanKreditDitolak::values())],
'alasan_kredit_ditolak_lainnya' => [
Rule::requiredIf(fn () => in_array(AlasanKreditDitolak::LAINNYA->value, $request->input('alasan_kredit_ditolak', []), true)),
'nullable',
'string',
'max:255',
],
'kondisi_aset_produktif' => ['required', 'integer', Rule::in(KondisiAsetProduktif::values())],
'jenis_aset_produktif' => [
Rule::requiredIf(fn () => $kondisiAset > KondisiAsetProduktif::TIDAK_ADA->value),
'nullable',
'array',
],
'jenis_aset_produktif.*' => ['string', Rule::in(JenisAsetProduktif::values())],
'jenis_aset_produktif_lainnya' => [
Rule::requiredIf(fn () => in_array(JenisAsetProduktif::LAINNYA->value, $request->input('jenis_aset_produktif', []), true)),
'nullable',
'string',
'max:255',
],
'keterangan_kondisi_aset' => [
Rule::requiredIf(fn () => $kondisiAset > KondisiAsetProduktif::TIDAK_ADA->value),
'nullable',
'string',
],
'keterlibatan_program_keuangan_formal' => ['required', 'integer', Rule::in(KeterlibatanProgramKeuanganFormal::values())],
'status_program_keuangan_formal' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'array'],
'status_program_keuangan_formal.pkh' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.blt' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.kur' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.kredit_koperasi_resmi' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.credit_union_cu' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.pinjaman_bumdes' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.modal_hibah_dinas_pertanian' => [Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram), 'nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'status_program_keuangan_formal.lainnya' => ['nullable', 'string', Rule::in(StatusProgramKeuanganFormal::values())],
'program_keuangan_formal_lainnya_nama' => [
Rule::requiredIf(fn () => !$tidakPernahMenerimaProgram && filled($request->input('status_program_keuangan_formal.lainnya'))),
'nullable',
'string',
'max:255',
],
'pernah_bantuan_ngo_perusahaan' => ['required', 'string', Rule::in(PernahBantuanNgoPerusahaan::values())],
'detail_bantuan_ngo_perusahaan' => [
Rule::requiredIf(fn () => $request->input('pernah_bantuan_ngo_perusahaan') === PernahBantuanNgoPerusahaan::PERNAH->value),
'nullable',
'array',
],
'detail_bantuan_ngo_perusahaan.0.nama_lembaga' => [
Rule::requiredIf(fn () => $request->input('pernah_bantuan_ngo_perusahaan') === PernahBantuanNgoPerusahaan::PERNAH->value),
'nullable',
'string',
'max:255',
],
'detail_bantuan_ngo_perusahaan.0.bentuk_bantuan' => [
Rule::requiredIf(fn () => $request->input('pernah_bantuan_ngo_perusahaan') === PernahBantuanNgoPerusahaan::PERNAH->value),
'nullable',
'string',
'max:255',
],
'detail_bantuan_ngo_perusahaan.0.frekuensi' => [
Rule::requiredIf(fn () => $request->input('pernah_bantuan_ngo_perusahaan') === PernahBantuanNgoPerusahaan::PERNAH->value),
'nullable',
'integer',
'min:0',
],
'detail_bantuan_ngo_perusahaan.0.tahun_terakhir' => [
Rule::requiredIf(fn () => $request->input('pernah_bantuan_ngo_perusahaan') === PernahBantuanNgoPerusahaan::PERNAH->value),
'nullable',
'integer',
'digits:4',
],
]);
}
if ($section === 'keterikatan_hutang') {
$totalHutangTidakBersedia = $request->boolean('total_hutang_tidak_bersedia');
$kebebasanMemilihPembeli = (int) $request->input('kebebasan_memilih_pembeli');
$rules = array_merge($rules, [
'keterikatan_hutang_panen' => ['required', 'integer', Rule::in(KeterikatanHutangPanen::values())],
'pinjaman_tokoh_adat_panen' => ['required', 'string', Rule::in(PinjamanTokohAdatPanen::values())],
'keterangan_pinjaman_tokoh_adat' => ['required_if:pinjaman_tokoh_adat_panen,' . PinjamanTokohAdatPanen::ADA->value, 'nullable', 'string'],
'total_hutang_rupiah' => [
Rule::requiredIf(fn () => !$totalHutangTidakBersedia && !filled($request->input('total_hutang_setara_kg_panen'))),
'nullable',
'integer',
'min:0',
],
'total_hutang_setara_kg_panen' => ['nullable', 'numeric', 'min:0'],
'total_hutang_tidak_bersedia' => ['nullable', 'boolean'],
'praktik_ijon' => ['required', 'integer', Rule::in(PraktikIjon::values())],
'tekanan_jual_segera' => ['required', 'string', Rule::in(TekananJualSegera::values())],
'penyebab_tekanan_jual_segera' => ['required_if:tekanan_jual_segera,' . TekananJualSegera::ADA->value, 'nullable', 'string'],
'kebebasan_memilih_pembeli' => ['required', 'integer', Rule::in(KebebasanMemilihPembeli::values())],
'alasan_tidak_bebas_memilih_pembeli' => [
Rule::requiredIf(fn () => $kebebasanMemilihPembeli > 0 && $kebebasanMemilihPembeli < KebebasanMemilihPembeli::CUKUP_BEBAS_SESEKALI_PINDAH->value),
'nullable',
'string',
],
'minat_pembeli_harga_stabil' => ['required', 'string', Rule::in(MinatPembeliHargaStabil::values())],
'alasan_minat_pembeli_harga_stabil' => ['required', 'string'],
]);
}
return $request->validate($rules, $this->validationMessages(), $this->validationAttributes());
}
private function sosialEkonomiPayload(array $validated): array
private function normalizeCurrencyInput(Request $request): void
{
$data = $request->all();
$currencyFields = [
'total_estimasi_biaya_kebun_per_musim',
'estimasi_tabungan_per_bulan',
'total_hutang_rupiah',
];
foreach ($currencyFields as $field) {
if (array_key_exists($field, $data)) {
$data[$field] = $this->currencyDigits($data[$field]);
}
}
if (isset($data['pendapatan_komoditas']) && is_array($data['pendapatan_komoditas'])) {
foreach ($data['pendapatan_komoditas'] as $index => $item) {
if (is_array($item) && array_key_exists('pendapatan', $item)) {
$data['pendapatan_komoditas'][$index]['pendapatan'] = $this->currencyDigits($item['pendapatan']);
}
}
}
if (isset($data['pendapatan_luar_pertanian']) && is_array($data['pendapatan_luar_pertanian'])) {
foreach ($data['pendapatan_luar_pertanian'] as $index => $item) {
if (is_array($item) && array_key_exists('estimasi_per_bulan', $item)) {
$data['pendapatan_luar_pertanian'][$index]['estimasi_per_bulan'] = $this->currencyDigits($item['estimasi_per_bulan']);
}
}
}
$request->merge($data);
}
private function currencyDigits(mixed $value): ?string
{
if (is_null($value) || $value === '') {
return null;
}
$digits = preg_replace('/\D/', '', (string) $value);
return $digits === '' ? null : $digits;
}
private function sosialEkonomiPayload(array $validated, ?SosialEkonomi $sosialEkonomi = null): array
{
$payload = collect($validated)
->except(['pendapatan_komoditas', 'pendapatan_luar_pertanian'])
->merge([
'uid' => $validated['uid'] ?? 'SEK-' . Str::uuid(),
])
->except(['form_section', 'pendapatan_komoditas', 'pendapatan_luar_pertanian'])
->all();
if (!$sosialEkonomi && !array_key_exists('uid', $payload)) {
$payload['uid'] = 'SEK-' . Str::uuid();
}
$payload = $this->normalizeSkorPayload($payload);
if (array_key_exists('ada_sengketa', $payload) && !filter_var($payload['ada_sengketa'], FILTER_VALIDATE_BOOLEAN)) {
$payload['keterangan_sengketa'] = null;
$payload['pihak_membantu_konflik'] = null;
$payload['pihak_membantu_konflik_nama'] = null;
$payload['tahun_bantuan_konflik'] = null;
$payload['status_konflik'] = null;
}
if (($payload['pihak_membantu_konflik'] ?? null) === PihakMembantuKonflik::TIDAK_ADA_SELESAIKAN_SENDIRI->value) {
$payload['pihak_membantu_konflik_nama'] = null;
$payload['tahun_bantuan_konflik'] = null;
}
if (
array_key_exists('pernah_jual_harga_lebih_tinggi', $payload)
&& ($payload['pernah_jual_harga_lebih_tinggi'] ?? null) !== PernahJualHargaLebihTinggi::PERNAH->value
) {
$payload['penyebab_harga_lebih_tinggi'] = null;
}
if (
array_key_exists('panutan_petani_sukses', $payload)
&& (int) ($payload['panutan_petani_sukses'] ?? 0) === PanutanPetaniSukses::TIDAK_ADA->value
) {
$payload['nama_lokasi_panutan'] = null;
}
if (
array_key_exists('terdaftar_kelompok_tani', $payload)
&& ($payload['terdaftar_kelompok_tani'] ?? null) === TerdaftarKelompokTani::TIDAK->value
) {
$payload['nama_kelompok_tani'] = null;
$payload['lama_bergabung_kelompok_tahun'] = null;
$payload['kedekatan_sesama_anggota'] = null;
$payload['keaktifan_pertemuan_kelompok'] = KeaktifanPertemuanKelompok::TIDAK_PERNAH_HADIR->value;
$payload['topik_biasa_dibahas'] = null;
$payload['peran_dalam_kelompok'] = PeranDalamKelompok::ANGGOTA_PASIF->value;
$payload['manfaat_kelompok_tani'] = ManfaatKelompokTani::TIDAK_ADA_MANFAAT->value;
}
if (
array_key_exists('status_rekening_tabungan', $payload)
&& (int) ($payload['status_rekening_tabungan'] ?? 0) === StatusRekeningTabungan::TIDAK_PUNYA_REKENING->value
) {
$payload['nama_bank'] = null;
}
if (
array_key_exists('pengalaman_kredit_kebun', $payload)
&& !in_array((int) ($payload['pengalaman_kredit_kebun'] ?? 0), [
PengalamanKreditKebun::TIDAK_PERNAH_MENCOBA->value,
PengalamanKreditKebun::PERNAH_COBA_DITOLAK->value,
], true)
) {
$payload['alasan_kredit_ditolak'] = null;
$payload['alasan_kredit_ditolak_lainnya'] = null;
}
if (
array_key_exists('alasan_kredit_ditolak', $payload)
&& !in_array(AlasanKreditDitolak::LAINNYA->value, $payload['alasan_kredit_ditolak'] ?? [], true)
) {
$payload['alasan_kredit_ditolak_lainnya'] = null;
}
if (
array_key_exists('kondisi_aset_produktif', $payload)
&& (int) ($payload['kondisi_aset_produktif'] ?? 0) === KondisiAsetProduktif::TIDAK_ADA->value
) {
$payload['jenis_aset_produktif'] = null;
$payload['jenis_aset_produktif_lainnya'] = null;
$payload['keterangan_kondisi_aset'] = null;
}
if (
array_key_exists('jenis_aset_produktif', $payload)
&& !in_array(JenisAsetProduktif::LAINNYA->value, $payload['jenis_aset_produktif'] ?? [], true)
) {
$payload['jenis_aset_produktif_lainnya'] = null;
}
if (
array_key_exists('keterlibatan_program_keuangan_formal', $payload)
&& (int) ($payload['keterlibatan_program_keuangan_formal'] ?? 0) === KeterlibatanProgramKeuanganFormal::TIDAK_PERNAH_MENERIMA->value
) {
$payload['status_program_keuangan_formal'] = null;
$payload['program_keuangan_formal_lainnya_nama'] = null;
}
if (!filled($payload['status_program_keuangan_formal']['lainnya'] ?? null)) {
$payload['program_keuangan_formal_lainnya_nama'] = null;
}
if (
array_key_exists('pernah_bantuan_ngo_perusahaan', $payload)
&& ($payload['pernah_bantuan_ngo_perusahaan'] ?? null) === PernahBantuanNgoPerusahaan::TIDAK_PERNAH->value
) {
$payload['detail_bantuan_ngo_perusahaan'] = null;
}
if (array_key_exists('detail_bantuan_ngo_perusahaan', $payload) && is_array($payload['detail_bantuan_ngo_perusahaan'])) {
$payload['detail_bantuan_ngo_perusahaan'] = collect($payload['detail_bantuan_ngo_perusahaan'])
->filter(fn ($row) => collect($row)->filter(fn ($value) => filled($value))->isNotEmpty())
->values()
->all();
}
if (
array_key_exists('pinjaman_tokoh_adat_panen', $payload)
&& ($payload['pinjaman_tokoh_adat_panen'] ?? null) !== PinjamanTokohAdatPanen::ADA->value
) {
$payload['keterangan_pinjaman_tokoh_adat'] = null;
}
if (!empty($payload['total_hutang_tidak_bersedia'])) {
$payload['total_hutang_rupiah'] = null;
$payload['total_hutang_setara_kg_panen'] = null;
}
if (
array_key_exists('tekanan_jual_segera', $payload)
&& ($payload['tekanan_jual_segera'] ?? null) !== TekananJualSegera::ADA->value
) {
$payload['penyebab_tekanan_jual_segera'] = null;
}
if (
array_key_exists('kebebasan_memilih_pembeli', $payload)
&& (int) ($payload['kebebasan_memilih_pembeli'] ?? 0) >= KebebasanMemilihPembeli::CUKUP_BEBAS_SESEKALI_PINDAH->value
) {
$payload['alasan_tidak_bebas_memilih_pembeli'] = null;
}
return $payload;
}
private function validationMessages(): array
{
return [
'required' => ':attribute wajib diisi.',
'required_if' => ':attribute wajib diisi.',
'required_unless' => ':attribute wajib diisi.',
'required_with' => ':attribute wajib diisi.',
'integer' => ':attribute harus berupa angka.',
'numeric' => ':attribute harus berupa angka.',
'boolean' => ':attribute harus dipilih.',
'min' => ':attribute tidak boleh kurang dari :min.',
'max' => ':attribute tidak boleh lebih dari :max karakter.',
'digits' => ':attribute harus terdiri dari :digits digit.',
'exists' => ':attribute tidak valid.',
'in' => ':attribute tidak valid.',
'array' => ':attribute harus berupa daftar data.',
'string' => ':attribute harus berupa teks.',
'unique' => ':attribute sudah digunakan.',
];
}
private function validationAttributes(): array
{
return [
'form_section' => 'Bagian form',
'petani_id' => 'Petani',
'jumlah_tanggungan' => 'Jumlah tanggungan',
'anak_masih_sekolah' => 'Anak masih sekolah',
'yang_ikut_bekerja' => 'Yang ikut bekerja',
'skor_periode_paceklik' => 'Periode paceklik',
'bulan_paceklik' => 'Bulan paceklik biasanya',
'sumber_penghasilan_paceklik' => 'Sumber penghasilan saat paceklik',
'total_estimasi_biaya_kebun_per_musim' => 'Total estimasi biaya kebun per musim',
'skor_kemampuan_menabung' => 'Kemampuan menabung',
'estimasi_tabungan_per_bulan' => 'Estimasi tabungan per bulan',
'skor_kecukupan_penghasilan' => 'Kecukupan penghasilan untuk keluarga',
'alasan_kecukupan_penghasilan' => 'Alasan kecukupan penghasilan',
'status_kepemilikan_lahan' => 'Status kepemilikan lahan',
'skor_kejelasan_status_lahan' => 'Kejelasan status lahan',
'ada_sertifikat_dokumen' => 'Ada sertifikat/dokumen',
'ada_sengketa' => 'Ada sengketa',
'keterangan_sengketa' => 'Keterangan sengketa',
'pihak_membantu_konflik' => 'Pihak yang membantu',
'pihak_membantu_konflik_nama' => 'Nama pihak',
'tahun_bantuan_konflik' => 'Tahun bantuan',
'status_konflik' => 'Status konflik',
'kemauan_mencoba_praktik_baru' => 'Kemauan mencoba cara baru',
'alasan_mencoba_praktik_baru' => 'Alasan mencoba praktik baru',
'hambatan_mengubah_cara_bertani' => 'Hambatan utama mengubah cara berkebun',
'pemahaman_mutu_harga' => 'Pemahaman mutu vs. harga jual',
'contoh_pemahaman_mutu_harga' => 'Contoh/penjelasan petani',
'pengetahuan_harga_hasil_olah' => 'Pengetahuan harga hasil olah',
'pernah_jual_harga_lebih_tinggi' => 'Pengalaman jual dengan harga lebih tinggi',
'penyebab_harga_lebih_tinggi' => 'Penyebab harga lebih tinggi',
'usaha_mutu_tambahan_harga' => 'Usaha meningkatkan mutu vs. tambahan harga',
'alasan_usaha_mutu_tambahan_harga' => 'Alasan usaha mutu tambahan harga',
'panutan_petani_sukses' => 'Panutan di sekitar petani',
'nama_lokasi_panutan' => 'Nama dan lokasi panutan',
'kemauan_ikut_program' => 'Kemauan ikut program',
'harapan_program' => 'Harapan dari program',
'orientasi_rencana_jangka_panjang' => 'Orientasi rencana 3-5 tahun ke depan',
'rencana_jangka_panjang' => 'Rencana yang akan dilakukan',
'terdaftar_kelompok_tani' => 'Status terdaftar di kelompok tani',
'nama_kelompok_tani' => 'Nama kelompok',
'lama_bergabung_kelompok_tahun' => 'Lama bergabung',
'kedekatan_sesama_anggota' => 'Kedekatan dengan sesama anggota',
'keaktifan_pertemuan_kelompok' => 'Kehadiran dan keaktifan di pertemuan',
'topik_biasa_dibahas' => 'Topik yang biasa dibahas',
'peran_dalam_kelompok' => 'Jabatan atau peran di kelompok',
'manfaat_kelompok_tani' => 'Manfaat dari kelompok tani',
'status_kartu_tani' => 'Status Kartu Tani',
'bisa_beli_pupuk_bersubsidi' => 'Kemampuan menggunakan Kartu Tani untuk membeli pupuk bersubsidi',
'manfaat_lain_kartu_tani' => 'Manfaat lain setelah daftar Kartu Tani',
'status_rekening_tabungan' => 'Status rekening atau tabungan',
'nama_bank' => 'Nama bank',
'pengalaman_kredit_kebun' => 'Pengalaman kredit untuk kebun',
'alasan_kredit_ditolak' => 'Alasan kredit ditolak atau tidak jadi',
'alasan_kredit_ditolak.*' => 'Alasan kredit ditolak atau tidak jadi',
'alasan_kredit_ditolak_lainnya' => 'Alasan kredit lainnya',
'kondisi_aset_produktif' => 'Kondisi aset produktif selain lahan',
'jenis_aset_produktif' => 'Jenis aset',
'jenis_aset_produktif.*' => 'Jenis aset',
'jenis_aset_produktif_lainnya' => 'Jenis aset lainnya',
'keterangan_kondisi_aset' => 'Keterangan kondisi aset',
'keterlibatan_program_keuangan_formal' => 'Keterlibatan dalam program bantuan',
'status_program_keuangan_formal' => 'Status per program',
'status_program_keuangan_formal.*' => 'Status per program',
'program_keuangan_formal_lainnya_nama' => 'Nama program lainnya',
'pernah_bantuan_ngo_perusahaan' => 'Status bantuan NGO atau perusahaan',
'detail_bantuan_ngo_perusahaan' => 'Detail bantuan NGO atau perusahaan',
'detail_bantuan_ngo_perusahaan.*.nama_lembaga' => 'Nama lembaga',
'detail_bantuan_ngo_perusahaan.*.bentuk_bantuan' => 'Bentuk bantuan',
'detail_bantuan_ngo_perusahaan.*.frekuensi' => 'Frekuensi bantuan',
'detail_bantuan_ngo_perusahaan.*.tahun_terakhir' => 'Tahun terakhir bantuan',
'keterikatan_hutang_panen' => 'Hutang yang dikaitkan dengan hasil panen',
'pinjaman_tokoh_adat_panen' => 'Pinjaman dari tokoh adat atau mosalaki terkait panen',
'keterangan_pinjaman_tokoh_adat' => 'Keterangan pinjaman dari tokoh adat atau mosalaki',
'total_hutang_rupiah' => 'Total hutang saat ini',
'total_hutang_setara_kg_panen' => 'Total hutang setara hasil panen',
'total_hutang_tidak_bersedia' => 'Status tidak bersedia menjawab total hutang',
'praktik_ijon' => 'Praktik ijon',
'tekanan_jual_segera' => 'Tekanan jual segera meski harga rendah',
'penyebab_tekanan_jual_segera' => 'Penyebab tekanan jual segera',
'kebebasan_memilih_pembeli' => 'Kebebasan memilih pembeli',
'alasan_tidak_bebas_memilih_pembeli' => 'Alasan jika tidak bebas',
'minat_pembeli_harga_stabil' => 'Minat ke pembeli harga stabil jangka panjang',
'alasan_minat_pembeli_harga_stabil' => 'Alasan minat ke pembeli harga stabil',
'pendapatan_komoditas.*.kategori' => 'Kategori pendapatan komoditas',
'pendapatan_komoditas.*.komoditas' => 'Komoditas',
'pendapatan_komoditas.*.pendapatan' => 'Pendapatan',
'pendapatan_komoditas.*.periode' => 'Periode',
'pendapatan_luar_pertanian.*.jenis_pekerjaan' => 'Jenis pekerjaan',
'pendapatan_luar_pertanian.*.estimasi_per_bulan' => 'Estimasi per bulan',
];
}
private function normalizeSkorPayload(array $payload): array
{
foreach ([
@@ -279,6 +860,21 @@ class SosialEkonomiController extends Controller
'skor_kemampuan_menabung',
'skor_kecukupan_penghasilan',
'skor_kejelasan_status_lahan',
'kemauan_mencoba_praktik_baru',
'pemahaman_mutu_harga',
'panutan_petani_sukses',
'kemauan_ikut_program',
'orientasi_rencana_jangka_panjang',
'keaktifan_pertemuan_kelompok',
'peran_dalam_kelompok',
'manfaat_kelompok_tani',
'status_rekening_tabungan',
'pengalaman_kredit_kebun',
'kondisi_aset_produktif',
'keterlibatan_program_keuangan_formal',
'keterikatan_hutang_panen',
'praktik_ijon',
'kebebasan_memilih_pembeli',
] as $field) {
if (array_key_exists($field, $payload) && !is_null($payload[$field]) && $payload[$field] !== '') {
$payload[$field] = (int) $payload[$field];
@@ -371,6 +967,65 @@ class SosialEkonomiController extends Controller
'pihakMembantuKonflikOptions' => PihakMembantuKonflik::options(),
'pihakMembantuTanpaBantuanValue' => PihakMembantuKonflik::TIDAK_ADA_SELESAIKAN_SENDIRI->value,
'statusKonflikOptions' => StatusKonflik::options(),
'kemauanMencobaPraktikBaruOptions' => KemauanMencobaPraktikBaru::options(),
'pemahamanMutuHargaOptions' => PemahamanMutuHarga::options(),
'pengetahuanHargaHasilOlahOptions' => PengetahuanHargaHasilOlah::options(),
'pernahJualHargaLebihTinggiOptions' => PernahJualHargaLebihTinggi::options(),
'pernahJualHargaLebihTinggiPernahValue' => PernahJualHargaLebihTinggi::PERNAH->value,
'usahaMutuTambahanHargaOptions' => UsahaMutuTambahanHarga::options(),
'panutanPetaniSuksesOptions' => PanutanPetaniSukses::options(),
'panutanPetaniSuksesTidakAdaValue' => PanutanPetaniSukses::TIDAK_ADA->value,
'kemauanIkutProgramOptions' => KemauanIkutProgram::options(),
'orientasiRencanaJangkaPanjangOptions' => OrientasiRencanaJangkaPanjang::options(),
'terdaftarKelompokTaniOptions' => TerdaftarKelompokTani::options(),
'terdaftarKelompokTaniYaValue' => TerdaftarKelompokTani::YA->value,
'terdaftarKelompokTaniTidakValue' => TerdaftarKelompokTani::TIDAK->value,
'kedekatanSesamaAnggotaOptions' => KedekatanSesamaAnggota::options(),
'keaktifanPertemuanKelompokOptions' => KeaktifanPertemuanKelompok::options(),
'keaktifanPertemuanKelompokLowestValue' => KeaktifanPertemuanKelompok::TIDAK_PERNAH_HADIR->value,
'peranDalamKelompokOptions' => PeranDalamKelompok::options(),
'peranDalamKelompokLowestValue' => PeranDalamKelompok::ANGGOTA_PASIF->value,
'manfaatKelompokTaniOptions' => ManfaatKelompokTani::options(),
'manfaatKelompokTaniLowestValue' => ManfaatKelompokTani::TIDAK_ADA_MANFAAT->value,
'statusKartuTaniOptions' => StatusKartuTani::options(),
'bisaBeliPupukBersubsidiOptions' => BisaBeliPupukBersubsidi::options(),
'statusRekeningTabunganOptions' => StatusRekeningTabungan::options(),
'statusRekeningTidakPunyaValue' => StatusRekeningTabungan::TIDAK_PUNYA_REKENING->value,
'pengalamanKreditKebunOptions' => PengalamanKreditKebun::options(),
'pengalamanKreditPerluAlasanValues' => [
PengalamanKreditKebun::TIDAK_PERNAH_MENCOBA->value,
PengalamanKreditKebun::PERNAH_COBA_DITOLAK->value,
],
'alasanKreditDitolakOptions' => AlasanKreditDitolak::options(),
'alasanKreditLainnyaValue' => AlasanKreditDitolak::LAINNYA->value,
'kondisiAsetProduktifOptions' => KondisiAsetProduktif::options(),
'kondisiAsetTidakAdaValue' => KondisiAsetProduktif::TIDAK_ADA->value,
'jenisAsetProduktifOptions' => JenisAsetProduktif::options(),
'jenisAsetLainnyaValue' => JenisAsetProduktif::LAINNYA->value,
'keterlibatanProgramKeuanganFormalOptions' => KeterlibatanProgramKeuanganFormal::options(),
'keterlibatanProgramKeuanganFormalTidakPernahValue' => KeterlibatanProgramKeuanganFormal::TIDAK_PERNAH_MENERIMA->value,
'statusProgramKeuanganFormalOptions' => StatusProgramKeuanganFormal::options(),
'programKeuanganFormalRows' => [
'pkh' => 'PKH',
'blt' => 'BLT',
'kur' => 'KUR',
'kredit_koperasi_resmi' => 'Kredit koperasi resmi',
'credit_union_cu' => 'Credit Union (CU)',
'pinjaman_bumdes' => 'Pinjaman BUMDes',
'modal_hibah_dinas_pertanian' => 'Modal-hibah dinas pertanian',
'lainnya' => 'Lainnya',
],
'pernahBantuanNgoPerusahaanOptions' => PernahBantuanNgoPerusahaan::options(),
'pernahBantuanNgoPerusahaanPernahValue' => PernahBantuanNgoPerusahaan::PERNAH->value,
'keterikatanHutangPanenOptions' => KeterikatanHutangPanen::options(),
'pinjamanTokohAdatPanenOptions' => PinjamanTokohAdatPanen::options(),
'pinjamanTokohAdatAdaValue' => PinjamanTokohAdatPanen::ADA->value,
'praktikIjonOptions' => PraktikIjon::options(),
'tekananJualSegeraOptions' => TekananJualSegera::options(),
'tekananJualSegeraAdaValue' => TekananJualSegera::ADA->value,
'kebebasanMemilihPembeliOptions' => KebebasanMemilihPembeli::options(),
'kebebasanMemilihPembeliCukupBebasValue' => KebebasanMemilihPembeli::CUKUP_BEBAS_SESEKALI_PINDAH->value,
'minatPembeliHargaStabilOptions' => MinatPembeliHargaStabil::options(),
];
}
@@ -0,0 +1,346 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use App\Models\Pertanian\KelompokTani;
use App\Models\Wilayah\Provinsi;
use App\Models\Pertanian\Komoditas;
class SurveyKelompokTaniController extends Controller
{
private function authorizeAccess(): void
{
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
abort(403);
}
}
private function redirectToSection(string $url, string $section, string $message)
{
return redirect($url . '#section-' . $section)->with([
'success' => $message,
'success_section' => $section,
]);
}
public function index(Request $request)
{
$kelompokTani = KelompokTani::query()
->when($request->search, fn ($query, $search) => $query->where('nama_kelompok', 'like', "%{$search}%"))
->latest()
->paginate($request->per_page ?? 10)
->appends($request->query());
return view('produktivitas.survey-kelompok-tani.index', compact('kelompokTani'));
}
public function create()
{
$this->authorizeAccess();
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
$komoditas = Komoditas::where('is_utama', true)->get();
return view('produktivitas.survey-kelompok-tani.add', compact('provinsi', 'komoditas'));
}
public function store(Request $request)
{
$this->authorizeAccess();
$validated = $this->validateIdentitas($request);
if (($validated['jenis_kelompok'] ?? null) === 'Lainnya') {
$validated['jenis_kelompok'] = $validated['jenis_kelompok_lainnya'];
}
unset($validated['jenis_kelompok_lainnya']);
$kelompokTani = KelompokTani::create([
...$validated,
'uid' => 'KLP-' . strtoupper(Str::random(10)),
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.edit', $kelompokTani->id), 'identitas', 'Data kelompok tani berhasil ditambahkan.');
}
private function editUrl(KelompokTani $kelompokTani, string $section = 'identitas'): string
{
$url = route('kelompok-tani.edit', $kelompokTani->id);
return $section === 'identitas' ? $url : $url . '?section=' . $section;
}
public function edit(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
return match ($request->query('section', 'identitas')) {
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani')),
'aktivitas-fungsi' => view('produktivitas.survey-kelompok-tani.aktivitas-fungsi', compact('kelompokTani')),
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani')),
default => view('produktivitas.survey-kelompok-tani.edit', [
'kelompokTani' => $kelompokTani,
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
'komoditas' => Komoditas::where('is_utama', true)->get(),
]),
};
}
public function update(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
return match ($request->input('section', 'identitas')) {
'ringkasan-pengurus' => $this->updateRingkasanPengurus($request, $kelompokTani),
'kelembagaan' => $this->updateKelembagaan($request, $kelompokTani),
'aktivitas-fungsi' => $this->updateAktivitasFungsi($request, $kelompokTani),
'kas' => $this->updateKasSimpanPinjam($request, $kelompokTani),
'sosial-konflik' => $this->updateSosialKonflik($request, $kelompokTani),
'status-bantuan' => $this->updateStatusBantuan($request, $kelompokTani),
'penilaian' => $this->updatePenilaian($request, $kelompokTani),
default => $this->updateIdentitas($request, $kelompokTani),
};
}
public function destroy(string $id)
{
$this->authorizeAccess();
$kelompokTani = KelompokTani::findOrFail($id);
$kelompokTani->update(['deleted_by' => auth()->id()]);
$kelompokTani->delete();
return response()->json(['message' => 'Data kelompok tani berhasil dihapus.']);
}
private function validateIdentitas(Request $request)
{
return $request->validate([
// Data Responden
'nama_responden' => ['required', 'string', 'max:255'],
'jabatan_responden' => ['nullable', 'string', 'max:255'],
'kontak_responden' => ['nullable', 'string', 'max:20'],
// Identitas Kelompok Tani
'nama_kelompok' => ['required', 'string', 'max:255'],
'desa_kelurahan_id' => ['required', 'exists:master_desa_kelurahan,id'],
'dusun' => ['nullable', 'string', 'max:255'],
'tahun_berdiri' => ['nullable', 'digits:4', 'integer', 'min:1900', 'max:' . date('Y')],
'jenis_kelompok' => ['required', 'string', 'max:50'],
'jenis_kelompok_lainnya' => ['required_if:jenis_kelompok,Lainnya', 'nullable', 'string', 'max:255'],
'nama_gapoktan' => ['nullable', 'string', 'max:255'],
'komoditas_id' => ['nullable', 'array'],
'komoditas_id.*' => ['exists:master_komoditas,id'],
], [
'nama_responden.required' => 'Nama responden wajib diisi.',
'nama_kelompok.required' => 'Nama kelompok wajib diisi.',
'desa_kelurahan_id.required' => 'Desa/Kelurahan wajib dipilih.',
'desa_kelurahan_id.exists' => 'Desa/Kelurahan tidak ditemukan.',
'jenis_kelompok.required' => 'Jenis kelompok wajib dipilih.',
'jenis_kelompok_lainnya.required_if' => 'Sebutkan jenis kelompok lainnya.',
'tahun_berdiri.digits' => 'Tahun berdiri harus 4 digit.',
]);
}
private function updateIdentitas(Request $request, KelompokTani $kelompokTani)
{
$validated = $this->validateIdentitas($request);
if (($validated['jenis_kelompok'] ?? null) === 'Lainnya') {
$validated['jenis_kelompok'] = $validated['jenis_kelompok_lainnya'];
}
unset($validated['jenis_kelompok_lainnya']);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.edit', $kelompokTani->id), 'identitas', 'Data kelompok tani berhasil diperbarui.');
}
private function updateRingkasanPengurus(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
'jumlah_pengurus' => ['nullable', 'integer', 'min:0'],
'jumlah_pengurus_aktif' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_terdaftar' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_aktif' => ['nullable', 'integer', 'min:0'],
'jumlah_pengurus_laki_laki' => ['nullable', 'integer', 'min:0'],
'jumlah_pengurus_perempuan' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_laki_laki' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_perempuan' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_pemuda_laki_laki' => ['nullable', 'integer', 'min:0'],
'jumlah_anggota_pemuda_perempuan' => ['nullable', 'integer', 'min:0'],
]);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'ringkasan', 'Ringkasan pengurus & anggota berhasil disimpan.');
}
private function updateKelembagaan(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
// Cakupan Wilayah Kelompok
'cakupan_wilayah' => ['nullable', 'string', 'max:50'],
'nama_wilayah_cakupan' => ['nullable', 'string'],
// Status Legalitas Kelompok
'status_legalitas' => ['nullable', 'string', 'max:50'],
'nomor_sk' => ['nullable', 'string', 'max:100'],
'tanggal_tahun_penerbitan_sk' => ['nullable', 'string', 'max:30'],
// Kualitas Kepemimpinan
'tingkat_keaktifan_ketua' => ['nullable', 'string', 'max:30'],
'status_pembagian_peran' => ['nullable', 'string', 'max:30'],
'status_dokumentasi_rapat' => ['nullable', 'string', 'max:30'],
// Frekuensi Pertemuan Rutin
'frekuensi_pertemuan' => ['nullable', 'string', 'max:50'],
'tanggal_pertemuan_terakhir' => ['nullable', 'date'],
]);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection($this->editUrl($kelompokTani, 'kelembagaan'), 'kelembagaan', 'Data kelembagaan berhasil disimpan.');
}
private function resolveLainnya(?array $selected, ?string $lainnyaText): ?array
{
if (empty($selected)) {
return $selected;
}
return array_values(array_map(function ($item) use ($lainnyaText) {
return $item === 'Lainnya' && filled($lainnyaText) ? $lainnyaText : $item;
}, $selected));
}
private function updateAktivitasFungsi(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
// Kegiatan Konkret yang Sudah Berjalan
'kegiatan_terlaksana' => ['nullable', 'array'],
'kegiatan_terlaksana.*' => ['string', 'max:255'],
'kegiatan_terlaksana_lainnya' => ['nullable', 'string', 'max:255'],
'estimasi_panen_minimal' => ['nullable', 'numeric', 'min:0'],
'estimasi_panen_maksimal' => ['nullable', 'numeric', 'min:0', 'gte:estimasi_panen_minimal'],
'satuan_hasil_panen' => ['nullable', 'string', 'max:20'],
'tindakan_hasil_panen' => ['nullable', 'array'],
'tindakan_hasil_panen.*' => ['string', 'max:255'],
// Peran dan Fungsi Kelompok
'fungsi_kelompok_bagi_anggota' => ['nullable', 'array'],
'fungsi_kelompok_bagi_anggota.*' => ['string', 'max:255'],
'fungsi_kelompok_lainnya' => ['nullable', 'string', 'max:255'],
], [
'estimasi_panen_maksimal.gte' => 'Estimasi panen maksimal tidak boleh kurang dari estimasi minimal.',
]);
$validated['kegiatan_terlaksana'] = $this->resolveLainnya(
$validated['kegiatan_terlaksana'] ?? null,
$validated['kegiatan_terlaksana_lainnya'] ?? null
);
unset($validated['kegiatan_terlaksana_lainnya']);
$validated['fungsi_kelompok_bagi_anggota'] = $this->resolveLainnya(
$validated['fungsi_kelompok_bagi_anggota'] ?? null,
$validated['fungsi_kelompok_lainnya'] ?? null
);
unset($validated['fungsi_kelompok_lainnya']);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection($this->editUrl($kelompokTani, 'aktivitas-fungsi'), 'aktivitas-fungsi', 'Data aktivitas & fungsi kelompok berhasil disimpan.');
}
private function updateKasSimpanPinjam(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
'pembukuan_kas' => ['nullable', 'string', 'max:50'],
'pengelola_kas' => ['nullable', 'string', 'max:255'],
'masalah_pengelolaan_kas_simpan_pinjam' => ['nullable', 'string', 'max:255'],
'deskripsi_masalah_pengelolaan_kas_simpan_pinjam' => ['nullable', 'string'],
]);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'kas', 'Data kas/simpan-pinjam berhasil disimpan.');
}
private function updateSosialKonflik(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
// Keterlibatan Perempuan
'partisipasi_perempuan' => ['nullable', 'string', 'max:255'],
// Riwayat Konflik Internal Kelompok
'konflik_kelompok' => ['nullable', 'string', 'max:255'],
'topik_konflik' => ['nullable', 'string', 'max:255'],
'penyelesaian_konflik' => ['nullable', 'string'],
]);
if (!in_array($validated['konflik_kelompok'] ?? null, ['Pernah, sudah selesai', 'Pernah, masih berlanjut/belum selesai'])) {
$validated['topik_konflik'] = null;
$validated['penyelesaian_konflik'] = null;
}
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection($this->editUrl($kelompokTani, 'sosial-konflik'), 'sosial-konflik', 'Data sosial & konflik berhasil disimpan.');
}
private function updateStatusBantuan(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
'pernah_mendapatkan_bantuan' => ['required', 'boolean'],
]);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'status-bantuan', 'Status akses bantuan berhasil disimpan.');
}
private function updatePenilaian(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate([
'potensi_kelompok' => ['nullable', 'string', 'max:50'],
'alasan_penilaian_potensi' => ['nullable', 'string'],
]);
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'penilaian', 'Penilaian potensi kelompok berhasil disimpan.');
}
}
@@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\SurveyProduktivitas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class SurveyProduktivitasController extends Controller
{
public function store($petaniId, Request $request)
{
$petani = Petani::findOrFail($petaniId);
$validator = Validator::make($request->all(), [
'produktivitas_komoditas' => ['required', 'array', 'min:1'],
'produktivitas_komoditas.*.komoditas_id' => ['required', 'exists:master_komoditas,id'],
'produktivitas_komoditas.*.hasil_panen_3_musim' => ['required', 'numeric', 'min:0'],
'produktivitas_komoditas.*.satuan' => ['required', 'in:ha_tahun,pohon_tahun'],
], [
'produktivitas_komoditas.required' => 'Minimal satu data produktivitas harus diisi.',
'produktivitas_komoditas.*.komoditas_id.required' => 'Komoditas wajib dipilih.',
'produktivitas_komoditas.*.komoditas_id.exists' => 'Komoditas yang dipilih tidak valid.',
'produktivitas_komoditas.*.hasil_panen_3_musim.required' => 'Hasil panen wajib diisi.',
'produktivitas_komoditas.*.hasil_panen_3_musim.numeric' => 'Hasil panen harus berupa angka.',
'produktivitas_komoditas.*.hasil_panen_3_musim.min' => 'Hasil panen tidak boleh kurang dari 0.',
'produktivitas_komoditas.*.satuan.required' => 'Satuan wajib dipilih.',
'produktivitas_komoditas.*.satuan.in' => 'Satuan yang dipilih tidak valid.',
]);
if ($validator->fails()) {
return $this->redirectBackToFragment($request)->withErrors($validator)->withInput();
}
$validated = $validator->validated();
foreach ($validated['produktivitas_komoditas'] as $row) {
SurveyProduktivitas::create([
'uid' => 'SPV-'.strtoupper(Str::random(10)),
'petani_id' => $petani->id,
'komoditas_id' => $row['komoditas_id'],
'kuantitas' => $row['hasil_panen_3_musim'],
'satuan' => $row['satuan'],
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
return $this->redirectBackToFragment($request)->with('success', 'Data produktivitas berhasil disimpan.');
}
public function destroy($petaniId, $surveyProduktivitasId, Request $request)
{
$surveyProduktivitas = SurveyProduktivitas::where('petani_id', $petaniId)
->where('id', $surveyProduktivitasId)
->firstOrFail();
$surveyProduktivitas->deleted_by = auth()->id();
$surveyProduktivitas->save();
$surveyProduktivitas->delete();
return $this->redirectBackToFragment($request)->with('success', 'Data produktivitas berhasil dihapus.');
}
private function redirectBackToFragment(Request $request)
{
$fragment = $request->input('redirect_fragment');
if (! is_string($fragment) || ! preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
return back();
}
return redirect()->to(Str::before(url()->previous(), '#').'#'.$fragment);
}
}
@@ -0,0 +1,71 @@
<?php
namespace App\Models\Bumdes;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class KelembagaanAktorBumdes extends Model
{
use SoftDeletes;
protected $table = 'bumdes_kelembagaan_aktor';
protected $fillable = [
'pengurus_bumdes_id',
'status_badan_hukum',
'nomor_dasar_hukum',
'tahun_dasar_hukum',
'status_keaktifan',
'alasan_tidak_aktif',
'unit_usaha',
'unit_usaha_jasa_lainnya',
'status_unit_komoditas',
'nama_unit_komoditas',
'status_ad_art',
'tahun_ad_art',
'pengurus_inti',
'memiliki_staf',
'jumlah_staf',
'unit_usaha_staf',
'modal_awal',
'tahun_modal_awal',
'pernah_penambahan_modal',
'penambahan_modal',
'aset_produktif',
'aset_produktif_lainnya',
'status_kepemilikan_aset',
'infrastruktur_pascapanen',
'infrastruktur_pascapanen_lainnya',
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'tahun_dasar_hukum' => 'integer',
'tahun_ad_art' => 'integer',
'unit_usaha' => 'array',
'pengurus_inti' => 'array',
'memiliki_staf' => 'boolean',
'jumlah_staf' => 'integer',
'modal_awal' => 'decimal:2',
'tahun_modal_awal' => 'integer',
'pernah_penambahan_modal' => 'boolean',
'penambahan_modal' => 'array',
'aset_produktif' => 'array',
'infrastruktur_pascapanen' => 'array',
];
public function pengurusBumdes(): BelongsTo
{
return $this->belongsTo(PengurusBumdes::class, 'pengurus_bumdes_id');
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace App\Models\Bumdes;
use App\Enums\Bumdes\JabatanBumdes;
use App\Enums\Pendidikan;
use App\Models\User;
use App\Models\Wilayah\DesaKelurahan;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
class PengurusBumdes extends Model
{
use SoftDeletes;
protected $table = 'bumdes_pengurus';
protected $fillable = [
'uid',
'nama',
'jenis_kelamin',
'tempat_lahir',
'tanggal_lahir',
'pendidikan_terakhir',
'alamat',
'desa_kelurahan_id',
'nomor_telepon',
'jabatan',
'jabatan_lainnya',
'lama_menjabat_tahun',
'tanggal_mulai_menjabat',
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'jenis_kelamin' => 'boolean',
'tanggal_lahir' => 'date',
'pendidikan_terakhir' => Pendidikan::class,
'tanggal_mulai_menjabat' => 'date',
'jabatan' => JabatanBumdes::class,
];
public function getUsiaAttribute(): ?int
{
if (! $this->tanggal_lahir) {
return null;
}
return Carbon::parse($this->tanggal_lahir)->age;
}
public function getLamaMenjabatAttribute(): string
{
if (! is_null($this->lama_menjabat_tahun)) {
return $this->lama_menjabat_tahun . ' tahun';
}
if (! $this->tanggal_mulai_menjabat) {
return '-';
}
$tanggalMulai = Carbon::parse($this->tanggal_mulai_menjabat)->startOfDay();
if ($tanggalMulai->isFuture()) {
return 'Belum mulai';
}
$selisih = $tanggalMulai->diff(Carbon::now());
$bagian = [];
if ($selisih->y > 0) {
$bagian[] = $selisih->y . ' tahun';
}
if ($selisih->m > 0) {
$bagian[] = $selisih->m . ' bulan';
}
if (empty($bagian)) {
return $selisih->d . ' hari';
}
return implode(' ', $bagian);
}
public function getNamaJabatanAttribute(): string
{
if ($this->jabatan === JabatanBumdes::LAINNYA && $this->jabatan_lainnya) {
return $this->jabatan_lainnya;
}
return $this->jabatan?->label() ?? '-';
}
public function desaKelurahan(): BelongsTo
{
return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id');
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function kelembagaanAktor(): HasOne
{
return $this->hasOne(KelembagaanAktorBumdes::class, 'pengurus_bumdes_id');
}
}
-1
View File
@@ -56,7 +56,6 @@ class FormBasisData extends Model
public function form(): HasMany
{
return $this->hasMany(FormBasisData::class, 'parent_id')
->with('form')
->orderBy('urutan');
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace App\Models\Koperasi;
use Illuminate\Database\Eloquent\Model;
class PengurusKoperasi extends Model
{
protected $table = 'pengurus_koperasi';
protected $fillable = [
// Profil Dassar
'nama_lengkap', 'posisi_dalam_koperasi', 'tahun_bergabung',
'no_hp', 'alamat_tinggal',
// Profil Koperasi
'nama_koperasi', 'jenis_koperasi', 'alamat_koperasi',
'skala_operasi', 'tahun_berdiri',
// Anggota & Pengurus
'jumlah_anggota_awal', 'jumlah_anggota_dewasa', 'jumlah_anggota_anak', 'jumlah_pengurus',
// SHU
'shu_2025', 'shu_2024', 'shu_2023',
// Kelembagaan & Manajemen
'sejarah_lembaga', 'fokus_komoditas', 'detail_fokus_komoditas',
'ad_art', 'tahun_ad_art', 'layanan_koperasi', 'layanan_lainnya',
'pelatihan_produksi', 'pelatihan_pascapanen', 'pelatihan_pengolahan',
'pelatihan_keuangan', 'pelatihan_pemasaran', 'pelatihan_lainnya',
'status_pembukuan', 'format_pembukuan', 'pertemuan_rutin', 'pertemuan_lainnya',
'kasus_kredit_macet', 'sanksi_kredit_macet', 'efektivitas_sanksi',
'faktor_manajemen', 'faktor_manajemen_lainnya', 'keterlibatan_rantai_nilai',
'agregasi_komoditas', 'agregasi_komoditas_lainnya', 'fasilitas_koperasi', 'fasilitas_lainnya',
'pengolahan_lanjutan', 'detail_pengolahan_lanjutan', 'cara_penentuan_harga',
'kontrak_offtaker', 'kontrak_komoditas',
// Akses Pasar & Rantai Nilai Eksternal
'sumber_pasokan', 'sumber_pasokan_lainnya', 'bentuk_pembelian',
'volume_beli_sekarang', 'volume_beli_1_tahun', 'volume_beli_2_tahun',
'tujuan_penjualan', 'tujuan_penjualan_lainnya', 'bentuk_penjualan',
'volume_jual_sekarang', 'volume_jual_1_tahun', 'volume_jual_2_tahun',
'sifat_hubungan_bisnis', 'sifat_hubungan_lainnya', 'mekanisme_pembayaran', 'status_perjanjian_tertulis',
// Harga Komoditas
'harga_segar_sekarang', 'harga_segar_1_tahun', 'harga_segar_2_tahun',
'harga_basah_sekarang', 'harga_basah_1_tahun', 'harga_basah_2_tahun',
'harga_kering_sekarang', 'harga_kering_1_tahun', 'harga_kering_2_tahun',
'harga_fermentasi_sekarang', 'harga_fermentasi_1_tahun', 'harga_fermentasi_2_tahun',
'cara_menetapkan_harga_pasar', 'cara_menetapkan_harga_lainnya',
// Praktik Kualitas & Pascapanen
'praktik_sortasi', 'standar_sortasi', 'praktik_pengeringan', 'cara_pengeringan',
'praktik_fermentasi', 'model_fermentasi', 'proses_fermentasi',
'harga_premium_rupiah', 'harga_premium_persen',
// Sosial Ekonomi
'simpanan_terbesar', 'momen_simpanan_terbesar',
'alasan_pinjaman', 'alasan_pinjaman_lainnya', 'pembayaran_selain_uang',
'bentuk_pembayaran_barang', 'bentuk_pembayaran_lainnya', 'perhitungan_harga_pasar', 'alasan_bukan_harga_pasar',
// PENILAIAN ENUMERATOR
'enum_potensi', 'enum_alasan_potensi', 'enum_kebutuhan', 'enum_kebutuhan_lain', 'enum_keterbukaan'
];
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models\Master;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class JenisTanaman extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'master_jenis_tanaman';
protected $fillable = [
'nama',
'created_by',
'updated_by',
'deleted_by',
];
}
+43
View File
@@ -4,8 +4,14 @@ namespace App\Models\Pertanian;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use App\Models\User;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\SkorPetani;
use App\Enums\AksesPasar\PraktikFermentasi;
use App\Enums\AksesPasar\SaluranTerdahulu;
use App\Enums\AksesPasar\SkorPengalaman;
use App\Enums\AksesPasar\SkorKesadaranPremium;
class AksesPasar extends Model
{
@@ -16,15 +22,25 @@ class AksesPasar extends Model
protected $fillable = [
'uid',
'petani_id',
'tujuan_penjualan',
'tujuan_penjualan_lainnya',
'sifat_hubungan_bisnis',
'bentuk_penjualan',
'frekuensi_penjualan',
'skor_pengalaman',
'saluran_terdahulu',
'saluran_terdahulu_lainnya',
'alasan_berhenti',
'tahu_harga_sebelumnya',
'sumber_info_harga',
'sumber_info_harga_lainnya',
'praktik_fermentasi',
'proses_fermentasi',
'model_fermentasi_memungkinkan',
'skor_kesadaran_premium',
'sumber_info_premium',
'tingkat_kepercayaan_pembeli_baru',
'alasan_kepercayaan_pembeli_baru',
'waktu_mulai_wawancara',
'waktu_selesai_wawancara',
'skor_kepercayaan_responden',
@@ -36,8 +52,35 @@ class AksesPasar extends Model
];
protected $casts = [
'tujuan_penjualan' => 'array',
'saluran_terdahulu' => 'array',
'sumber_info_harga' => 'array',
];
protected static function booted(): void
{
static::saved(function (AksesPasar $aksesPasar) {
$petani = Petani::find($aksesPasar->petani_id);
$skor = SkorPetani::where('petani_id', $petani->id)->orderBy('id')->first();
if (!$skor) {
$petaniUid = $petani->uid;
$part = explode('-', $petaniUid);
$skor = SkorPetani::create([
'petani_id' => $petani->id,
'uid' => 'SKR-' . $part[1] . '-' . strtoupper(Str::random(10)),
'created_by' => $petani->created_by,
'updated_by' => $petani->updated_by
]);
}
$skor->update([
'skor_d6_k1_6_pengalaman_menjual_ke_saluran_berbeda' => $aksesPasar->skor_pengalaman,
'skor_d6_k4_1_kesadaran_akses_pasar_premium' => $aksesPasar->skor_kesadaran_premium,
'skor_d6_k5_1_hambatan_mutu' => $aksesPasar->skor_kepercayaan_responden
]);
});
}
public function petani()

Some files were not shown because too many files have changed in this diff Show More